React Native Biometrics
There is something about having biometrics in your app that I find really cool. It definitely helps with making your app more secure, as it forces the user to give permission before they take a certain action. This is the way to go.
Security
Let us first talk about which React Native library to use. There are several to choose from: react-native-touchid is not currently maintained and they recommend expo-local-authentication (expo has instructions on how to add to bare React Native app), react-native-biometrics, and react-native-keychain. For the purpose of this tutorial, I used react-native-keychain. I made this decision after reading this blog by Kathy Dinh. They write that using keychain is the most secure while when using local authentication there is a possibility of getting hacked.
Installation
To install, first runyarn add react-native-keychain
and thencd ios && pod install
In Xcode go to your info.plist (could be found in the folder with your project name). Next to Information Property List (it’s the dictionary) is a little plus sign. Press it and add Privacy — Face ID Usage Description (as a string) and for the value add Enabling Face ID allows you quick and secure access to your account.
Usage
Get Biometry Type
Before asking to implement biometrics first check to see if they have it set up or not. For the purpose of this blog the example I am using is a Switch component that ‘toggles’ on when they choose to add biometrics. If the device doesn’t have biometrics or doesn’t have biometrics set up the Switch is disabled.
import React, {useState, useEffect} from 'react'
import {View, Text, StyleSheet} from 'react-native'
import { Switch, } from 'react-native-gesture-handler'import * as Keychain from 'react-native-keychain';const App = () => {
const [isEnabled, setIsEnabled] = useState(false);
const [disabled, setDisabled] = useState(false)
useEffect(() => {
Keychain.getSupportedBiometryType()
.then(biometryType => {
if (!!biometryType) setDisabled(false)
else setDisabled(true)
})
}, []) // toggles the switch if they want to use biometrics
const toggleSwitch = () => {
setIsEnabled(!isEnabled)
} return (
<View>
<Text>Allow Biometrics</Text>
<Switch value={isEnabled} onValueChange={toggleSwitch} disabled={disabled} />
</View>
)
}
getSupportedBiometryType
Gets what type of hardware biometry support the device has. Resolves to a Keychain.BIOMETRY_TYPE
value when supported, otherwise null
. (This will return null
if nothing is enrolled even if the phone supports it).
Set Generic Password
Next, we need to set the information you want into the keychain.
Keychain.setGenericPassword('passcode', passcode, {
accessControl: 'BiometryCurrentSetOrDevicePasscode',
accessible: 'WhenPasscodeSetThisDeviceOnly',
authenticationType: 'DevicePasscodeOrBiometrics'})
.then(result => {
console.log(result)
})
The format is setGenericPassword(username, password, [{ accessControl, accessible, accessGroup, service, securityLevel }])
.
Something to keep in mind if you are looking at the docs (or what I write below) is that the value works only in Camel Case.
accessControl
dictates how a keychain item may be used. Possible values are:
1. USER_PRESENCE
Constraint to access an item with either Touch ID or passcode.
2. BIOMETRY_ANY
Constraint to access an item with Touch ID for any enrolled fingers.
3. BIOMETRY_CURRENT_SET
Constraint to access an item with Touch ID for currently enrolled fingers.
4. DEVICE_PASSCODE
Constraint to access an item with a passcode.
5. APPLICATION_PASSWORD
Constraint to use an application-provided password for data encryption key generation.
6. BIOMETRY_ANY_OR_DEVICE_PASSCODE
Constraint to access an item with Touch ID for any enrolled fingers or passcode.
7. BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE
Constraint to access an item with Touch ID for currently enrolled fingers or passcode.
accessible
dictates when a keychain item is accessible (ios only). Possible values are:
1. WHEN_UNLOCKED
The data in the keychain item can be accessed only while the device is unlocked by the user.
2. AFTER_FIRST_UNLOCK
The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user.
3. ALWAYS
The data in the keychain item can always be accessed regardless of whether the device is locked.
4. WHEN_PASSCODE_SET_THIS_DEVICE_ONLY
The data in the keychain can only be accessed when the device is unlocked. This is only available if a passcode is set on the device. Items with this attribute never migrate to a new device.
5. WHEN_UNLOCKED_THIS_DEVICE_ONLY
The data in the keychain item can be accessed only while the device is unlocked by the user. Items with this attribute do not migrate to a new device.
6. AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY
The data in the keychain item cannot be accessed after a restart until the device has been unlocked once by the user. Items with this attribute never migrate to a new device.
7. ALWAYS_THIS_DEVICE_ONLY
The data in the keychain item can always be accessed regardless of whether the device is locked. Items with this attribute never migrate to a new device.
authenticationType
Policies specifying which forms of authentication are acceptable (ios only). Possible values are:
1. DEVICE_PASSCODE_OR_BIOMETRICS
Device owner is going to be authenticated by biometry or device passcode.
2. BIOMETRICS
Device owner is going to be authenticated using a biometric method (Touch ID or Face ID).
For other options refer to the docs.
Get Generic Password
Now to retrieve what you set in the keychain.
Keychain.getGenericPassword()
.then(result => {
console.log(result)
}
.catch(async (error) => {
if ((await Keychain.getSupportedBiometryType()) === null){
// After 5 failed attempts
// biometrics authentication is disabled system-wide.
// Keychain.getsupportedBioemtryType() will return null
}
if (error.message === 'The user name or passphrase you entered is not correct.'){
// Invalid biometrics credentials after 3 attempts
}
if (error.message === 'User canceled the operation.'){
// Authentication was cancelled
}
})
It is that easy. getGenericPassword([{ authenticationPrompt, service, accessControl }])
Will retrieve the username/password combination from the secure storage.
I hope this helps you out when trying to implement biometrics in your React Native app. Happy coding!