To add biometric login to an app you unlock a credential you already have.
To add biometric login to an app you do two things: ask the operating system to check a face or a fingerprint, then use that answer to unlock a credential the device is already holding. The check is one function call. Everything that matters is the second half. Face ID and a fingerprint reader never tell your server who anybody is, so biometric authentication sits on top of adding user login rather than replacing it.
Below: what the sensor actually proves, the keychain pattern that turns a boolean into a real session, what fingerprint login and Face ID in an app cost to build in React Native and Expo, and the cases where this is the wrong feature.
See what sits behind the gateThe short version
Biometrics unlock a secret, they do not issue one.
A biometric check is local. The phone compares the scan against what its owner enrolled and hands your app a single yes or no. No image, no template and no identity crosses into your code, and nothing at all reaches your server. Your app learns exactly one thing: the person holding this phone is one of the people enrolled on it.
So the feature you are shipping is skip the password on a device we already trust. That is genuinely worth having. It is not an authentication method, and an app that treats a yes from the sensor as proof of identity has built a lock with no door behind it.
What the sensor actually proves
Apple states the boundary plainly. The Local Authentication framework hands the comparison to the Secure Enclave, a hardware security processor isolated from the rest of the system, and your app is never given the underlying authentication data. There is no fingerprint image for your code to read. You pick a policy, you supply the sentence the system shows the user explaining why you are asking, and what comes back is a boolean: it worked or it did not. That boolean is the entire output of the feature.
Android draws the same line with the BiometricPrompt dialog from the Biometric library, and adds a distinction worth knowing about. Sensors are graded. BIOMETRIC_STRONG is a Class 3 sensor and BIOMETRIC_WEAK is a Class 2 sensor, as defined in the Android compatibility definition, and DEVICE_CREDENTIAL means the screen lock PIN, pattern or password instead. A cheap face unlock can be Class 2. If the gate guards anything valuable, ask for a strong authenticator rather than accepting whatever the handset offers, and decide deliberately whether the screen lock counts as a pass.
The second limit trips up more projects than the first, and it is not technical. Biometric enrolment belongs to the phone, not to your app. Anyone whose face or finger is enrolled on that device opens your app, and on a shared tablet that is several people. Your code cannot tell which of them it was, because it only ever sees the boolean. If your app holds something exactly one person should see, a biometric gate on its own is the wrong control.
The pattern: a token in the keychain, biometry on the door
Here is the shape that works. The user signs in once the ordinary way, with a password or an emailed code, and your server returns a refresh token. You write that token into the iOS keychain or the Android keystore with an access control setting that requires biometry before the item can be read. On the next launch you run the biometric check, read the token back, and exchange it for a session. The sensor is not authenticating anyone. It is the key to a drawer, and the drawer holds something your server issued and can take back.
That one decision separates a real feature from a screen saver. If the yes from the sensor only flips a variable inside your app, then anybody who can get the phone open can get your app open too, and your server cannot tell the difference. If the yes is the only route to a token your server signed, your server keeps control: it can revoke that token, expire it, or require a fresh sign-in after a password change. All of that belongs to app authentication rather than to the sensor, which is why the sensor is the small part of this job.
Decide the enrolment-change case before you ship. On iOS, storing the item with the constraint for the currently enrolled set means the item is invalidated when fingers are added or removed for Touch ID, or when the user re-enrols for Face ID. The looser constraint keeps the item readable after those changes. Android keystore keys have a comparable option, so check the exact flag in the keystore reference before you rely on it. The strict version costs your user one extra sign-in after they add a finger, and it stops a finger added to a stolen phone from opening your app.
Try it
What sits behind the gate
The biometric check is identical in all four. What changes is what it was guarding.
Better looking, no stronger
Someone holding the unlocked phone gets the session, by clearing app data and editing the flag.
Your server cannot tell a real sign-in from a bypass.
What it costs to build in a React Native and Expo app
In an Expo project this is a small library and an afternoon, not a project. The LocalAuthentication module answers the four questions you need in order: hasHardwareAsync for whether a face or fingerprint scanner exists on the device, isEnrolledAsync for whether anything is saved to compare against, supportedAuthenticationTypesAsync for which kinds the device supports, and authenticateAsync for the check itself, which resolves to success true or to success false with an error.
Two configuration details catch people out. Apple requires a description of why the app uses Face ID, supplied either through the config plugin faceIDPermission property or as NSFaceIDUsageDescription in the Info.plist. And Face ID is not supported in Expo Go, so you need a development build before you can test it on iOS at all. Find that out now rather than an hour before a demo.
The library is the easy half. The work is the fallback path: a device with no sensor, a user who declines, a user who fails three times, a sensor locked out after repeated failures, and the first launch when there is no stored token yet. Each of those has to land somewhere sensible instead of on a dead prompt. That is ordinary app security basics rather than anything biometric, and it is where the day goes.
Build it, buy it, or skip it
Skip it when there is nothing behind the login worth a second lock. A read-only app with no personal data and no payments gains a nicer launch and one more way to fail. Skip it too when your users share devices, for the enrolment reason above, because the gate will not do what the person asking for it thinks it does.
Buy it, meaning do not write it yourself, when you already use a hosted authentication provider. Check whether its mobile SDK already stores the refresh token behind a device biometric gate, because if it does you are turning on a setting rather than designing a key store. We have not verified which providers do this, so read the SDK reference rather than the marketing page, and test that revoking a token from the dashboard actually closes the app session.
Build it when the login guards money, health information, private messages or anything an employer would call confidential, and the realistic alternative is people typing a password so often that they pick a short one. That is the case where biometrics raise security rather than only convenience. What each of those signed-in people is then allowed to do is a separate question: it is what the login unlocks, and it is worth settling before you write the gate.
Which version of this are you actually building
| What you built | Skips typing at launch | Stops someone holding the unlocked phone | Secret sits in hardware-backed storage | Effort |
|---|---|---|---|---|
| Password typed at every launch | No | Yes | nothing is stored | none |
| Signed in forever, no gate | Yes | No | No | none |
| Biometric check, flag in app storage | Yes | until app data is cleared | No | an hour |
| Biometric gate on a keychain token | Yes | Yes | Yes | about a day |
| Same, invalidated on enrolment change | Yes | Yes | Yes | a day plus testing |
Building the app the login is protecting
If the app does not exist yet, build the login and the thing it protects together. Retrofitting a token store into a finished app is the version that goes wrong, because by then the session model was decided by whatever was quickest at the time, and the biometric gate ends up wrapped around a variable instead of a key.
Newly is an AI app builder. You describe the app in plain English and it writes a real React Native and Expo project you own, runs it on a cloud iPhone or Android simulator while it builds, and ships it to TestFlight and to Google Play internal testing. Plans are $25 a month and there is no free plan. Because the output is an ordinary Expo project, adding the platform biometric library is the same job it would be in any other Expo project, and the code comes out as a ZIP from Settings or through two-way GitHub sync if you want to write that part by hand.
One honest note on that: we did not find biometric login documented as a one-click switch, so plan for it as code inside the project rather than as a setting you turn on. Describe the app and the session rules first, get a signed-in build running on a real handset, then add the sensor over a session model you already trust.
Questions people ask about biometric login
No. The comparison happens on the device and your app receives only a success or failure result. Apple's Local Authentication framework keeps the underlying data inside the Secure Enclave and never hands it to your code, so your server learns nothing at all. That is why biometrics have to unlock a credential your server already issued rather than acting as a login of their own.
Describe the app, then decide what the gate protects
Write down what a signed-in person can see and do, and build the session model and the biometric gate as one thing rather than bolting one onto the other later.
Start building