42 lines
1.4 KiB
Swift
42 lines
1.4 KiB
Swift
import Foundation
|
|
|
|
#if os(macOS)
|
|
import Darwin
|
|
import LocalAuthentication
|
|
import Security
|
|
|
|
enum KeychainNoUIQuery {
|
|
private static let uiFailPolicy = KeychainNoUIQuery.resolveUIFailPolicy()
|
|
|
|
static func apply(to query: inout [String: Any]) {
|
|
let context = LAContext()
|
|
context.interactionNotAllowed = true
|
|
query[kSecUseAuthenticationContext as String] = context
|
|
|
|
// Keep explicit UI-fail policy for legacy keychain behavior on macOS where
|
|
// `interactionNotAllowed` alone can still surface Allow/Deny prompts.
|
|
query[kSecUseAuthenticationUI as String] = self.uiFailPolicy as CFString
|
|
}
|
|
|
|
static func uiFailPolicyForTesting() -> String {
|
|
self.uiFailPolicy
|
|
}
|
|
|
|
private static func resolveUIFailPolicy() -> String {
|
|
// Resolve the Security symbol at runtime to preserve the true constant value
|
|
// without directly referencing deprecated API at compile time.
|
|
let securityPath = "/System/Library/Frameworks/Security.framework/Security"
|
|
guard let handle = dlopen(securityPath, RTLD_NOW) else {
|
|
return "u_AuthUIF"
|
|
}
|
|
defer { dlclose(handle) }
|
|
|
|
guard let symbol = dlsym(handle, "kSecUseAuthenticationUIFail") else {
|
|
return "u_AuthUIF"
|
|
}
|
|
let valuePointer = symbol.assumingMemoryBound(to: CFString?.self)
|
|
return (valuePointer.pointee as String?) ?? "u_AuthUIF"
|
|
}
|
|
}
|
|
#endif
|