Securely unlock devices, authenticate purchases, sign in to apps, and more with facial recognition using Face ID.

Posts under Face ID tag

8 Posts

Post

Replies

Boosts

Views

Activity

LAContext and its usage in context of Local Authentication
While working with Local Authentication framework, specifically LAContext class I found myself with few contradictions to documentation, and although I believe that those differences are rather positive than negative, either documentation is lacking behind or those APIs are not working as intended - which I believe is combination of both. 1. Local Authentication 1.1 Biometry type, and associated with it hash With introduction of LADomainState one can extract underlying biometry type along it's (current) state hash this way: @available(iOS 18, macOS 15, *) func postIOS18() { let context = LAContext() let biometryType = context.domainState.biometry.biometryType // (1) let biometryStateHash = context.domainState.biometry.stateHash // (2) } prior to receiving above APIs, we would retrieve such information something along those lines: func preIOS18() { let context = LAContext() let policy: LAPolicy // ... var error: NSError? _ = context.canEvaluatePolicy(policy, error: error) // (3) // ... (Handle error - if present) let biometryType = context.biometryType // (4) let biometryStateHash = context.evaluatedPolicyDomainState // (5) } However, moving let biometryType = context.biometryType (4) before call to canEvaluatePolicy (3) still yields correct biometry type. This is in contradiction to article from Local Authentication documentation page Optionally, Adjust Your User Interface to Accommodate Face ID. Furthermore, biometryType documentation does not mentions such requirement. Another difference is that call to canEvaluatePolicy (3) might return an error, eg. LAError(.biometryLockout) (if implemented correctly) preventing as from returning biometryStateHash (5) with nil value. This is not the case for new API, where the same call (2) will yield nil as a result - LADomainStateBiometry documentation does not mention it. In summary, here are some questions: Which API should be used to retrieve biometry type?, and why the "older way" has not been deprecated? Is is safe to assume that calls to biometryType and stateHash from LADomainStateBiometry will produce meaningful results without prior call to canEvaluatePolicy? Should I assume that call to biometryType found on LAContext instance will always return biometryType without prior call to canEvaluatePolicy?, or perhaps those are only side effects of changes made to accommodate LADomainState, and prior to them (pre-iOS 18) we must call canEvaluatePolicy to get meaningful value. Are the stateHash properties found on LADomainState, LADomainStateBiometry and LADomainStateCompanion will return nil upon encountering any error under the hood? (which would be equivalent of below code, prior to iOS 18) func biometryStateHash() -> Data? { let context = LAContext() if #available(iOS 18, macOS 15, *) { _ = context.canEvaluatePolicy(policy, error: nil) return context.evaluatedPolicyDomainState } else { return context.domainState.biometry.stateHash } } 1.2 Deprecation of evaluatedDomainState There is a forum post LAContext.evaluatedPolicyDomainState change between major OS versions mentioning missing documentation (fixed), however there's still information missing of how they correlate to each other. From my findings, the deprecated evaluatedDomainState property value matches those of LADomainState stateHash (when no companion device is present), and LADomainStateBiometry stateHash (all the time). Are those assumptions correct? 1.3 LAContext (authenticated) session lifespan Theres is little information about it state when authenticated by the user. Documentation on LAContext does not mention this behavior, while there are hints that once authenticated, and context is reused, any farther calls will not prompt user with UI. The problem is that this behavior is little, to no documented. Here are few examples I have found: Logging a User into Your App with Face ID or Touch ID (code sample) contains comment // Get a fresh context for each login. If you use the same context on multiple attempts //  (by commenting out the next line), then a previously successful authentication //  causes the next policy evaluation to succeed without testing biometry again. //  That's usually not what you want. Recent forum post, where such approach is mentioned by Quinn 'The Eskimo!' "At the API level, one option you have is to create an LAContext and pass it in to each SecItemCopyMatching call via kSecUseAuthenticationContext." WWDC22 Streamline local authorization flows session "By binding the LAContext to our private key reference, we ensure that executing the signature operation will not trigger another authentication, while allowing the operation to continue without unnecessary prompts. These binding also means that no additional user interactions will be required for future signatures until the LAContext is invalidated." Furthermore this is complicated by the touchIDAuthenticationAllowableReuseDuration property from LAContext instance which states that "The default value is 0, meaning that no previous biometric unlock can be reused." which is in direct contradiction to what I have experienced while working with LAContext and sources mentioned above. While digging on this, whether this behavior is intended or not, I came across a post (I would love to share it, but the domain is not permitted) that shared the same findings (and concerns) regarding LAContext behavior as me. The author also provided a FB9984036 feedback number - although no further update was made on that topic. So my questions are: Is it safe to reuse LAContext (authenticated) instance? How long such instance is considered authenticated?, is it a time duration or perhaps it stays in authenticated state until explicitly invalidated using invalidate method. (its invalidated for sure when app is terminated, but this was to be expected :)) How does touchIDAuthenticationAllowableReuseDuration work, and how does it correlate to "reusability" of the authenticated LAContext instance? In what scenarios touchIDAuthenticationAllowableReuseDuration should be used and what is its expected behavior? (I have tried it both on iOS and macOS; from my perspective API this does not "work")
0
0
49
6h
LAContext and its usage in context of Local Authentication
While working with Local Authentication framework, specifically LAContext class I found myself with few contradictions to documentation, and although I believe that those differences are rather positive than negative, either documentation is lacking behind or those APIs are not working as intended - which I believe is combination of both. 1. Local Authentication 1.1 Biometry type, and associated with it hash With introduction of LADomainState one can extract underlying biometry type along it's (current) state hash this way: @available(iOS 18, macOS 15, *) func postIOS18() { let context = LAContext() let biometryType = context.domainState.biometry.biometryType // (1) let biometryStateHash = context.domainState.biometry.stateHash // (2) } prior to receiving above APIs, we would retrieve such information something along those lines: func preIOS18() { let context = LAContext() let policy: LAPolicy // ... var error: NSError? _ = context.canEvaluatePolicy(policy, error: error) // (3) // ... (Handle error - if present) let biometryType = context.biometryType // (4) let biometryStateHash = context.evaluatedPolicyDomainState // (5) } However, moving let biometryType = context.biometryType (4) before call to canEvaluatePolicy (3) still yields correct biometry type. This is in contradiction to article from Local Authentication documentation page Optionally, Adjust Your User Interface to Accommodate Face ID. Furthermore, biometryType documentation does not mentions such requirement. Another difference is that call to canEvaluatePolicy (3) might return an error, eg. LAError(.biometryLockout) (if implemented correctly) preventing as from returning biometryStateHash (5) with nil value. This is not the case for new API, where the same call (2) will yield nil as a result - LADomainStateBiometry documentation does not mention it. In summary, here are some questions: Which API should be used to retrieve biometry type?, and why the "older way" has not been deprecated? Is is safe to assume that calls to biometryType and stateHash from LADomainStateBiometry will produce meaningful results without prior call to canEvaluatePolicy? Should I assume that call to biometryType found on LAContext instance will always return biometryType without prior call to canEvaluatePolicy?, or perhaps those are only side effects of changes made to accommodate LADomainState, and prior to them (pre-iOS 18) we must call canEvaluatePolicy to get meaningful value. Are the stateHash properties found on LADomainState, LADomainStateBiometry and LADomainStateCompanion will return nil upon encountering any error under the hood? (which would be equivalent of below code, prior to iOS 18) func biometryStateHash() -> Data? { let context = LAContext() if #available(iOS 18, macOS 15, *) { _ = context.canEvaluatePolicy(policy, error: nil) return context.evaluatedPolicyDomainState } else { return context.domainState.biometry.stateHash } } 1.2 Deprecation of evaluatedDomainState There is a forum post LAContext.evaluatedPolicyDomainState change between major OS versions mentioning missing documentation (fixed), however there's still information missing of how they correlate to each other. From my findings, the deprecated evaluatedDomainState property value matches those of LADomainState stateHash (when no companion device is present), and LADomainStateBiometry stateHash (all the time). Are those assumptions correct? 1.3 LAContext (authenticated) session lifespan Theres is little information about it state when authenticated by the user. Documentation on LAContext does not mention this behavior, while there are hints that once authenticated, and context is reused, any farther calls will not prompt user with UI. The problem is that this behavior is little, to no documented. Here are few examples I have found: Logging a User into Your App with Face ID or Touch ID (code sample) contains comment // Get a fresh context for each login. If you use the same context on multiple attempts //  (by commenting out the next line), then a previously successful authentication //  causes the next policy evaluation to succeed without testing biometry again. //  That's usually not what you want. Recent forum post, where such approach is mentioned by Quinn 'The Eskimo!' "At the API level, one option you have is to create an LAContext and pass it in to each SecItemCopyMatching call via kSecUseAuthenticationContext." WWDC22 Streamline local authorization flows session "By binding the LAContext to our private key reference, we ensure that executing the signature operation will not trigger another authentication, while allowing the operation to continue without unnecessary prompts. These binding also means that no additional user interactions will be required for future signatures until the LAContext is invalidated." Furthermore this is complicated by the touchIDAuthenticationAllowableReuseDuration property from LAContext instance which states that "The default value is 0, meaning that no previous biometric unlock can be reused." which is in direct contradiction to what I have experienced while working with LAContext and sources mentioned above. While digging on this, whether this behavior is intended or not, I came across a post (I would love to share it, but the domain is not permitted) that shared the same findings (and concerns) regarding LAContext behavior as me. The author also provided a FB9984036 feedback number - although no further update was made on that topic. So my questions are: Is it safe to reuse LAContext (authenticated) instance? How long such instance is considered authenticated?, is it a time duration or perhaps it stays in authenticated state until explicitly invalidated using invalidate method. (its invalidated for sure when app is terminated, but this was to be expected :)) How does,touchIDAuthenticationAllowableReuseDuration work, and how does it correlate to "reusability" of the authenticated LAContext instance? In what scenarios, touchIDAuthenticationAllowableReuseDuration should be used and what is its expected behavior? (I have tried it both on iOS and macOS; from my perspective API this does not "work")
0
0
45
6h
Face ID hardware failure
What could be the possible reasons for my device getting a Face id hardware failure after updating the phone to 26.4.1? I am suspect there could be some failure connections between TrueDepth sensors and front camera. Can anyone try to help me here
0
0
91
3w
Exploring Secure Enclave–backed biometric authorization between macOS and iPhone using public APIs (FaceBridge prototype)
Hi everyone, I’ve been working on an experimental prototype called FaceBridge that explores whether Secure Enclave–backed biometric authorization can be delegated between macOS and iPhone using only public Apple APIs. The goal of the project was to better understand the architectural boundaries of cross-device trust and approval flows that resemble Apple’s built-in Touch ID / Continuity authorization experiences. FaceBridge implements a local authorization pipeline where: macOS generates a signed authorization request the request is delivered to a trusted nearby iPhone over BLE / Network framework the iPhone verifies sender identity Face ID approval is requested using LocalAuthentication the iPhone signs the approval response using Secure Enclave–backed keys macOS validates the response and unlocks a protected action Security properties currently implemented: • Secure Enclave–backed signing identities per device • cryptographic device pairing and trust persistence • replay protection using nonce + timestamp binding • structured authorization request/response envelopes • signed responder identity verification • trusted-device registry model • local encrypted transport over BLE and local network This is intentionally not attempting to intercept or replace system-level Touch ID dialogs (App Store installs, Keychain prompts, loginwindow, etc.), but instead explores what is possible within application-level authorization boundaries using public APIs only. The project is open source: https://github.com/wesleysfavarin/facebridge Technical architecture write-up: https://medium.com/@wesleysfavarin/facebridge I’m particularly interested in feedback around: • recommended Secure Enclave identity lifecycle patterns • best practices for cross-device trust persistence • LocalAuthentication usage in delegated approval scenarios • whether similar authorization models are expected to become more formally supported across Apple platforms in the future Thanks in advance for any guidance or suggestions.
1
0
258
Mar ’26
Biometrics prompt + private key access race condition on since iOS 26.1
We are using SecItemCopyMatching from LocalAuthentication to access the private key to sign a challenge in our native iOS app twice in a few seconds from user interactions. This was working as expected up until about a week ago where we started getting reports of it hanging on the biometrics screen (see screenshot below). From our investigation we've found the following: It impacts newer iPhones using iOS 26.1 and later. We have replicated on these devices: iPhone 17 Pro max iPhone 16 Pro iPhone 15 Pro max iPhone 15 Only reproducible if the app tries to access the private key twice in quick succession after granting access to face ID. Looks like a race condition between the biometrics permission prompt and Keychain private key access We were able to make it work by waiting 10 seconds between private key actions, but this is terrible UX. We tried adding adding retries over the span of 10 seconds which fixed it on some devices, but not all. We checked the release notes for iOS 26.1, but there is nothing related to this. Screenshot:
5
0
850
Mar ’26
evaluatedPolicyDomainState
Hi Apple Developers, I'm having a problem with evaluatedPolicyDomainState: on the same device, its value keeps changing and then switching back to the original. My current iOS version is 26.1. I upgraded my iOS from version 18.6.2 to 26.1. What could be the potential reasons for this issue? { NSError *error; BOOL success = YES; char *eds = nil; int edslen = 0; LAContext *context = [[LAContext alloc] init]; // test if we can evaluate the policy, this test will tell us if Touch ID is available and enrolled // success = [context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]; if (SystemVersion > 9.3) { // test if we can evaluate the policy, this test will tell us if Touch ID is available and enrolled success = [context canEvaluatePolicy: LAPolicyDeviceOwnerAuthentication error:&error]; } else{ // test if we can evaluate the policy, this test will tell us if Touch ID is available and enrolled success = [context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]; } if (success) { if (@available(iOS 18.0, *)) { NSData *stateHash = nil; if ([context respondsToSelector:@selector(domainState)]) { stateHash = [[context performSelector:@selector(domainState)] performSelector:@selector(stateHash)]; }else{ stateHash = [context evaluatedPolicyDomainState]; } eds = (char *)stateHash.bytes; edslen = (int)stateHash.length; } else { eds = (char *)[[context evaluatedPolicyDomainState] bytes]; edslen = (int)[[context evaluatedPolicyDomainState] length]; } CC_SHA256(eds, edslen, uviOut); *poutlen = CC_SHA256_DIGEST_LENGTH; } else { *poutlen = 32; gm_memset(uviOut, 0x01, 32); } }
6
0
1.3k
Jan ’26
Does accessing multiple Keychain items with .userPresence force multiple biometric prompts despite reuse duration?
Hi everyone, I'm working on an app that stores multiple secrets in the Keychain, each protected with .userPresence. My goal is to authenticate the user once via FaceID/TouchID and then read multiple Keychain items without triggering subsequent prompts. I am reusing the same LAContext instance for these operations, and I have set: context.touchIDAuthenticationAllowableReuseDuration = LATouchIDAuthenticationMaximumAllowableReuseDuration However, I'm observing that every single SecItemCopyMatching call triggers a new FaceID/TouchID prompt, even if they happen within seconds of each other using the exact same context. Here is a simplified flow of what I'm doing: Create a LAContext. Set touchIDAuthenticationAllowableReuseDuration to max. Perform a query (SecItemCopyMatching) for Item A, passing [kSecUseAuthenticationContext: context]. Result: System prompts for FaceID. Success. Immediately perform a query (SecItemCopyMatching) for Item B, passing the same [kSecUseAuthenticationContext: context]. Result: System prompts for FaceID again. My question is: Does the .userPresence access control flag inherently force a new user interaction for every Keychain access, regardless of the LAContext reuse duration? Is allowableReuseDuration only applicable for LAContext.evaluatePolicy calls and not for SecItem queries? If so, is there a recommended pattern for "unlocking" a group of Keychain items with a single biometric prompt? Environment: iOS 17+, Swift. Thanks!
3
0
585
Jan ’26
Biometric Authentication Behavior in IAP Sandbox Environment
Where the problem occurs: In-app purchase Non-ApplePay Non-local authentication login Environment where the problem occurs: Sandbox environment (Development environment, TestFlight environment) Problem handling process: Open page A in the app and purchase product B (auto-renewable subscription) on that page. User authentication is required to purchase product B. During the authentication process, the user needs to enter the Apple account and Apple account password. After completing the authentication, complete the purchase of product B. Problem in step 3: Why is FaceID or TouchID not used for authentication? Note: Face ID and Password -> iTunes Store and App Store -> Status is Enabled
0
0
183
Jun ’25
LAContext and its usage in context of Local Authentication
While working with Local Authentication framework, specifically LAContext class I found myself with few contradictions to documentation, and although I believe that those differences are rather positive than negative, either documentation is lacking behind or those APIs are not working as intended - which I believe is combination of both. 1. Local Authentication 1.1 Biometry type, and associated with it hash With introduction of LADomainState one can extract underlying biometry type along it's (current) state hash this way: @available(iOS 18, macOS 15, *) func postIOS18() { let context = LAContext() let biometryType = context.domainState.biometry.biometryType // (1) let biometryStateHash = context.domainState.biometry.stateHash // (2) } prior to receiving above APIs, we would retrieve such information something along those lines: func preIOS18() { let context = LAContext() let policy: LAPolicy // ... var error: NSError? _ = context.canEvaluatePolicy(policy, error: error) // (3) // ... (Handle error - if present) let biometryType = context.biometryType // (4) let biometryStateHash = context.evaluatedPolicyDomainState // (5) } However, moving let biometryType = context.biometryType (4) before call to canEvaluatePolicy (3) still yields correct biometry type. This is in contradiction to article from Local Authentication documentation page Optionally, Adjust Your User Interface to Accommodate Face ID. Furthermore, biometryType documentation does not mentions such requirement. Another difference is that call to canEvaluatePolicy (3) might return an error, eg. LAError(.biometryLockout) (if implemented correctly) preventing as from returning biometryStateHash (5) with nil value. This is not the case for new API, where the same call (2) will yield nil as a result - LADomainStateBiometry documentation does not mention it. In summary, here are some questions: Which API should be used to retrieve biometry type?, and why the "older way" has not been deprecated? Is is safe to assume that calls to biometryType and stateHash from LADomainStateBiometry will produce meaningful results without prior call to canEvaluatePolicy? Should I assume that call to biometryType found on LAContext instance will always return biometryType without prior call to canEvaluatePolicy?, or perhaps those are only side effects of changes made to accommodate LADomainState, and prior to them (pre-iOS 18) we must call canEvaluatePolicy to get meaningful value. Are the stateHash properties found on LADomainState, LADomainStateBiometry and LADomainStateCompanion will return nil upon encountering any error under the hood? (which would be equivalent of below code, prior to iOS 18) func biometryStateHash() -> Data? { let context = LAContext() if #available(iOS 18, macOS 15, *) { _ = context.canEvaluatePolicy(policy, error: nil) return context.evaluatedPolicyDomainState } else { return context.domainState.biometry.stateHash } } 1.2 Deprecation of evaluatedDomainState There is a forum post LAContext.evaluatedPolicyDomainState change between major OS versions mentioning missing documentation (fixed), however there's still information missing of how they correlate to each other. From my findings, the deprecated evaluatedDomainState property value matches those of LADomainState stateHash (when no companion device is present), and LADomainStateBiometry stateHash (all the time). Are those assumptions correct? 1.3 LAContext (authenticated) session lifespan Theres is little information about it state when authenticated by the user. Documentation on LAContext does not mention this behavior, while there are hints that once authenticated, and context is reused, any farther calls will not prompt user with UI. The problem is that this behavior is little, to no documented. Here are few examples I have found: Logging a User into Your App with Face ID or Touch ID (code sample) contains comment // Get a fresh context for each login. If you use the same context on multiple attempts //  (by commenting out the next line), then a previously successful authentication //  causes the next policy evaluation to succeed without testing biometry again. //  That's usually not what you want. Recent forum post, where such approach is mentioned by Quinn 'The Eskimo!' "At the API level, one option you have is to create an LAContext and pass it in to each SecItemCopyMatching call via kSecUseAuthenticationContext." WWDC22 Streamline local authorization flows session "By binding the LAContext to our private key reference, we ensure that executing the signature operation will not trigger another authentication, while allowing the operation to continue without unnecessary prompts. These binding also means that no additional user interactions will be required for future signatures until the LAContext is invalidated." Furthermore this is complicated by the touchIDAuthenticationAllowableReuseDuration property from LAContext instance which states that "The default value is 0, meaning that no previous biometric unlock can be reused." which is in direct contradiction to what I have experienced while working with LAContext and sources mentioned above. While digging on this, whether this behavior is intended or not, I came across a post (I would love to share it, but the domain is not permitted) that shared the same findings (and concerns) regarding LAContext behavior as me. The author also provided a FB9984036 feedback number - although no further update was made on that topic. So my questions are: Is it safe to reuse LAContext (authenticated) instance? How long such instance is considered authenticated?, is it a time duration or perhaps it stays in authenticated state until explicitly invalidated using invalidate method. (its invalidated for sure when app is terminated, but this was to be expected :)) How does touchIDAuthenticationAllowableReuseDuration work, and how does it correlate to "reusability" of the authenticated LAContext instance? In what scenarios touchIDAuthenticationAllowableReuseDuration should be used and what is its expected behavior? (I have tried it both on iOS and macOS; from my perspective API this does not "work")
Replies
0
Boosts
0
Views
49
Activity
6h
LAContext and its usage in context of Local Authentication
While working with Local Authentication framework, specifically LAContext class I found myself with few contradictions to documentation, and although I believe that those differences are rather positive than negative, either documentation is lacking behind or those APIs are not working as intended - which I believe is combination of both. 1. Local Authentication 1.1 Biometry type, and associated with it hash With introduction of LADomainState one can extract underlying biometry type along it's (current) state hash this way: @available(iOS 18, macOS 15, *) func postIOS18() { let context = LAContext() let biometryType = context.domainState.biometry.biometryType // (1) let biometryStateHash = context.domainState.biometry.stateHash // (2) } prior to receiving above APIs, we would retrieve such information something along those lines: func preIOS18() { let context = LAContext() let policy: LAPolicy // ... var error: NSError? _ = context.canEvaluatePolicy(policy, error: error) // (3) // ... (Handle error - if present) let biometryType = context.biometryType // (4) let biometryStateHash = context.evaluatedPolicyDomainState // (5) } However, moving let biometryType = context.biometryType (4) before call to canEvaluatePolicy (3) still yields correct biometry type. This is in contradiction to article from Local Authentication documentation page Optionally, Adjust Your User Interface to Accommodate Face ID. Furthermore, biometryType documentation does not mentions such requirement. Another difference is that call to canEvaluatePolicy (3) might return an error, eg. LAError(.biometryLockout) (if implemented correctly) preventing as from returning biometryStateHash (5) with nil value. This is not the case for new API, where the same call (2) will yield nil as a result - LADomainStateBiometry documentation does not mention it. In summary, here are some questions: Which API should be used to retrieve biometry type?, and why the "older way" has not been deprecated? Is is safe to assume that calls to biometryType and stateHash from LADomainStateBiometry will produce meaningful results without prior call to canEvaluatePolicy? Should I assume that call to biometryType found on LAContext instance will always return biometryType without prior call to canEvaluatePolicy?, or perhaps those are only side effects of changes made to accommodate LADomainState, and prior to them (pre-iOS 18) we must call canEvaluatePolicy to get meaningful value. Are the stateHash properties found on LADomainState, LADomainStateBiometry and LADomainStateCompanion will return nil upon encountering any error under the hood? (which would be equivalent of below code, prior to iOS 18) func biometryStateHash() -> Data? { let context = LAContext() if #available(iOS 18, macOS 15, *) { _ = context.canEvaluatePolicy(policy, error: nil) return context.evaluatedPolicyDomainState } else { return context.domainState.biometry.stateHash } } 1.2 Deprecation of evaluatedDomainState There is a forum post LAContext.evaluatedPolicyDomainState change between major OS versions mentioning missing documentation (fixed), however there's still information missing of how they correlate to each other. From my findings, the deprecated evaluatedDomainState property value matches those of LADomainState stateHash (when no companion device is present), and LADomainStateBiometry stateHash (all the time). Are those assumptions correct? 1.3 LAContext (authenticated) session lifespan Theres is little information about it state when authenticated by the user. Documentation on LAContext does not mention this behavior, while there are hints that once authenticated, and context is reused, any farther calls will not prompt user with UI. The problem is that this behavior is little, to no documented. Here are few examples I have found: Logging a User into Your App with Face ID or Touch ID (code sample) contains comment // Get a fresh context for each login. If you use the same context on multiple attempts //  (by commenting out the next line), then a previously successful authentication //  causes the next policy evaluation to succeed without testing biometry again. //  That's usually not what you want. Recent forum post, where such approach is mentioned by Quinn 'The Eskimo!' "At the API level, one option you have is to create an LAContext and pass it in to each SecItemCopyMatching call via kSecUseAuthenticationContext." WWDC22 Streamline local authorization flows session "By binding the LAContext to our private key reference, we ensure that executing the signature operation will not trigger another authentication, while allowing the operation to continue without unnecessary prompts. These binding also means that no additional user interactions will be required for future signatures until the LAContext is invalidated." Furthermore this is complicated by the touchIDAuthenticationAllowableReuseDuration property from LAContext instance which states that "The default value is 0, meaning that no previous biometric unlock can be reused." which is in direct contradiction to what I have experienced while working with LAContext and sources mentioned above. While digging on this, whether this behavior is intended or not, I came across a post (I would love to share it, but the domain is not permitted) that shared the same findings (and concerns) regarding LAContext behavior as me. The author also provided a FB9984036 feedback number - although no further update was made on that topic. So my questions are: Is it safe to reuse LAContext (authenticated) instance? How long such instance is considered authenticated?, is it a time duration or perhaps it stays in authenticated state until explicitly invalidated using invalidate method. (its invalidated for sure when app is terminated, but this was to be expected :)) How does,touchIDAuthenticationAllowableReuseDuration work, and how does it correlate to "reusability" of the authenticated LAContext instance? In what scenarios, touchIDAuthenticationAllowableReuseDuration should be used and what is its expected behavior? (I have tried it both on iOS and macOS; from my perspective API this does not "work")
Replies
0
Boosts
0
Views
45
Activity
6h
Face ID hardware failure
What could be the possible reasons for my device getting a Face id hardware failure after updating the phone to 26.4.1? I am suspect there could be some failure connections between TrueDepth sensors and front camera. Can anyone try to help me here
Replies
0
Boosts
0
Views
91
Activity
3w
Exploring Secure Enclave–backed biometric authorization between macOS and iPhone using public APIs (FaceBridge prototype)
Hi everyone, I’ve been working on an experimental prototype called FaceBridge that explores whether Secure Enclave–backed biometric authorization can be delegated between macOS and iPhone using only public Apple APIs. The goal of the project was to better understand the architectural boundaries of cross-device trust and approval flows that resemble Apple’s built-in Touch ID / Continuity authorization experiences. FaceBridge implements a local authorization pipeline where: macOS generates a signed authorization request the request is delivered to a trusted nearby iPhone over BLE / Network framework the iPhone verifies sender identity Face ID approval is requested using LocalAuthentication the iPhone signs the approval response using Secure Enclave–backed keys macOS validates the response and unlocks a protected action Security properties currently implemented: • Secure Enclave–backed signing identities per device • cryptographic device pairing and trust persistence • replay protection using nonce + timestamp binding • structured authorization request/response envelopes • signed responder identity verification • trusted-device registry model • local encrypted transport over BLE and local network This is intentionally not attempting to intercept or replace system-level Touch ID dialogs (App Store installs, Keychain prompts, loginwindow, etc.), but instead explores what is possible within application-level authorization boundaries using public APIs only. The project is open source: https://github.com/wesleysfavarin/facebridge Technical architecture write-up: https://medium.com/@wesleysfavarin/facebridge I’m particularly interested in feedback around: • recommended Secure Enclave identity lifecycle patterns • best practices for cross-device trust persistence • LocalAuthentication usage in delegated approval scenarios • whether similar authorization models are expected to become more formally supported across Apple platforms in the future Thanks in advance for any guidance or suggestions.
Replies
1
Boosts
0
Views
258
Activity
Mar ’26
Biometrics prompt + private key access race condition on since iOS 26.1
We are using SecItemCopyMatching from LocalAuthentication to access the private key to sign a challenge in our native iOS app twice in a few seconds from user interactions. This was working as expected up until about a week ago where we started getting reports of it hanging on the biometrics screen (see screenshot below). From our investigation we've found the following: It impacts newer iPhones using iOS 26.1 and later. We have replicated on these devices: iPhone 17 Pro max iPhone 16 Pro iPhone 15 Pro max iPhone 15 Only reproducible if the app tries to access the private key twice in quick succession after granting access to face ID. Looks like a race condition between the biometrics permission prompt and Keychain private key access We were able to make it work by waiting 10 seconds between private key actions, but this is terrible UX. We tried adding adding retries over the span of 10 seconds which fixed it on some devices, but not all. We checked the release notes for iOS 26.1, but there is nothing related to this. Screenshot:
Replies
5
Boosts
0
Views
850
Activity
Mar ’26
evaluatedPolicyDomainState
Hi Apple Developers, I'm having a problem with evaluatedPolicyDomainState: on the same device, its value keeps changing and then switching back to the original. My current iOS version is 26.1. I upgraded my iOS from version 18.6.2 to 26.1. What could be the potential reasons for this issue? { NSError *error; BOOL success = YES; char *eds = nil; int edslen = 0; LAContext *context = [[LAContext alloc] init]; // test if we can evaluate the policy, this test will tell us if Touch ID is available and enrolled // success = [context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]; if (SystemVersion > 9.3) { // test if we can evaluate the policy, this test will tell us if Touch ID is available and enrolled success = [context canEvaluatePolicy: LAPolicyDeviceOwnerAuthentication error:&error]; } else{ // test if we can evaluate the policy, this test will tell us if Touch ID is available and enrolled success = [context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]; } if (success) { if (@available(iOS 18.0, *)) { NSData *stateHash = nil; if ([context respondsToSelector:@selector(domainState)]) { stateHash = [[context performSelector:@selector(domainState)] performSelector:@selector(stateHash)]; }else{ stateHash = [context evaluatedPolicyDomainState]; } eds = (char *)stateHash.bytes; edslen = (int)stateHash.length; } else { eds = (char *)[[context evaluatedPolicyDomainState] bytes]; edslen = (int)[[context evaluatedPolicyDomainState] length]; } CC_SHA256(eds, edslen, uviOut); *poutlen = CC_SHA256_DIGEST_LENGTH; } else { *poutlen = 32; gm_memset(uviOut, 0x01, 32); } }
Replies
6
Boosts
0
Views
1.3k
Activity
Jan ’26
Does accessing multiple Keychain items with .userPresence force multiple biometric prompts despite reuse duration?
Hi everyone, I'm working on an app that stores multiple secrets in the Keychain, each protected with .userPresence. My goal is to authenticate the user once via FaceID/TouchID and then read multiple Keychain items without triggering subsequent prompts. I am reusing the same LAContext instance for these operations, and I have set: context.touchIDAuthenticationAllowableReuseDuration = LATouchIDAuthenticationMaximumAllowableReuseDuration However, I'm observing that every single SecItemCopyMatching call triggers a new FaceID/TouchID prompt, even if they happen within seconds of each other using the exact same context. Here is a simplified flow of what I'm doing: Create a LAContext. Set touchIDAuthenticationAllowableReuseDuration to max. Perform a query (SecItemCopyMatching) for Item A, passing [kSecUseAuthenticationContext: context]. Result: System prompts for FaceID. Success. Immediately perform a query (SecItemCopyMatching) for Item B, passing the same [kSecUseAuthenticationContext: context]. Result: System prompts for FaceID again. My question is: Does the .userPresence access control flag inherently force a new user interaction for every Keychain access, regardless of the LAContext reuse duration? Is allowableReuseDuration only applicable for LAContext.evaluatePolicy calls and not for SecItem queries? If so, is there a recommended pattern for "unlocking" a group of Keychain items with a single biometric prompt? Environment: iOS 17+, Swift. Thanks!
Replies
3
Boosts
0
Views
585
Activity
Jan ’26
Biometric Authentication Behavior in IAP Sandbox Environment
Where the problem occurs: In-app purchase Non-ApplePay Non-local authentication login Environment where the problem occurs: Sandbox environment (Development environment, TestFlight environment) Problem handling process: Open page A in the app and purchase product B (auto-renewable subscription) on that page. User authentication is required to purchase product B. During the authentication process, the user needs to enter the Apple account and Apple account password. After completing the authentication, complete the purchase of product B. Problem in step 3: Why is FaceID or TouchID not used for authentication? Note: Face ID and Password -> iTunes Store and App Store -> Status is Enabled
Replies
0
Boosts
0
Views
183
Activity
Jun ’25