| 1 | import Foundation |
| 2 | import Security |
| 3 | |
| 4 | enum NativeCredentials { |
| 5 | private static var service: String { (Bundle.main.bundleIdentifier ?? "oficina.native") + ".session" } |
| 6 | private static func query(_ origin: String) -> [String: Any] { |
| 7 | [kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: service, |
| 8 | kSecAttrAccount as String: origin] |
| 9 | } |
| 10 | static func read(_ origin: String) -> NativeSession? { |
| 11 | var q = query(origin) |
| 12 | q[kSecReturnData as String] = true |
| 13 | q[kSecMatchLimit as String] = kSecMatchLimitOne |
| 14 | var result: CFTypeRef? |
| 15 | guard SecItemCopyMatching(q as CFDictionary, &result) == errSecSuccess, |
| 16 | let data = result as? Data else { return nil } |
| 17 | return try? JSONDecoder().decode(NativeSession.self, from: data) |
| 18 | } |
| 19 | static func save(_ session: NativeSession, origin: String) throws { |
| 20 | let data = try JSONEncoder().encode(session) |
| 21 | let updated = SecItemUpdate(query(origin) as CFDictionary, [kSecValueData as String: data] as CFDictionary) |
| 22 | if updated == errSecSuccess { return } |
| 23 | guard updated == errSecItemNotFound else { throw NativeClientError.message("Could not update this session in Keychain.") } |
| 24 | var q = query(origin) |
| 25 | q[kSecValueData as String] = data |
| 26 | q[kSecAttrAccessible as String] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly |
| 27 | guard SecItemAdd(q as CFDictionary, nil) == errSecSuccess else { |
| 28 | throw NativeClientError.message("Could not save this session in Keychain.") |
| 29 | } |
| 30 | } |
| 31 | static func remove(_ origin: String) { SecItemDelete(query(origin) as CFDictionary) } |
| 32 | } |
| 33 | |