-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathKeychainStore.swift
More file actions
47 lines (42 loc) · 1.63 KB
/
KeychainStore.swift
File metadata and controls
47 lines (42 loc) · 1.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import Foundation
import Security
/// Stores the OpenAI API key in the macOS Keychain (never in plaintext on disk).
enum KeychainStore {
private static let service = "VectorTrace.OpenAI"
private static let account = "apiKey"
private static var baseQuery: [String: Any] {
[
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
]
}
/// Save the key, replacing any existing one. An empty string deletes it.
static func saveAPIKey(_ key: String) {
SecItemDelete(baseQuery as CFDictionary)
let trimmed = key.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return }
var add = baseQuery
add[kSecValueData as String] = Data(trimmed.utf8)
add[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
SecItemAdd(add as CFDictionary, nil)
}
/// Load the stored key, or nil if none. Falls back to the OPENAI_API_KEY env var.
static func loadAPIKey() -> String? {
var query = baseQuery
query[kSecReturnData as String] = true
query[kSecMatchLimit as String] = kSecMatchLimitOne
var item: CFTypeRef?
if SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess,
let data = item as? Data,
let str = String(data: data, encoding: .utf8),
!str.isEmpty {
return str
}
if let env = ProcessInfo.processInfo.environment["OPENAI_API_KEY"],
!env.isEmpty {
return env
}
return nil
}
}