diff --git a/PediFoods.xcodeproj/project.pbxproj b/PediFoods.xcodeproj/project.pbxproj index 077e564..9f006ab 100644 --- a/PediFoods.xcodeproj/project.pbxproj +++ b/PediFoods.xcodeproj/project.pbxproj @@ -981,6 +981,7 @@ 388EFAF707AD295240B9E951 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + APP_ATTEST_ENVIRONMENT = production; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1025,6 +1026,7 @@ 4424276379B7A6A98892CFBC /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + APP_ATTEST_ENVIRONMENT = development; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; diff --git a/PediFoods/PediFoods.entitlements b/PediFoods/PediFoods.entitlements index e267110..e6f7ec8 100644 --- a/PediFoods/PediFoods.entitlements +++ b/PediFoods/PediFoods.entitlements @@ -4,7 +4,11 @@ aps-environment development + com.apple.developer.devicecheck.app-attest-opt-in + + CDhash + com.apple.developer.devicecheck.appattest-environment - development + $(APP_ATTEST_ENVIRONMENT) diff --git a/PediFoods/Services/GuestSessionService.swift b/PediFoods/Services/GuestSessionService.swift index 869c3ed..411bace 100644 --- a/PediFoods/Services/GuestSessionService.swift +++ b/PediFoods/Services/GuestSessionService.swift @@ -93,6 +93,7 @@ actor GuestSessionService { #if os(iOS) private func refreshTokenWithAppAttest() async throws -> String { + print("[GuestSessionService] DCAppAttestService.isSupported = \(DCAppAttestService.shared.isSupported), existingKeyId = \(store.appAttestKeyId ?? "nil")") guard DCAppAttestService.shared.isSupported else { // Simulator can never support App Attest (hardware limitation, // not environment-specific) — server has its own documented @@ -112,6 +113,15 @@ actor GuestSessionService { // other error (network blip, timeout, decode issue) must // NOT wipe a perfectly valid registered key. store.appAttestKeyId = nil + } catch let error as DCError { + // DeviceCheck itself rejects the key locally (e.g. the app + // was reinstalled and the Secure Enclave key backing this + // keyId no longer exists) - distinct from the server + // rejecting it, but equally unrecoverable without a fresh + // key. Without this, generateAssertion fails the same way + // forever since appAttestKeyId is never cleared. + print("[GuestSessionService] existing key rejected locally, re-attesting with a fresh key: \(error)") + store.appAttestKeyId = nil } } @@ -141,9 +151,22 @@ actor GuestSessionService { } private func handshakeWithFreshAttestation(challenge: String) async throws -> String { - let keyId = try await DCAppAttestService.shared.generateKey() + let keyId: String + do { + keyId = try await DCAppAttestService.shared.generateKey() + } catch { + print("[GuestSessionService] generateKey failed: \(error)") + throw error + } + let clientDataHash = Data(SHA256.hash(data: Data(challenge.utf8))) - let attestationObject = try await DCAppAttestService.shared.attestKey(keyId, clientDataHash: clientDataHash) + let attestationObject: Data + do { + attestationObject = try await DCAppAttestService.shared.attestKey(keyId, clientDataHash: clientDataHash) + } catch { + print("[GuestSessionService] attestKey failed: \(error)") + throw error + } let payload = GuestSessionAttestPayload( platform: "ios", @@ -175,7 +198,13 @@ actor GuestSessionService { private func handshakeWithAssertion(keyId: String, challenge: String) async throws -> String { let clientDataHash = Data(SHA256.hash(data: Data(challenge.utf8))) - let assertionObject = try await DCAppAttestService.shared.generateAssertion(keyId, clientDataHash: clientDataHash) + let assertionObject: Data + do { + assertionObject = try await DCAppAttestService.shared.generateAssertion(keyId, clientDataHash: clientDataHash) + } catch { + print("[GuestSessionService] generateAssertion failed: \(error)") + throw error + } let payload = GuestSessionAttestPayload( platform: "ios", diff --git a/PediFoods/Views/Main/PublicLocationPickerView.swift b/PediFoods/Views/Main/PublicLocationPickerView.swift index 085b911..f5263e1 100644 --- a/PediFoods/Views/Main/PublicLocationPickerView.swift +++ b/PediFoods/Views/Main/PublicLocationPickerView.swift @@ -161,6 +161,7 @@ struct PublicLocationPickerView: View { errorMessage = "Nenhum estado disponível no momento." } } catch { + print("[PublicLocationPickerView] loadLocations failed: \(error)") errorMessage = "Não foi possível carregar. Tente novamente." } isLoading = false