Face Id
Introduction
Implementation
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as LocalAuthentication from "expo-local-authentication";
import * as SecureStore from "expo-secure-store";
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
const CREDENTIALS_KEY = "biometric_credentials";
interface BiometricState {
isBiometricEnabled: boolean;
storedUserEmail: string | null;
// Modal state (not persisted)
isModalOpen: boolean;
pendingCredentials: { email: string; password: string } | null;
}
interface BiometricActions {
enableBiometric: (email: string, password: string) => Promise<void>;
disableBiometric: () => Promise<void>;
clearBiometric: () => Promise<void>;
authenticateWithBiometric: () => Promise<{
email: string;
password: string;
} | null>;
checkBiometricAvailability: () => Promise<boolean>;
// Modal actions
promptEnableBiometric: (email: string, password: string) => void;
confirmEnableBiometric: () => Promise<void>;
dismissEnableBiometric: () => void;
}
type BiometricStore = BiometricState & BiometricActions;
export const useBiometric = create<BiometricStore>()(
persist(
(set, get) => ({
isBiometricEnabled: false,
storedUserEmail: null,
isModalOpen: false,
pendingCredentials: null,
checkBiometricAvailability: async () => {
const hasHardware = await LocalAuthentication.hasHardwareAsync();
if (!hasHardware) return false;
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
return isEnrolled;
},
enableBiometric: async (email: string, password: string) => {
// Store credentials securely with biometric protection
await SecureStore.setItemAsync(
CREDENTIALS_KEY,
JSON.stringify({ email, password }),
{
requireAuthentication: true,
authenticationPrompt: "Authenticate to enable Face ID login",
},
);
set({ isBiometricEnabled: true, storedUserEmail: email });
},
clearBiometric: async () => {
await SecureStore.deleteItemAsync(CREDENTIALS_KEY);
set({ isBiometricEnabled: false, storedUserEmail: null });
},
authenticateWithBiometric: async () => {
try {
// Retrieve stored credentials
const credentialsJson = await SecureStore.getItemAsync(
CREDENTIALS_KEY,
{
requireAuthentication: true,
authenticationPrompt: "Authenticate to retrieve credentials",
},
);
if (!credentialsJson) {
return null;
}
return JSON.parse(credentialsJson) as {
email: string;
password: string;
};
} catch {
return null;
}
},
promptEnableBiometric: (email: string, password: string) => {
set({ isModalOpen: true, pendingCredentials: { email, password } });
},
confirmEnableBiometric: async () => {
const { pendingCredentials } = get();
if (pendingCredentials) {
// Store credentials securely with biometric protection
await SecureStore.setItemAsync(
CREDENTIALS_KEY,
JSON.stringify(pendingCredentials),
{
requireAuthentication: true,
authenticationPrompt: "Authenticate to enable Face ID login",
},
);
set({
isBiometricEnabled: true,
storedUserEmail: pendingCredentials.email,
isModalOpen: false,
pendingCredentials: null,
});
}
},
dismissEnableBiometric: () => {
set({
isModalOpen: false,
pendingCredentials: null,
});
},
}),
{
name: "biometric-storage",
storage: createJSONStorage(() => AsyncStorage),
partialize: (state) => ({
isBiometricEnabled: state.isBiometricEnabled,
storedUserEmail: state.storedUserEmail,
}),
},
),
);Last updated