import Foundation import Security /// Keychain-backed secure storage for auth tokens enum SecureStorage { private static let service = "com.payfrit.beacon" struct Session { let token: String let userId: String } static func saveSession(token: String, userId: String) { save(key: "authToken", value: token) save(key: "userId", value: userId) } static func loadSession() -> Session? { guard let token = load(key: "authToken"), let userId = load(key: "userId") else { return nil } return Session(token: token, userId: userId) } static func clearSession() { delete(key: "authToken") delete(key: "userId") } // MARK: - Keychain Helpers private static func save(key: String, value: String) { guard let data = value.data(using: .utf8) else { return } delete(key: key) // Remove old value first let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: key, kSecValueData as String: data, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ] SecItemAdd(query as CFDictionary, nil) } private static func load(key: String) -> String? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, 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 else { return nil } return String(data: data, encoding: .utf8) } private static func delete(key: String) { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, kSecAttrAccount as String: key ] SecItemDelete(query as CFDictionary) } }