[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:
Daniel Arantes Loverde
2026-07-30 11:35:25 -03:00
parent 3e93196b92
commit 017bd7168f
21 changed files with 1140 additions and 136 deletions

View File

@@ -1,7 +1,4 @@
import Foundation
#if os(iOS)
import Security
#endif
protocol TokenStore: AnyObject {
var jwt: String? { get set }
@@ -10,12 +7,13 @@ protocol TokenStore: AnyObject {
final class DefaultTokenStore: TokenStore {
private let key = "auth_jwt"
private let serviceName = "com.br.pedifoods.app.auth"
private let defaults = UserDefaults.standard
var jwt: String? {
get {
#if os(iOS)
if let keychainValue = loadKeychainValue(for: key) {
if let keychainValue = KeychainStore.load(service: serviceName, key: key) {
return keychainValue
}
#endif
@@ -24,9 +22,9 @@ final class DefaultTokenStore: TokenStore {
set {
#if os(iOS)
if let newValue {
saveKeychainValue(newValue, for: key)
KeychainStore.save(newValue, service: serviceName, key: key)
} else {
deleteKeychainValue(for: key)
KeychainStore.delete(service: serviceName, key: key)
}
#endif
defaults.set(newValue, forKey: key)
@@ -35,57 +33,8 @@ final class DefaultTokenStore: TokenStore {
func clear() {
#if os(iOS)
deleteKeychainValue(for: key)
KeychainStore.delete(service: serviceName, key: key)
#endif
defaults.removeObject(forKey: key)
}
#if os(iOS)
private var serviceName: String { "com.br.pedifoods.app.auth" }
private func saveKeychainValue(_ value: String, for key: String) {
guard let data = value.data(using: .utf8) else { return }
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: key
]
SecItemDelete(query as CFDictionary)
let attributes: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: key,
kSecValueData as String: data
]
SecItemAdd(attributes as CFDictionary, nil)
}
private func loadKeychainValue(for key: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess,
let data = result as? Data,
let value = String(data: data, encoding: .utf8) else {
return nil
}
return value
}
private func deleteKeychainValue(for key: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: key
]
SecItemDelete(query as CFDictionary)
}
#endif
}