BiometricDeviceCredentialAuthenticator
public class BiometricDeviceCredentialAuthenticator : DefaultDeviceAuthenticator
An authenticator that uses biometrics (Face ID or Touch ID) with a fallback to device credentials (passcode/PIN).
This authenticator supports the BIOMETRIC_ALLOW_FALLBACK policy, allowing authentication
via either biometrics (Face ID / Touch ID) or device passcode.
Security Model
Unlike BiometricOnlyAuthenticator which uses .biometryCurrentSet to have the Secure Enclave
hardware-invalidate keys when biometric enrollment changes, this class uses a dynamic
access control policy:
- Biometrics enrolled:
.biometryAny OR .devicePasscode— allows both authentication methods. - No biometrics enrolled:
.devicePasscodeonly — avoids Secure Enclave key creation failure on devices that have a passcode set but no Face ID / Touch ID registered.
Because .biometryAny does not invalidate keys on biometric enrollment changes at the
hardware level, this class compensates with a software-level check using
LAContext.evaluatedPolicyDomainState. The biometric domain state is captured at bind time
and stored in the UserKey. During signing, the stored state is compared against the current
state — if they differ (e.g., a fingerprint was added or removed), the keys are deleted and
re-binding is required by throwing .deviceNotRegistered.
Backward Compatibility
For UserKey instances created before this change (i.e., without a stored biometricDomainState),
the enrollment check is skipped and signing proceeds normally. This ensures existing bindings
are not broken on SDK update. Only keys created after the update will enforce the biometric
domain state check.
-
Initializes the authenticator with a
BiometricAuthenticatorConfig.Declaration
Swift
public init(config: BiometricAuthenticatorConfig)Parameters
configThe configuration object for the authenticator.
-
The type of authenticator, specifically
.biometricAllowFallback.Declaration
Swift
public override func type() -> DeviceBindingAuthenticationType -
register()AsynchronousGenerates a new cryptographic key pair for biometric and device credential authentication. The key is stored in the Secure Enclave (if available) and associated with a unique key tag.
Access control flags are dynamically selected based on biometric availability:
- When biometrics are enrolled:
.biometryAny OR .devicePasscode— allows both methods. When only passcode is set:
.devicePasscodeonly — avoids Secure Enclave rejecting key creation when biometry flags are present but no biometrics are enrolled.
Throws
DeviceBindingError.unknownif access control creation fails, or aCryptoKeyerror if key generation fails.Declaration
Swift
public override func register() async throws -> KeyPairReturn Value
A
KeyPaircontaining the newly generated public and private keys. - When biometrics are enrolled:
-
authenticate(keyTag:Asynchronous) Declaration
Swift
public override func authenticate(keyTag: String) async -> Result<SecKey, Error>Return Value
A
Resultcontaining theSecKeyon success, or anErroron failure. -
Checks if the device supports biometric or device credential authentication.
Note
Always returnsfalseon simulator — biometric keys require Secure Enclave to enforce the authentication challenge. Without it the key is accessible with no user verification, which is equivalent to the NONE type.Declaration
Swift
public override func isSupported(attestation: Attestation) -> BoolParameters
attestationThe attestation type (currently ignored).
Return Value
trueif the device supports the authentication policy,falseotherwise. -
deleteKeys()AsynchronousDeletes all biometric and device credential keys associated with this authenticator. It iterates through all stored user keys and deletes those with
.biometricOnlyor.biometricAllowFallbackauthentication types.Throws
CryptoKeyErrorif key deletion fails.Declaration
Swift
public override func deleteKeys() async throws -
Returns the current biometric domain state from LAContext. Used to detect biometric enrollment changes between bind and sign operations.
Declaration
Swift
public func biometricDomainState() -> Data?Return Value
The evaluated policy domain state data, or
nilif biometrics are not enrolled. -
Override sign to validate biometric enrollment has not changed since binding. If biometrics were enrolled at bind time and the enrollment has since changed, the keys are deleted and
.deviceNotRegisteredis thrown to trigger re-binding.Declaration
Swift
public override func sign(params: UserKeySigningParameters, journey: Journey?) throws -> String
View on GitHub