{"id":16808,"library":"expo-local-authentication","title":"Expo Local Authentication","description":"The `expo-local-authentication` package provides a unified JavaScript API for integrating device-specific biometric authentication methods such as Face ID and Touch ID on iOS, and the Fingerprint API on Android. This module, currently at version 55.0.13, is a core component within the Expo ecosystem, ensuring seamless cross-platform functionality for biometric identification. Its release cadence is tightly coupled with the Expo SDK releases, which typically align with new React Native versions, offering predictable updates and integration. Key differentiators include its deep integration with the Expo development workflow, abstracting away native module complexities, and providing a consistent API for checking hardware availability, user enrollment, and performing biometric authentication across both major mobile platforms. It simplifies the process of adding a crucial security layer to mobile applications developed with Expo and React Native, without requiring direct native code interaction.","status":"active","version":"55.0.13","language":"javascript","source_language":"en","source_url":"https://github.com/expo/expo","tags":["javascript","react-native","expo","authentication","auth","touchID","faceID","fingerprint","typescript"],"install":[{"cmd":"npm install expo-local-authentication","lang":"bash","label":"npm"},{"cmd":"yarn add expo-local-authentication","lang":"bash","label":"yarn"},{"cmd":"pnpm add expo-local-authentication","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Required peer dependency for all Expo modules to function correctly within the Expo SDK environment.","package":"expo","optional":false}],"imports":[{"note":"This is the standard ESM import for Expo modules, bringing all functions into the `LocalAuthentication` namespace. CommonJS `require()` is generally discouraged in modern Expo/React Native projects.","wrong":"const LocalAuthentication = require('expo-local-authentication');","symbol":"LocalAuthentication","correct":"import * as LocalAuthentication from 'expo-local-authentication';"},{"note":"The primary method to prompt the user for biometric authentication. Accessed as a method of the `LocalAuthentication` namespace.","symbol":"authenticateAsync","correct":"await LocalAuthentication.authenticateAsync({ /* options */ });"},{"note":"Used to check if the device supports any form of biometric authentication. Should always be checked before attempting authentication.","symbol":"hasHardwareAsync","correct":"await LocalAuthentication.hasHardwareAsync();"}],"quickstart":{"code":"import * as LocalAuthentication from 'expo-local-authentication';\nimport { Alert, Platform } from 'react-native'; // Assuming a React Native environment for Alert\n\nasync function performBiometricAuth() {\n  // 1. Check if biometric hardware is available\n  const hasHardware = await LocalAuthentication.hasHardwareAsync();\n  if (!hasHardware) {\n    Alert.alert('Authentication Error', 'Biometric hardware is not available on this device.');\n    return;\n  }\n\n  // 2. Check if biometrics are enrolled\n  const isEnrolled = await LocalAuthentication.isEnrolledAsync();\n  if (!isEnrolled) {\n    Alert.alert('Authentication Error', 'No biometrics (Face ID/Fingerprint) are enrolled. Please set them up in your device settings.');\n    return;\n  }\n\n  // 3. Attempt to authenticate\n  try {\n    const result = await LocalAuthentication.authenticateAsync({\n      promptMessage: 'Authenticate to access your account',\n      cancelLabel: 'Use Password', // Android specific (optional, customizes button text)\n      fallbackLabel: 'Enter Passcode', // iOS specific for older versions (optional)\n      disableDeviceFallback: false, // iOS specific (optional, disables passcode fallback)\n    });\n\n    if (result.success) {\n      Alert.alert('Success!', 'Biometric authentication successful!');\n      // Proceed with authenticated user flow\n    } else {\n      let errorMessage = 'Authentication failed.';\n      if (result.error === 'user_cancel') {\n        errorMessage = 'Authentication canceled by user.';\n      } else if (result.error === 'system_cancel') {\n        errorMessage = 'Authentication canceled by system (e.g., app in background).';\n      } else if (result.error === 'passcode_not_set') {\n        errorMessage = 'Device passcode not set, biometric authentication is not possible.';\n      } else if (result.error === 'not_enrolled') {\n        errorMessage = 'No biometrics enrolled on the device.';\n      } else if (result.error === 'not_available') {\n        errorMessage = 'Biometric hardware not available or supported.';\n      }\n      Alert.alert('Authentication Failed', `${errorMessage} (Error: ${result.error})`);\n    }\n  } catch (error) {\n    console.error('Biometric authentication unexpected error:', error);\n    Alert.alert('Error', 'An unexpected error occurred during authentication.');\n  }\n}\n\n// To integrate this in an Expo/React Native component:\n// import React from 'react';\n// import { Button, View } from 'react-native';\n\n// const BiometricAuthScreen = () => (\n//   <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>\n//     <Button title=\"Authenticate with Biometrics\" onPress={performBiometricAuth} />\n//   </View>\n// );\n\n// export default BiometricAuthScreen;\n\n// For immediate testing (e.g., in a standalone script or effect hook):\n// (async () => {\n//   if (Platform.OS === 'web') {\n//     console.warn('Local authentication is not available on web.');\n//     return;\n//   }\n//   // await performBiometricAuth();\n// })();","lang":"typescript","description":"This quickstart demonstrates the full biometric authentication flow, including checking for hardware availability and user enrollment before attempting to authenticate the user. It also provides basic error handling for common authentication outcomes."},"warnings":[{"fix":"Add `\"NSFaceIDUsageDescription\": \"Your app needs Face ID to authenticate you.\"` to the `infoPlist` object within `expo.ios` in your `app.json`.","message":"On iOS, if using Face ID, you must add the `NSFaceIDUsageDescription` key to your `app.json` (under `expo.ios.infoPlist`). Without this, the app will crash when attempting to use Face ID on iOS 11 and above.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"Ensure your `app.json` does not override default Android permissions. If building a bare workflow app, manually add `<uses-permission android:name=\"android.permission.USE_BIOMETRIC\"/>` and `<uses-permission android:name=\"android.permission.USE_FINGERPRINT\"/>` to your `AndroidManifest.xml`.","message":"For Android, ensure necessary permissions are declared in your `AndroidManifest.xml` (handled automatically by Expo in `app.json` for common cases). Specifically, `USE_BIOMETRIC` and `USE_FINGERPRINT`.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Replace `await LocalAuthentication.supportedAuthenticationTypesAsync()` with `await LocalAuthentication.getEnrolledLevelAsync()` for a more robust check on enrolled biometric types and security levels.","message":"The `LocalAuthentication.supportedAuthenticationTypesAsync` method has been deprecated. It is recommended to use `LocalAuthentication.getEnrolledLevelAsync` instead to determine the strength of biometric enrollment.","severity":"deprecated","affected_versions":">=13.0.0"},{"fix":"Implement sequential checks: `if (await LocalAuthentication.hasHardwareAsync()) { if (await LocalAuthentication.isEnrolledAsync()) { await LocalAuthentication.authenticateAsync(); } }`","message":"It's crucial to always check `hasHardwareAsync()` and `isEnrolledAsync()` before calling `authenticateAsync()`. Failing to do so can lead to a poor user experience or unexpected errors if the device lacks hardware or the user hasn't set up biometrics.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Add `\"NSFaceIDUsageDescription\": \"Your app needs Face ID to authenticate you.\"` to `expo.ios.infoPlist` in your `app.json`.","cause":"Missing `NSFaceIDUsageDescription` in `app.json`'s `infoPlist` for iOS.","error":"This app has not been granted the necessary permissions for Face ID."},{"fix":"Ensure `LocalAuthentication.hasHardwareAsync()` returns `true` before proceeding. If it returns `false`, gracefully inform the user.","cause":"The device either lacks biometric hardware or the `expo-local-authentication` module cannot access it.","error":"Biometric authentication is not available on this device."},{"fix":"Ensure `LocalAuthentication.isEnrolledAsync()` returns `true` before attempting authentication. If it returns `false`, prompt the user to enroll biometrics in their device settings.","cause":"The device has biometric hardware, but the user has not set up Face ID or Fingerprint authentication in their device settings.","error":"No biometrics enrolled on the device."}],"ecosystem":"npm","meta_description":null}