initial commit: Proximity iOS app proposal, full Swift code scaffold, reference backend, testing docs, generated Xcode project
This commit is contained in:
47
Proximity/Services/Auth/KeychainStore.swift
Normal file
47
Proximity/Services/Auth/KeychainStore.swift
Normal file
@@ -0,0 +1,47 @@
|
||||
import Foundation
|
||||
import Security
|
||||
|
||||
/// Secure persistence for the session token using the iOS Keychain.
|
||||
/// Never stores the session in UserDefaults or plain files.
|
||||
struct KeychainStore {
|
||||
|
||||
private let service = "com.proximity.app.session"
|
||||
|
||||
func save(_ session: Session) {
|
||||
let data = (try? JSONEncoder().encode(session)) ?? Data()
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: service,
|
||||
kSecAttrAccount as String: "session"
|
||||
]
|
||||
// Delete existing, then add.
|
||||
SecItemDelete(query as CFDictionary)
|
||||
var add = query
|
||||
add[kSecValueData as String] = data
|
||||
SecItemAdd(add as CFDictionary, nil)
|
||||
}
|
||||
|
||||
func loadSession() -> Session? {
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: service,
|
||||
kSecAttrAccount as String: "session",
|
||||
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 try? JSONDecoder().decode(Session.self, from: data)
|
||||
}
|
||||
|
||||
func clearSession() {
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: service,
|
||||
kSecAttrAccount as String: "session"
|
||||
]
|
||||
SecItemDelete(query as CFDictionary)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user