Skip to content
Innovialabs

How to Add Face ID and Fingerprint Login to a Mobile App

Practice4 min

In short

Biometrics never hand your app a fingerprint or face; they only unlock a key or token in the device's secure storage, so proper Face ID login is built on Keychain/Keystore, not on a yes/no check.

How it actually works

Your app never receives a fingerprint or a face scan. Matching happens in a secure module on the device: the Secure Enclave on iPhone, a TEE or dedicated chip on Android. The system only tells the app the result.

There are two ways to use that result:

  • A yes/no check. The app asks "did the user confirm?" and gets true. Simple, but on a compromised device the answer can be faked.
  • Cryptographic binding. A secret (such as a refresh token) or an encryption key lives in Keychain/Keystore with a rule "release only after biometrics". Without a successful check the data is simply unavailable.

Use the second approach for signing in. The first is fine for low-stakes things: hiding screen content, confirming a harmless action.

A typical login flow

  1. The user signs in the normal way the first time: phone and code, password, or Apple/Google.
  2. The app offers to enable Face ID or fingerprint login.
  3. The refresh token is saved to secure storage with a biometric requirement.
  4. On the next launch the system shows the biometric prompt, storage releases the token, and the app gets a fresh session from the server.

The server knows nothing about biometrics; it sees an ordinary token refresh.

Implementation on iOS

Use the Keychain with the .biometryCurrentSet access flag, and add NSFaceIDUsageDescription to Info.plist with a clear explanation; without it the Face ID prompt will not work.

let access = SecAccessControlCreateWithFlags(
    nil,
    kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
    .biometryCurrentSet,
    nil
)!
let query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrAccount as String: "refresh_token",
    kSecValueData as String: tokenData,
    kSecAttrAccessControl as String: access
]
SecItemAdd(query as CFDictionary, nil)

When the app reads this item, the system shows Face ID or Touch ID on its own.

Implementation on Android

Create a key in the Android Keystore that requires user authentication, and pass a Cipher to BiometricPrompt through a CryptoObject. The token can be decrypted only after a successful check.

val spec = KeyGenParameterSpec.Builder(
    "session_key",
    KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
    .setUserAuthenticationRequired(true)
    .setInvalidatedByBiometricEnrollment(true)
    .build()

For cryptographic binding, request the BIOMETRIC_STRONG class; weaker unlock methods do not support it.

Flutter and React Native

  • Flutter: the local_auth package gives you only a yes/no check. To bind to storage, use flutter_secure_storage with biometric access options or specialised packages that work through Keystore/Keychain.
  • React Native: expo-local-authentication is also yes/no. To keep a token behind biometrics, use react-native-keychain with accessControl, or react-native-biometrics, which creates keys and signs data.

Before picking a package, check that it really uses Keychain/Keystore under the hood rather than just showing the system dialog.

Fallback and re-enrollment

  • Fallback. Biometrics can fail: wet fingers, a mask, lockout after several failed attempts. Always keep a path through the device passcode, an in-app PIN or a full login.
  • New fingerprint or face. With .biometryCurrentSet and setInvalidatedByBiometricEnrollment(true), adding new biometrics makes the stored secret unavailable; on Android you get KeyPermanentlyInvalidatedException. This is protection, not a bug: handle it, delete the old secret, ask for a full login and let the user re-enable biometrics.
  • Biometrics turned off. If the user removed all fingerprints or disabled Face ID, go straight to the normal login.

What not to rely on it for

  • Biometrics confirm "someone enrolled", not a specific person. A family member's fingerprint may be on the device.
  • It does not replace the first login and is not an account recovery method.
  • For high-stakes actions (transfers, changing the phone number), add server-side verification: a signature with a key from secure storage or a fresh code.
  • A yes/no check without storage is easy to bypass on rooted and jailbroken devices.

FAQ

No. Biometric data never leaves the device. The server sees only a normal token refresh request or a key signature.

We can help with this

If you'd rather get the result than figure it all out yourself — this is what we do every day.

Discuss a project

Read next

Have an idea or a task? Tell us about it

We'll reply within one business day and propose a solution. The consultation is free.