2022 Privacy & Security
WWDC22 · 16 min · Privacy & Security
Streamline local authorization flows
Discover how you can use the latest authorization-focused APIs in LocalAuthentication to protect the privacy and security of people’s data. We’ll show you how LocalAuthentication can authorize access to secrets, keys, and other sensitive resources in your app, all while reducing complexity and relying on the security and usability of common local authentication methods such as Touch ID and Face ID.
Watch at developer.apple.com ↗Code shown on screen · 7 snippets
LAContext (authorize a signature operation 1)
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecAttrApplicationTag as String: "com.example.app.key",
kSecReturnAttributes as String: true,
]
var item: CFTypeRef? = nil
guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess, let attrs = item as? NSDictionary, let accessControl = attrs[kSecAttrAccessControl] else {
throw .aclNotFound
} LAContext (authorize a signature operation 2)
let context = LAContext()
try await context.evaluateAccessControl(accessControl as! SecAccessControl,
operation: .useKeySign,
localizedReason: "Authentication is required to proceed") LAContext (authorize a signature operation 3)
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecAttrApplicationTag as String: "com.example.app.key",
kSecReturnRef as String: true,
kSecUseAuthenticationContext as String: context
]
var item: CFTypeRef? = nil
guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess, item != nil else {
throw .keyNotFound
} LAContext (authorize a signature operation 4)
let privateKey = item as! SecKey
var error: Unmanaged<CFError>?
guard let sgt = SecKeyCreateSignature(privateKey, self.algorithm, blob, &error) as Data? else {
throw .signatureFailure
} LA Right (basic usage)
// LARight: Basic usage
func login() async {
self.loginRight = LARight(requirement: .biometry(fallback: .devicePasscode))
do {
try await loginRight.checkCanAuthorize()
} catch {
navigateTo(section: .public)
return
}
do {
try await self.loginRight.authorize(localizedReason: self.localizedReason)
navigateTo(section: .protected)
} catch {
showError(.authenticationRequired)
}
} LARight (logout and deauthorization)
// LARight: Basic usage
func login() async {
self.loginRight = LARight(requirement: .biometry(fallback: .devicePasscode))
// ...
do {
try await self.loginRight.authorize(localizedReason: self.localizedReason)
navigateTo(section: .protected)
} catch {
showError(.authenticationRequired)
}
}
func logout() async {
await self.loginRight.deauthorize()
} LAPersistedRight
// LAPersistedRight: Retrieval and private key usage
func generateClientKeys() async throws -> Data {
let login2FA = LARight(requirement: .biometryCurrentSet)
let persisted2FA = try await LARightStore.shared.saveRight(login2FA, identifier: "2fa")
return try await persisted2FA.key.publicKey.bytes
}
func signChallenge(_ challenge: Data, algorithm: SecKeyAlgorithm) async throws -> Data {
let persisted2FA = try await LARightStore.shared.right(forIdentifier: "2fa")
let localizedReason = "Biometric authentication is required to proceed"
try await persisted2FA.authorize(localizedReason: localizedReason)
return try await persisted2FA.key.sign(challenge, algorithm: algorithm)
}