// Keychain-backed token storage for the live aid web connection.
// Exports: KeychainTokenStore.
import Foundation
import Security
enum KeychainTokenStore {
private static let service = "com.mingsun.aidcommand.web-token"
private static let account = "aid-web"
static func load() -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne,
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess, let data = item as? Data else { return nil }
return String(data: data, encoding: .utf8)
}
static func save(_ token: String) throws {
let data = Data(token.utf8)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
]
let attributes: [String: Any] = [kSecValueData as String: data]
let update = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
if update == errSecSuccess { return }
if update != errSecItemNotFound {
throw KeychainError.osStatus(update)
}
var add = query
add[kSecValueData as String] = data
add[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
let status = SecItemAdd(add as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError.osStatus(status) }
}
static func clear() {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
]
SecItemDelete(query as CFDictionary)
}
}
enum KeychainError: Error {
case osStatus(OSStatus)
}