Skip to main content

React Native Biometrics With Expo LocalAuthentication

· 5 min read
Full Stack Developer
Last updated on May 17, 2026

React Native Face ID Biometrics Auth

Biometric authentication can make a React Native app feel faster and safer, but it should be designed as a local unlock step, not as a replacement for your account system. The device verifies the enrolled fingerprint or face; your app decides what to unlock after that verification succeeds.

Expo's expo-local-authentication package gives React Native apps access to Face ID, Touch ID, Android Biometric Prompt, and device passcode fallback.

Quick Answer

Install expo-local-authentication, configure the Face ID usage message for iOS, test Face ID in a development build, and only reveal sensitive app state after authenticateAsync() returns success: true.

corepack yarn expo install expo-local-authentication

For a generic Expo project, the equivalent command is:

npx expo install expo-local-authentication

If you are setting up a full app, start with the React Native development environment guide and check the current React Native stack. For build errors, use React Native Errors and Solutions.

What Biometrics Should Protect

Use biometrics for local, user-friendly unlock flows:

  • opening an already signed-in account;
  • revealing a saved card, profile, or private setting;
  • confirming a sensitive in-app action;
  • gating access to a local admin or driver screen;
  • re-authenticating before payment, subscription, or account changes.

Do not treat biometrics as proof that a user owns a server account. Keep your normal authentication session, refresh token, Firebase Auth user, or backend identity checks in place. Biometrics should unlock local access after the user is already authorized.

Install the Package

In current Instamobile-style React Native apps with Expo modules:

corepack yarn expo install expo-local-authentication

Then rebuild the native app if this is a new native dependency:

corepack yarn ios
corepack yarn android

Configure Face ID Permission Text

Apple requires a clear NSFaceIDUsageDescription message. If your app uses Expo config plugins, configure it in app.json or app.config.js:

{
"expo": {
"plugins": [
[
"expo-local-authentication",
{
"faceIDPermission": "Allow $(PRODUCT_NAME) to use Face ID to unlock your account."
}
]
]
}
}

If your app manages native iOS files manually, add the matching NSFaceIDUsageDescription key in Info.plist.

Face ID is not fully testable in Expo Go. Use a development build or a native debug build when validating Face ID behavior.

Mega Bundle Sale is ON! Get ALL of our React Native codebases at 90% OFF discount 🔥

Get the Mega Bundle

Check Hardware and Enrollment

Before showing a biometric prompt, verify that the device supports it and that the user has enrolled a biometric method:

import * as LocalAuthentication from 'expo-local-authentication';

export async function getBiometricStatus() {
const [hasHardware, isEnrolled, supportedTypes] = await Promise.all([
LocalAuthentication.hasHardwareAsync(),
LocalAuthentication.isEnrolledAsync(),
LocalAuthentication.supportedAuthenticationTypesAsync(),
]);

return {
hasHardware,
isEnrolled,
supportedTypes,
canAuthenticate: hasHardware && isEnrolled,
};
}

Use this status to show a useful setup message instead of prompting users on a device that cannot authenticate.

Authenticate Before Unlocking Sensitive UI

Keep the authentication function small and explicit:

import * as LocalAuthentication from 'expo-local-authentication';

type UnlockResult =
| { ok: true }
| { ok: false; reason: 'not_available' | 'not_enrolled' | string };

export async function authenticateToUnlock(): Promise<UnlockResult> {
const hasHardware = await LocalAuthentication.hasHardwareAsync();
if (!hasHardware) {
return { ok: false, reason: 'not_available' };
}

const isEnrolled = await LocalAuthentication.isEnrolledAsync();
if (!isEnrolled) {
return { ok: false, reason: 'not_enrolled' };
}

const result = await LocalAuthentication.authenticateAsync({
promptMessage: 'Unlock your account',
fallbackLabel: 'Use passcode',
biometricsSecurityLevel: 'strong',
});

if (result.success) {
return { ok: true };
}

return { ok: false, reason: result.error };
}

Then gate the sensitive UI:

import { useState } from 'react';
import { Button, Text, View } from 'react-native';
import { authenticateToUnlock } from './authenticateToUnlock';

export function PrivateAccountPanel() {
const [unlocked, setUnlocked] = useState(false);
const [message, setMessage] = useState<string | null>(null);

async function unlock() {
const result = await authenticateToUnlock();

if (result.ok) {
setUnlocked(true);
setMessage(null);
return;
}

setMessage('Biometric authentication is not available right now.');
}

return (
<View>
{unlocked ? <Text>Private account details</Text> : <Button title="Unlock" onPress={unlock} />}
{message ? <Text>{message}</Text> : null}
</View>
);
}

Production Security Notes

Biometrics are a local gate. For production apps:

  • keep server-side authorization in place;
  • require a valid app session before showing biometric unlock;
  • store refresh tokens or local secrets only in secure storage;
  • use clear fallback behavior for devices without enrolled biometrics;
  • explain why Face ID is used in the permission string;
  • test both successful authentication and cancellation;
  • test lockout, passcode fallback, and app backgrounding.

Android permissions for biometrics are handled by the Expo package manifest. On iOS, the Face ID usage description is the important release requirement.

Looking for a custom mobile application?

Our team of expert mobile developers can help you build a custom mobile app that meets your specific needs.

Get in Touch

FAQ

Can I test Face ID in Expo Go?

No for the full Face ID flow. Use a development build or native build because Face ID requires native permission configuration in the app binary.

Does biometric authentication replace login?

No. It verifies the local device owner. Keep Firebase Auth, custom backend auth, or your normal account system as the source of truth.

Should I disable passcode fallback?

Only for a specific security reason. The system fallback is usually better for users and accessibility. If you disable fallback, provide a clear alternative flow.