Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

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 ↗

Transcript all transcripts

Code shown on screen · 7 snippets

LAContext (authorize a signature operation 1) swift · at 4:58 ↗
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) swift · at 5:15 ↗
let context = LAContext()
try await context.evaluateAccessControl(accessControl as! SecAccessControl, 
                      operation: .useKeySign, 
                       localizedReason: "Authentication is required to proceed")
LAContext (authorize a signature operation 3) swift · at 5:44 ↗
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) swift · at 6:00 ↗
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) swift · at 8:28 ↗
// 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) swift · at 11:01 ↗
// 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 swift · at 13:44 ↗
// 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)
}

Resources