ScaleRocket/Mobile

Biometrics

Add Face ID and fingerprint authentication to your mobile app with expo-local-authentication.

Biometrics

ScaleRocket Mobile includes expo-local-authentication for biometric authentication — Face ID on iPhone, fingerprint on Android, and Touch ID on older Apple devices.

Check Availability

Before prompting for biometrics, check if the device supports it and if the user has enrolled:

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

async function checkBiometrics() {
  // Check if hardware supports biometrics
  const compatible = await LocalAuthentication.hasHardwareAsync();
  if (!compatible) return false;

  // Check if user has enrolled biometrics (face/fingerprint registered)
  const enrolled = await LocalAuthentication.isEnrolledAsync();
  if (!enrolled) return false;

  return true;
}

You can also check which types of biometrics are available:

const types = await LocalAuthentication.supportedAuthenticationTypesAsync();
// Returns: [1] for fingerprint, [2] for facial recognition, or both

Authenticate the User

Prompt the user for biometric authentication:

async function authenticateWithBiometrics() {
  const result = await LocalAuthentication.authenticateAsync({
    promptMessage: "Authenticate to continue",
    fallbackLabel: "Use passcode",
    cancelLabel: "Cancel",
    disableDeviceFallback: false, // Allow passcode as fallback
  });

  if (result.success) {
    // User authenticated successfully
    return true;
  } else {
    // Authentication failed or was cancelled
    console.log(result.error);
    return false;
  }
}

Re-Authentication Flow

Use biometrics as a secondary gate after session check. For example, require biometrics when the app returns from background:

import { useEffect, useRef } from "react";
import { AppState } from "react-native";

function useBiometricLock() {
  const appState = useRef(AppState.currentState);
  const [isLocked, setIsLocked] = useState(false);

  useEffect(() => {
    const subscription = AppState.addEventListener("change", (nextState) => {
      if (
        appState.current.match(/inactive|background/) &&
        nextState === "active"
      ) {
        setIsLocked(true);
        authenticateWithBiometrics().then((success) => {
          if (success) setIsLocked(false);
        });
      }
      appState.current = nextState;
    });

    return () => subscription.remove();
  }, []);

  return isLocked;
}

Use Cases

ScenarioImplementation
App lockRe-authenticate when app returns from background
Sensitive actionsRequire biometrics before deleting account or changing password
Quick loginOffer biometric login instead of typing credentials
Payment confirmationVerify identity before processing payments

Important Notes

  • Biometrics require a development build (not Expo Go)
  • iOS simulators support simulated Face ID (Features > Face ID in the simulator menu)
  • Android emulators support simulated fingerprint (Extended controls > Fingerprint)
  • Always provide a fallback (passcode) for accessibility
  • On iOS, add NSFaceIDUsageDescription to app.json under ios.infoPlist:
{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSFaceIDUsageDescription": "We use Face ID to secure your account"
      }
    }
  }
}

On this page