Implementing Biometric Login in React Native: A Comprehensive Guide
Biometric authentication has become an essential feature for mobile applications, providing users with a convenient and secure way to access their accounts.
Biometric login offers a seamless and secure user authentication experience, allowing users to access their accounts with fingerprint, face recognition, or device credentials like PIN or pattern. In this blog post, we’ll walk through implementing biometric login in a React Native application using native code and bridging it with React Native application using Android’s BiometricPrompt API.
Why Biometric Authentication?
In today’s digital landscape, security and user experience are paramount. Biometric authentication offers:
Quick and seamless login experience
Enhanced security compared to traditional password methods
Support for multiple authentication types (fingerprint, face recognition, device credentials)
What You’ll Learn
- How to check biometric authentication availability on the device.
- How to implement biometric authentication with fallback to device credentials.
- How to bridge native code with React Native.
- How to use the functionality in your React Native app.
Step 1: Permissions Required for Biometric Authentication
To implement biometric authentication in your React Native app, you need to declare specific permissions in the Android AndroidManifest.xml file. These permissions ensure your app can access and use the device’s biometric features, such as fingerprint or face recognition.
Add the following permissions to your AndroidManifest.xml file:
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
Step 2: Checking Biometric Authentication Availability
First, we need to verify whether the device supports biometric authentication or device credentials.
Native Code (Android)
Here’s the native code to check biometric authentication availability using BiometricManager:
@ReactMethod
public void checkBiometricAuthAvailable(Promise promise) {
BiometricManager biometricManager = BiometricManager.from(getReactApplicationContext());
int canAuthenticateWithBiometric = biometricManager.canAuthenticate(
BiometricManager.Authenticators.BIOMETRIC_STRONG |
BiometricManager.Authenticators.BIOMETRIC_WEAK
);
int canAuthenticateWithCredential = biometricManager.canAuthenticate(
BiometricManager.Authenticators.DEVICE_CREDENTIAL
);
boolean isAuthAvailable = (canAuthenticateWithBiometric == BiometricManager.BIOMETRIC_SUCCESS) ||
(canAuthenticateWithCredential == BiometricManager.BIOMETRIC_SUCCESS);
promise.resolve(isAuthAvailable);
}
This method checks if biometric or device credential authentication is supported and returns a boolean value.
Step 3: Implementing Biometric Authentication
Next, we create a method to authenticate users using biometrics. If biometrics aren’t available, we fallback to device credentials (PIN, pattern, etc.).
Native Code (Android)
@ReactMethod
public void authenticateWithBiometric(Promise promise) {
FragmentActivity activity = (FragmentActivity) getCurrentActivity();
if (activity == null) {
promise.reject("NO_ACTIVITY", "No activity found");
return;
}
BiometricManager biometricManager = BiometricManager.from(activity);
int canAuthenticateWithBiometric = biometricManager.canAuthenticate(
BiometricManager.Authenticators.BIOMETRIC_WEAK
);
int canAuthenticateWithDeviceCredential = biometricManager.canAuthenticate(
BiometricManager.Authenticators.DEVICE_CREDENTIAL
);
if (canAuthenticateWithBiometric != BiometricManager.BIOMETRIC_SUCCESS &&
canAuthenticateWithDeviceCredential != BiometricManager.BIOMETRIC_SUCCESS) {
promise.reject("AUTH_NOT_AVAILABLE", "No authentication methods available");
return;
}
executor = ContextCompat.getMainExecutor(activity);
final int[] attemptCounter = {0};
biometricPrompt = new BiometricPrompt(activity, executor, new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
promise.reject("AUTH_ERROR", errString.toString());
}
@Override
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
promise.resolve("AUTH_SUCCESS");
}
@Override
public void onAuthenticationFailed() {
attemptCounter[0]++;
if (attemptCounter[0] >= 3) {
promise.reject("AUTH_FAILED", "Authentication failed after 3 attempts");
biometricPrompt.cancelAuthentication();
}
}
});
int allowedAuthenticators = (canAuthenticateWithBiometric == BiometricManager.BIOMETRIC_SUCCESS) ?
BiometricManager.Authenticators.BIOMETRIC_WEAK | BiometricManager.Authenticators.DEVICE_CREDENTIAL :
BiometricManager.Authenticators.DEVICE_CREDENTIAL;
try {
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle("Unlock to login")
.setSubtitle("Just one glance or touch, and you're in!")
.setAllowedAuthenticators(allowedAuthenticators)
.build();
activity.runOnUiThread(() -> biometricPrompt.authenticate(promptInfo));
} catch (Exception e) {
promise.reject("AUTH_ERROR", "Error building prompt: " + e.getMessage());
}
}
This method:
Displays the biometric prompt to the user.
Authenticates the user with biometrics or device credentials.
Handles success, error, and failed attempts.
Step 4: Bridging Native Code with React Native
We need to expose the native methods to React Native using a custom native module.
Native Code: NativeBridge
public class NativeBridgePackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new NativeBridge(reactContext));
return modules;
}
}
Register the package in MainApplication.java:
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new NativeBridgePackage());
return packages;
}
Step 5: React Native Integration
In your React Native app, create utility functions to call the native methods:
import { NativeModules } from 'react-native';
export const checkBiometricAuthAvailability = async () => {
try {
const isAvailable = await NativeModules.NativeBridge.checkBiometricAuthAvailable();
return isAvailable;
} catch (error) {
return false;
}
};
export const authenticateWithBiometric = async () => {
try {
const result = await NativeModules.NativeBridge.authenticateWithBiometric();
return result === 'AUTH_SUCCESS';
} catch (error) {
console.log('Authentication Error:', error);
return false;
}
};
Use these methods to:
Check if biometric authentication is available.
Authenticate users when they press the login button.
Next Steps
Implement credential storage with proper encryption
Add support for iOS biometric authentication
Create comprehensive error handling and user feedback mechanisms
Happy coding! 🚀🔐
References:
- For a deeper dive into the APIs used, check out the official Android BiometricManager documentation.
Conclusion
With the implementation above, you’ve added biometric authentication to your React Native app, providing users with a secure and user-friendly login experience. This guide can serve as a template for enhancing the security features of your app.
Let us know your thoughts or share your challenges in the comments below! 🚀