Biometric Authentication in React Native With Expo
Tags: reactnative, expo, authentication
Published: 2021-11-26 20:05:47

---
title: Biometric Authentication in React Native With Expo
published: true
date: 2021-11-26 00:00:00 UTC
tags: reactnative, expo, authentication
description: How to authenticate with facial recognition, fingerprint or iris recognition
canonical_url: https://bionicjulia.com/blog/biometric-authentication-react-native-expo
---

Here's a quick blog post on how to set up biometric authentication in your React Native + Expo app. Expo makes things super easy, so it's all pretty self explanatory really, but I wanted to write this blog post just to note down my process for someone using a "bare workflow".

### Step 1: Install expo-local-authentication.

Check out the instructions [here](https://docs.expo.dev/versions/latest/sdk/local-authentication/), but you basically need to run `expo install expo-local-authentication`. Depending on the Expo SDK / React Native version you're on, you might need to install the `react-native-unimodules` package as well.

If you've ejected from Expo, you'll need to explicitly state that you'll be using iOS's FaceID feature, along with the reason for using it. Add these lines (amended for your use case) to your `Info.plist` file. Android permissions should be set automatically.

```jsx
<key>NSFaceIDUsageDescription</key>
<string>This app uses Face ID to authenticate the user for blah blah blah reasons.</string>
```

### Step 2: Create your authentication screen

The `expo-local-authentication` package comes with some nice methods out of the box. The main ones I used were:

- `supportedAuthenticationTypesAsync` which determine what kinds of authentications are available on the device
- `authenticateAsync` which attempts to authenticate via Fingerprint/TouchID (or FaceID if available on the device).

The logical steps were therefore to:

- Call `supportedAuthenticationTypesAsync` upon the component screen mounting, to check what types of biometric authentication are available. This function returns an array of types, so I then looped through the results array and set the local state to either `true` or `false`, for `facialRecognitionAvailable`, `fingerprintAvailable` and `irisAvailable`.
- If at least one biometric authentication method is available, show a button, which on press, calls a function called `authenticate`. The function basically tries to authenticate the user using `authenticateAsync`. The return value from this function is either "success" or "error", with the latter providing an error message. This can therefore be used to set and display the `result` variable for feedback to the user.

### Side notes

- It's not possible to define a specific biometric authentication method for the `authenticateAsync` function.
- If neither of the biometric authentication methods work, the function uses fallback methods like device pin code / password.
- If the error message comes back as unknown, a likely cause is that the user has tried authenticating too many times.
- The use of the enum `EResult` here is entirely optional, but just organises the code a little more nicely. 🙂

```jsx
import * as LocalAuthentication from 'expo-local-authentication';
import * as React from 'react';
// import your other components

enum EResult {
  CANCELLED = 'CANCELLED',
  DISABLED = 'DISABLED',
  ERROR = 'ERROR',
  SUCCESS = 'SUCCESS',
}

export function BiometricAuthScreen() {
  const [facialRecognitionAvailable, setFacialRecognitionAvailable] = React.useState(false);
  const [fingerprintAvailable, setFingerprintAvailable] = React.useState(false);
  const [irisAvailable, setIrisAvailable] = React.useState(false);
  const [loading, setLoading] = React.useState(false);
  const [result, setResult] = React.useState<EResult>();

  const checkSupportedAuthentication = async () => {
    const types = await LocalAuthentication.supportedAuthenticationTypesAsync();
    if (types && types.length) {
      setFacialRecognitionAvailable(types.includes(LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION));
      setFingerprintAvailable(types.includes(LocalAuthentication.AuthenticationType.FINGERPRINT));
      setIrisAvailable(types.includes(LocalAuthentication.AuthenticationType.IRIS));
    }
  };

  const authenticate = async () => {
    if (loading) {
      return;
    }

    setLoading(true);

    try {
      const results = await LocalAuthentication.authenticateAsync();

      if (results.success) {
        setResult(EResult.SUCCESS);
      } else if (results.error === 'unknown') {
        setResult(EResult.DISABLED);
      } else if (
        results.error === 'user_cancel' ||
        results.error === 'system_cancel' ||
        results.error === 'app_cancel'
      ) {
        setResult(EResult.CANCELLED);
      }
    } catch (error) {
      setResult(EResult.ERROR);
    }

    setLoading(false);
  };

  React.useEffect(() => {
    checkSupportedAuthentication();
  }, []);

  let resultMessage;
  switch (result) {
    case EResult.CANCELLED:
      resultMessage = 'Authentication process has been cancelled';
      break;
    case EResult.DISABLED:
      resultMessage = 'Biometric authentication has been disabled';
      break;
    case EResult.ERROR:
      resultMessage = 'There was an error in authentication';
      break;
    case EResult.SUCCESS:
      resultMessage = 'Successfully authenticated';
      break;
    default:
      resultMessage = '';
      break;
  }

  let description;
  if (facialRecognitionAvailable && fingerprintAvailable && irisAvailable) {
    description = 'Authenticate with Face ID, touch ID or iris ID';
  } else if (facialRecognitionAvailable && fingerprintAvailable) {
    description = 'Authenticate with Face ID or touch ID';
  } else if (facialRecognitionAvailable && irisAvailable) {
    description = 'Authenticate with Face ID or iris ID';
  } else if (fingerprintAvailable && irisAvailable) {
    description = 'Authenticate with touch ID or iris ID';
  } else if (facialRecognitionAvailable) {
    description = 'Authenticate with Face ID';
  } else if (fingerprintAvailable) {
    description = 'Authenticate with touch ID ';
  } else if (irisAvailable) {
    description = 'Authenticate with iris ID';
  } else {
    description = 'No biometric authentication methods available';
  }

  return (
    <Screen>
      <Text>
        {description}
      </Text>
      {facialRecognitionAvailable || fingerprintAvailable || irisAvailable ? (
        <Button onPress={authenticate}>
          Authenticate
        </Button>
      ) : null}
      {resultMessage ? <Text>{resultMessage}</Text> : null}
    </Screen>
  );
}
```

Like this post? Connect with me on https://bionicjulia.com, [Twitter](https://twitter.com/bionicjulia) or [Instagram](https://instagram.com/bionicjulia).