[2026-07-resubmission] Add guest browsing flow with App Attest session for App Review resubmission
Adds a pre-login public store locator (guest session via DeviceCheck/App Attest, keychain-backed token storage) so the app no longer forces sign-in before showing any content, plus updated support URL metadata.
This commit is contained in:
@@ -57,19 +57,30 @@ struct ApiRequest: Sendable {
|
||||
let requiresAuth: Bool
|
||||
let queryItems: [URLQueryItem]
|
||||
let body: Data?
|
||||
/// Overrides `ApiConfig.baseURL` (Atomenta) for this single request — used
|
||||
/// to reach the PediFoods BFF (`ApiConfig.pediFoodsBFFURL`) instead.
|
||||
let baseURLOverride: URL?
|
||||
/// Sent as `Authorization: Bearer <token>` instead of the customer JWT.
|
||||
/// Used for guest-session-authenticated public locator calls, which must
|
||||
/// never touch `TokenStore`'s customer session.
|
||||
let customBearerToken: String?
|
||||
|
||||
init(path: String,
|
||||
method: String = "GET",
|
||||
module: ApiModule = .none,
|
||||
requiresAuth: Bool = true,
|
||||
queryItems: [URLQueryItem] = [],
|
||||
body: Data? = nil) {
|
||||
body: Data? = nil,
|
||||
baseURLOverride: URL? = nil,
|
||||
customBearerToken: String? = nil) {
|
||||
self.path = path
|
||||
self.method = method
|
||||
self.module = module
|
||||
self.requiresAuth = requiresAuth
|
||||
self.queryItems = queryItems
|
||||
self.body = body
|
||||
self.baseURLOverride = baseURLOverride
|
||||
self.customBearerToken = customBearerToken
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +131,7 @@ final class ApiClient: @unchecked Sendable {
|
||||
private extension ApiClient {
|
||||
#if canImport(LCEssentials) && os(iOS)
|
||||
func sendWithLCEssentials<T: Decodable>(_ request: ApiRequest) async throws -> T {
|
||||
let urlString = try buildURL(path: request.path, query: request.queryItems).absoluteString
|
||||
let urlString = try buildURL(path: request.path, query: request.queryItems, baseURL: request.baseURLOverride ?? ApiConfig.baseURL).absoluteString
|
||||
let method = request.method
|
||||
let headers = buildHeaders(for: request)
|
||||
let params = request.body
|
||||
@@ -228,7 +239,7 @@ private extension ApiClient {
|
||||
#endif
|
||||
|
||||
func sendWithURLSession<T: Decodable>(_ request: ApiRequest) async throws -> T {
|
||||
let url = try buildURL(path: request.path, query: request.queryItems)
|
||||
let url = try buildURL(path: request.path, query: request.queryItems, baseURL: request.baseURLOverride ?? ApiConfig.baseURL)
|
||||
var urlRequest = URLRequest(url: url)
|
||||
urlRequest.httpMethod = request.method
|
||||
urlRequest.httpBody = request.body
|
||||
@@ -314,7 +325,9 @@ private extension ApiClient {
|
||||
if let token = ApiConfig.token(for: request.module) {
|
||||
headers["Atomenta-Token"] = token
|
||||
}
|
||||
if request.requiresAuth, let jwt = tokenStore.jwt {
|
||||
if let customBearerToken = request.customBearerToken {
|
||||
headers["Authorization"] = "Bearer \(customBearerToken)"
|
||||
} else if request.requiresAuth, let jwt = tokenStore.jwt {
|
||||
headers["Authorization"] = "Bearer \(jwt)"
|
||||
}
|
||||
return headers
|
||||
@@ -474,8 +487,8 @@ private extension ApiClient {
|
||||
return trimmed
|
||||
}
|
||||
|
||||
func buildURL(path: String, query: [URLQueryItem]) throws -> URL {
|
||||
guard var components = URLComponents(url: ApiConfig.baseURL, resolvingAgainstBaseURL: false) else {
|
||||
func buildURL(path: String, query: [URLQueryItem], baseURL: URL) throws -> URL {
|
||||
guard var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false) else {
|
||||
throw NetworkError.invalidURL
|
||||
}
|
||||
components.path = components.path.appending(path)
|
||||
|
||||
Reference in New Issue
Block a user