How do Face ID and Touch ID with LocalAuthentication work, and what does the Secure Enclave add?
Predict first
Do the LocalAuthentication error codes match, and does an availability check compile as described?
import LocalAuthentication
let ctx = LAContext()
var err: NSError?
let can = ctx.canEvaluatePolicy(.deviceOwnerAuthentication, error: &err)
print(can || err != nil)
print(LAError.Code.userCancel.rawValue, LAError.Code.biometryNotAvailable.rawValue)
- true, then -2 -6
- false, then 2 6
- true, then -1 -2
Mental model. A boolean “Face ID passed” is a bouncer saying “he’s fine” and waving you in: a forger can imitate the wave. Keychain access control with biometrics is a vault that only opens when the bouncer’s key turns the lock.
LocalAuthentication (LAContext) asks the system to verify the user: canEvaluatePolicy(_:error:) checks availability first, and evaluatePolicy(_:localizedReason:) (async) shows the prompt. The policy choice matters: .deviceOwnerAuthenticationWithBiometrics accepts only Face ID or Touch ID and fails if none is enrolled or it is locked out, while .deviceOwnerAuthentication falls back to the device passcode. Handle LAError codes (user cancelled, −2; biometry not available, −6; not enrolled; lockout; user chose fallback) as normal outcomes with sensible UI, and set NSFaceIDUsageDescription in Info.plist, or the system refuses to use Face ID for your app. The crucial limitation: evaluatePolicy returns only a success or failure, which is a boolean in your process. An attacker with a hooked app (or a jailbroken device) can flip it to true. So an if success { showSecrets() } gate protects only against casual access and is not security for sensitive data.
Real protection ties the secret to the authentication in hardware: store it in the Keychain with access control (SecAccessControlCreateWithFlags with .biometryCurrentSet or .userPresence), so reading the item itself makes the system demand Face ID or the passcode and the data is never released without it (.biometryCurrentSet additionally invalidates the item if the enrolled fingerprints or face change). The Secure Enclave is a separate coprocessor with its own encrypted memory that holds private keys that never leave it: you create a P-256 key with SecureEnclave.P256.Signing.PrivateKey() (optionally bound to biometrics), and the system performs signing inside the hardware, so even a fully compromised app can request a signature but never copy the key. It supports P-256 (not arbitrary algorithms or symmetric bulk encryption: use it to sign challenges or to wrap a symmetric key). Typical designs: a device-bound signing key registered with your server so a login proves possession of this device, and a biometric-protected key that unlocks stored credentials. Remember the limits: the simulator has no Secure Enclave, biometrics can change, and users can always fall back to the passcode, so offer a normal recovery path.
The availability check returns a status or an error to inspect, and the common error codes are −2 (user cancelled) and −6 (biometry not available).
Common traps
- Gating sensitive data on the
BoolfromevaluatePolicy. It can be bypassed in a compromised process; protect the secret with Keychain access control. - Assuming a Secure Enclave key can be exported or used for any algorithm. It holds P-256 keys that never leave the chip.
They might follow up with
- What does
.biometryCurrentSetdo when a new face or finger is enrolled? The protected item becomes invalid. - What is the difference between the two LocalAuthentication policies? One is biometrics only; the other falls back to the passcode.
Related questions
- 14.1.1 What is the difference between hashing, a MAC, encryption and a digital signature, and when do you use…
- 14.2.2 What do App Attest and DeviceCheck prove, when do they help, and what are their limits?
- 13.1.2 How does the Keychain work, how do you add, read and update an item, and what do accessibility classes and…
What do App Attest and DeviceCheck prove, when do they help, and what… →