I am working on improving Keychain item storage secured with Face ID using SecAccessControlCreateWithFlags
. The implementation uses the .biometryAny
flag as shown below:
SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.biometryAny,
&error
)
While this approach generally works as expected, I encountered a specific edge case during testing. On iOS 18.3.1 with Xcode 15.4, the following sequence causes the Keychain item to become inaccessible:
- Navigate to Settings > Face ID & Passcode and select Reset Face ID.
- Before setting up a new Face ID, tap the Back button to exit the setup process.
- Reopen the Face ID setup and complete the enrollment.
- Return to the app—previously stored Keychain items protected by
.biometryAny
are no longer available.
This behavior appears to be a change introduced in recent iOS versions. In versions prior to iOS 15, resetting or deleting Face ID entries did not invalidate existing Keychain items protected by .biometryAny
.
This difference in behavior between iOS versions raises questions about the changes to biometric protection handling.
Any suggestions are welcomed that might shine a light on what the best practice to use keychain access control and prevent the data to become unavailable.
It’s not uncommon for the exact behaviour of biometrics to change between OS releases. Indeed, we call out this possibility in the doc comments for LADomainStateBiometry
; see <LocalAuthentication/LADomainState.h>
.
However, the specific steps you’ve described do seem weird. A .biometryAny
constraint is supposed to work with… well… any biometry, so step 3 should be sufficient to allow you to access your items.
Given that, I encourage you to file a bug about this. Either this is something we can fix or, if not, we need to update the documentation for .biometryAny
.
Please post your bug number, just for the record.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"