π₯ Secure Your React Native App with Biometric Authentication in Expo (2025 Guide) π
Learn how to implement biometric authentication (Face ID & Fingerprint) in your Expo Router app using Expoβs Local Authentication API!
π Why Use Biometric Authentication in Mobile Apps?
Security is non-negotiable in mobile applications today. With the rise of cyber threats and data breaches, integrating biometric authentication (Face ID & Fingerprint) enhances security while offering a seamless user experience.
β Faster Login & Authentication
β No Need to Remember Passwords
β Enhanced Security with Device-Level ProtectionExpo makes it easy to implement biometric authentication without writing native code! π
π Step 1: Setting Up an Expo App with Router
Before we dive into authentication, letβs set up an Expo app using the latest Expo Router for modern file-based navigation.
Run the command below to create a new Expo app:
npx create-expo-stack@latest securityApp --expo-router
cd securityApp
npm install
π This command creates a React Native app with Expo Router support!
π Step 2: Install & Configure Biometric Authentication
Expo provides an easy-to-use Local Authentication API via
expo-local-authentication
. Letβs install it:
expo install expo-local-authentication
This package allows us to use Face ID & Fingerprint authentication without needing to eject our Expo project! π
π Step 3: Implementing Biometric Authentication in Expo Router
Now, letβs create a lock screen that prompts users for biometric authentication before accessing the app.
π Biometric Authentication Screen (
/app/(modals)/lock.tsx
)
import { SafeAreaView, Text, TouchableOpacity, StyleSheet } from "react-native";
import * as LocalAuthentication from "expo-local-authentication";
import { useRouter } from "expo-router";
import { useEffect, useState } from "react";
import * as Haptics from "expo-haptics";
export default function LockScreen() {
const [isBiometricAvailable, setIsBiometricAvailable] = useState(false);
const router = useRouter();
useEffect(() => {
checkBiometricAvailability();
}, []);
const checkBiometricAvailability = async () => {
const hasHardware = await LocalAuthentication.hasHardwareAsync();
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
setIsBiometricAvailable(hasHardware && isEnrolled);
};
const authenticateUser = async () => {
const result = await LocalAuthentication.authenticateAsync({
promptMessage: "Authenticate to access the app",
fallbackLabel: "Use Passcode",
});
if (result.success) {
router.push("/");
} else {
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
}
};
return (
<SafeAreaView style={styles.container}>
<Text style={styles.title}>π Secure Access</Text>
<TouchableOpacity
style={styles.authButton}
onPress={authenticateUser}
disabled={!isBiometricAvailable}
>
<Text style={styles.authButtonText}>
{isBiometricAvailable ? "Authenticate with Face ID / Fingerprint" : "Biometrics Not Available"}
</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: "center", justifyContent: "center" },
title: { fontSize: 24, fontWeight: "bold", marginBottom: 20 },
authButton: { padding: 15, backgroundColor: "#3D38ED", borderRadius: 10 },
authButtonText: { color: "#fff", fontSize: 16, fontWeight: "bold" },
});
πΉ Whatβs happening here?
β We check for biometric availability usingLocalAuthentication.hasHardwareAsync()
β If available, the app prompts users to authenticate
β On success, the user is granted access to the app
β If authentication fails, the app provides haptic feedback
π Step 4: Auto-Locking the App on Inactivity π
To enhance security, letβs automatically lock the app when the user is inactive for 3 seconds.
π User Inactivity Provider (
/context/UserInactivity.tsx
)
import { useRouter } from "expo-router";
import { useEffect, useRef } from "react";
import { AppState } from "react-native";
import { MMKV } from "react-native-mmkv";
const storage = new MMKV({ id: "UserInactivity" });
const LOCK_TIME = 3000; // Auto-lock after 3 seconds
export const UserInactivityProvider = ({ children }: any) => {
const appState = useRef(AppState.currentState);
const router = useRouter();
useEffect(() => {
const subscription = AppState.addEventListener("change", handleAppStateChange);
return () => {
subscription.remove();
};
}, []);
const handleAppStateChange = (nextAppState: any) => {
if (nextAppState === "background" || nextAppState === "inactive") {
router.push("/(modals)/lock");
} else if (nextAppState === "active" && appState.current.match(/inactive|background/)) {
const elapsed = Date.now() - (storage.getNumber("startTime") || 0);
if (elapsed >= LOCK_TIME) {
router.push("/(modals)/lock");
}
}
appState.current = nextAppState;
};
return children;
};
π This ensures that if a user leaves the app for more than 3 seconds, they will have to re-authenticate! π
π Step 5: Testing Biometric Authentication
πΉ Run your Expo app on a physical device:
npx expo start
iOS Users: Enable Face ID in Settings > Face ID & Passcode
Android Users: Enable Fingerprint Unlock in Security Settings
π Final Thoughts & Next Steps
π― You just built a secure authentication system with biometrics in Expo!
β Biometric authentication improves app security
β Auto-locking feature protects user data
β Expo makes this easy withexpo-local-authentication
π‘ Want to take it further?
πΉ Integrate Expo SecureStore for password storage
πΉ Add Multi-Factor Authentication (MFA)
πΉ Implement JWT authentication for API securityπ₯ What do you think about biometric authentication? Let me know in the comments! ππ
π Follow me on LinkedIn for more React Native tips!
π https://www.linkedin.com/in/prasadranjane32/