React Native Fingerprint Scanner
React Native Fingerprint Scanner is a React Native library for authenticating users with Fingerprint (TouchID).
Table of Contents
iOS Version
The usage of the TouchID is based on a framework, named Local Authentication.
It provides a Default View that prompts the user to place a finger to the iPhone’s button for scanning.
Android Version
4.0.0 Prefers the new native Android BiometricPrompt lib on any Android >= v23 (M) 4.0.0 also DEPRECATES support for the legacy library that provides support for Samsung & MeiZu phones
3.0.2 and below: Using an expandable Android Fingerprint API library, which combines Samsung and MeiZu's official Fingerprint API.
Samsung and MeiZu's Fingerprint SDK supports most devices which system versions less than Android 6.0.
Installation
$ npm install react-native-fingerprint-scanner --save
or
$ yarn add react-native-fingerprint-scanner
Automatic Configuration
For RN >= 0.60
$ cd ios && pod install
For RN < 0.60, use react-native link to add the library to your project:
$ react-native link react-native-fingerprint-scanner
Manual Configuration
iOS
- In XCode, in the project navigator, right click
Libraries
➜Add Files to [your project's name]
- Go to
node_modules
➜react-native-fingerprint-scanner
and addReactNativeFingerprintScanner.xcodeproj
- In XCode, in the project navigator, select your project. Add
libReactNativeFingerprintScanner.a
to your project'sBuild Phases
➜Link Binary With Libraries
- Run your project (
Cmd+R
)
Android
- Open up
android/app/src/main/java/[...]/MainApplication.java
- Add
import com.hieuvp.fingerprint.ReactNativeFingerprintScannerPackage;
to the imports at the top of the file - Add
new ReactNativeFingerprintScannerPackage()
to the list returned by thegetPackages()
method
- Append the following lines to
android/settings.gradle
:include ':react-native-fingerprint-scanner' project(':react-native-fingerprint-scanner').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fingerprint-scanner/android')
- Insert the following lines inside the dependencies block in
android/app/build.gradle
:implementation project(':react-native-fingerprint-scanner')
App Permissions
Add the following permissions to their respective files:
Android
In your AndroidManifest.xml
:
API level 28+ (Uses Android native BiometricPrompt) (Reference)
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
API level 23-28 (Uses Android native FingerprintCompat) Reference)
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
// DEPRECATED in 4.0.0 API level <23 (Uses device-specific native fingerprinting, if available - Samsung & MeiZu only) Reference)
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
iOS
In your Info.plist
:
<key>NSFaceIDUsageDescription</key>
<string>$(PRODUCT_NAME) requires FaceID access to allows you quick and secure access.</string>
Extra Configuration
-
Make sure the following versions are all correct in
android/app/build.gradle
// API v29 enables FaceId android { compileSdkVersion 29 buildToolsVersion "29.0.2" ... defaultConfig { targetSdkVersion 29
-
Add necessary rules to
android/app/proguard-rules.pro
if you are using proguard:# MeiZu Fingerprint // DEPRECATED in 4.0.0 -keep class com.fingerprints.service.** { *; } -dontwarn com.fingerprints.service.** # Samsung Fingerprint // DEPRECATED in 4.0.0 -keep class com.samsung.android.sdk.** { *; } -dontwarn com.samsung.android.sdk.**
Compatibility
- For Gradle < 3 you MUST install react-native-fingerprint-scanner at version <= 2.5.0
- For RN >= 0.57 and/or Gradle >= 3 you MUST install react-native-fingerprint-scanner at version >= 2.6.0
- For RN >= 0.60 you MUST install react-native-fingerprint-scanner at version >= 3.0.0
- For Android native Face Unlock, MUST use >= 4.0.0
Example
iOS Implementation
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { AlertIOS } from 'react-native';
import FingerprintScanner from 'react-native-fingerprint-scanner';
class FingerprintPopup extends Component {
componentDidMount() {
this._iosTouchID()
}
_iosTouchID = () => {
FingerprintScanner
.authenticate({ description: 'Scan your fingerprint on the device scanner to continue' })
.then(() => {
AlertIOS.alert('Authenticated successfully');
})
.catch((error) => {
switch (error.biometric) {
case 'UserCancel':
AlertIOS.alert('The user clicks the cancel button')
break
case 'AuthenticationFailed':
AlertIOS.alert('User failed to identify 3 times')
break
case 'AuthenticationLockout':
// console.log('Accumulated 5 identification failures, fingerprint identification was locked')
AlertIOS.alert('Identify cumulative multiple failures, temporarily unavailable', [
{
text: 'cancel',
style: 'default',
onPress: () => {
}
}, {
text: 'To unlock',
style: 'default',
onPress: () => {
this._iosAuthenticateDevice()
}
}
])
break
default:
break
}
AlertIOS.alert(error.message);
});
}
_iosAuthenticateDevice = () => {
FingerprintScanner.authenticateDevice().then(() => {
// console.log('Device unlocked')
this._iosTouchID()
}).catch((error) => {
// error.biometric
AlertIOS.alert('catch error:', error.message)
})
}
render() {
return false;
}
}
export default FingerprintPopup;
Android Implementation
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
Alert,
Image,
Text,
TouchableOpacity,
View,
ViewPropTypes,
Platform,
} from 'react-native';
import FingerprintScanner from 'react-native-fingerprint-scanner';
import styles from './FingerprintPopup.component.styles';
import ShakingText from './ShakingText.component';
// - this example component supports both the
// legacy device-specific (Android < v23) and
// current (Android >= 23) biometric APIs
// - your lib and implementation may not need both
class BiometricPopup extends Component {
constructor(props) {
super(props);
this.state = {
errorMessageLegacy: undefined,
biometricLegacy: undefined
};
this.description = null;
}
componentDidMount() {
this._androidTouchID();
}
componentWillUnmount = () => {
FingerprintScanner.release();
}
requiresLegacyAuthentication() {
return Platform.Version < 23;
}
_androidTouchID() {
FingerprintScanner.release()
FingerprintScanner
.authenticate({ title: 'Log in with Biometrics' })
.then(() => {
this.props.onAuthenticate();
})
.catch(error => {
FingerprintScanner.release()
switch (error.biometric) {
case 'UserCancel':
AlertIOS.alert('Click the cancel button')
break
case 'DeviceLocked':
AlertIOS.alert('Accumulated 5 identification failures, fingerprint identification was locked')
break
case 'DeviceLockedPermanent':
AlertIOS.alert('Accumulates many times to recognize the failure, is locked permanently, needs to unlock')
break
default:
break
}
});
}
handleAuthenticationAttemptedLegacy = (error) => {
this.setState({ errorMessageLegacy: error.message });
this.description.shake();
};
renderLegacy() {
const { errorMessageLegacy, biometricLegacy } = this.state;
const { style, handlePopupDismissedLegacy } = this.props;
return (
<View style={styles.container}>
<View style={[styles.contentContainer, style]}>
<Image
style={styles.logo}
source={require('./assets/finger_print.png')}
/>
<Text style={styles.heading}>
Biometric{'\n'}Authentication
</Text>
<ShakingText
ref={(instance) => { this.description = instance; }}
style={styles.description(!!errorMessageLegacy)}>
{errorMessageLegacy || `Scan your ${biometricLegacy} on the\ndevice scanner to continue`}
</ShakingText>
<TouchableOpacity
style={styles.buttonContainer}
onPress={handlePopupDismissedLegacy}
>
<Text style={styles.buttonText}>
BACK TO MAIN
</Text>
</TouchableOpacity>
</View>
</View>
);
}
render = () => {
if (this.requiresLegacyAuthentication()) {
return this.renderLegacy();
}
// current API UI provided by native BiometricPrompt
return null;
}
}
BiometricPopup.propTypes = {
onAuthenticate: PropTypes.func.isRequired,
handlePopupDismissedLegacy: PropTypes.func,
style: ViewPropTypes.style,
};
export default BiometricPopup;
API
isSensorAvailable()
: (Android, iOS)
Checks if Fingerprint Scanner is able to be used by now.
- Returns a
Promise<string>
biometryType: String
- The type of biometric authentication supported by the device.- iOS: biometryType = 'Touch ID', 'Face ID'
- Android: biometryType = 'Biometrics'
error: FingerprintScannerError { name, message, biometric }
- The name and message of failure and the biometric type in use.
componentDidMount() {
FingerprintScanner
.isSensorAvailable()
.then(biometryType => this.setState({ biometryType }))
.catch(error => this.setState({ errorMessage: error.message }));
}
authenticateDevice()
: (iOS)
Unlock with the device password.
- Returns a
Promise<string>
error: FingerprintScannerError { name, message, biometric }
- The name and message of failure and the biometric type in use.
FingerprintScanner
.authenticateDevice()
.then(() => {
// AlertIOS.alert('Device unlocked')
this._iosTouchID()
}).catch((error) => {
// AlertIOS.alert('catch error:', error.message, error.biometric)
})
authenticate({ description, fallbackEnabled })
: (iOS)
Starts Fingerprint authentication on iOS.
- Returns a
Promise
description: String
- the string to explain the request for user authentication.fallbackEnabled: Boolean
- default totrue
, whether to display fallback button (e.g. Enter Password).
componentDidMount() {
FingerprintScanner
.authenticate({ description: 'Scan your fingerprint on the device scanner to continue' })
.then(() => {
this.props.handlePopupDismissed();
AlertIOS.alert('Authenticated successfully');
})
.catch((error) => {
this.props.handlePopupDismissed();
AlertIOS.alert(error.message);
});
}
authenticate({ title="Log In", subTitle, description, cancelButton="Cancel", onAttempt=() => (null) })
: (Android)
Starts Fingerprint authentication on Android.
- Returns a
Promise
title: String
the title text to display in the native Android popupsubTitle: String
the sub title text to display in the native Android popupdescription: String
the description text to display in the native Android popupcancelButton: String
the cancel button text to display in the native Android popuponAttempt: Function
- a callback function when users are trying to scan their fingerprint but failed.
componentDidMount() {
if (requiresLegacyAuthentication()) {
authLegacy();
} else {
authCurrent();
}
}
componentWillUnmount = () => {
FingerprintScanner.release();
}
requiresLegacyAuthentication() {
return Platform.Version < 23;
}
authCurrent() {
FingerprintScanner
.authenticate({ title: 'Log in with Biometrics' })
.then(() => {
this.props.onAuthenticate();
});
}
authLegacy() {
FingerprintScanner
.authenticate({ onAttempt: this.handleAuthenticationAttemptedLegacy })
.then(() => {
this.props.handlePopupDismissedLegacy();
Alert.alert('Fingerprint Authentication', 'Authenticated successfully');
})
.catch((error) => {
this.setState({ errorMessageLegacy: error.message, biometricLegacy: error.biometric });
this.description.shake();
});
}
handleAuthenticationAttemptedLegacy = (error) => {
this.setState({ errorMessageLegacy: error.message });
this.description.shake();
};
release()
: (Android)
Stops fingerprint scanner listener, releases cache of internal state in native code, and cancels native prompt if visible.
- Returns a
Void
componentWillUnmount() {
FingerprintScanner.release();
}
Types of Biometrics
Value | OS | Description |
---|---|---|
Touch ID | iOS | |
Face ID | iOS | |
Biometrics | Android | Refers to the biometric set as preferred on the device |
Errors
Name | Message |
---|---|
AuthenticationLockout | Authentication lockout |
AuthenticationNotMatch | No match |
AuthenticationFailed | Authentication was not successful because the user failed to provide valid credentials |
AuthenticationTimeout | Authentication was not successful because the operation timed out |
AuthenticationProcessFailed | 'Sensor was unable to process the image. Please try again |
UserCancel | Authentication was canceled by the user - e.g. the user tapped Cancel in the dialog |
UserFallback | Authentication was canceled because the user tapped the fallback button (Enter Password) |
SystemCancel | Authentication was canceled by system - e.g. if another application came to foreground while the authentication dialog was up |
PasscodeNotSet | Authentication could not start because the passcode is not set on the device |
DeviceLocked | Authentication was not successful, the device currently in a lockout of 30 seconds |
DeviceLockedPermanent | Authentication was not successful, device must be unlocked via password |
DeviceOutOfMemory | Authentication could not proceed because there is not enough free memory on the device |
HardwareError | A hardware error occurred |
UserDeviceCancel | Authentication Device was canceled |
FingerprintScannerUnknownError | Could not authenticate for an unknown reason |
FingerprintScannerNotSupported | Device does not support Fingerprint Scanner |
FingerprintScannerNotEnrolled | Authentication could not start because Fingerprint Scanner has no enrolled fingers |
FingerprintScannerNotAvailable | Authentication could not start because Fingerprint Scanner is not available on the device |
License
MIT