How to add Android fingerprint authentication to your app

by: | Jan 16, 2020

The experience of fingerprint authentication is really delightful. Once you have used a phone with a fingerprint scanner, it’s difficult to imagine going back to one without it. Remember how annoying it was to provide your password every time you needed to access your bank’s app? And every time you needed to perform a sensitive operation, like an account transfer? And even rapidly drawing some random lines to unlock your phone while fearing someone getting a glimpse of it?

As an Android app developer and an app user, I know just how big of a difference fingerprint authentication can make for the user experience. In this post, we provide step-by-step instructions for how developers can add Android fingerprint authentication into apps.

A bit of Android fingerprint authentication history

Fingerprint authentication has been officially supported by Android since API 23 (M). At that time, the feature was supported through the FingerprintManager API. Although there was the material.io UI standard for it, there wasn’t a unified authentication dialog — which meant app developers were wildly inconsistent how they supported it.

API 28 added the BiometricPrompt, a unified authentication dialog, and FingerprintManager was marked as deprecated. AndroidX’s BiometricPrompt reached beta in August 2019! ?

All code snippets provided in this guide were made using AndroidX’s BiometricPrompt (at the time of writing this post, the latest version is 1.0.0-beta01), so make sure to add its dependency in your app’s build.gradle:

implementation "androidx.biometric:biometric:$biometric_version"

Step by step: Implementing Android fingerprint authentication

While improving the UX is a great reason to implement fingerprint authentication in an Android app, companies must also take great care to protect features that run behind fingerprint authentication. Accessing them can only occur after the Android system confirms that the individual using the device is recognized and authorized.

Step 1: Check device support

First of all, check if the device supports fingerprint authentication by calling:

BiometricManager.from(context).canAuthenticate()

This method returns an int, which can have one of these values:

public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1;
public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11;
public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12;
public static final int BIOMETRIC_SUCCESS = 0;

Obviously, you should only show the option for biometric authentication if it’s available on the device, that is, if canAuthenticate() returns BIOMETRIC_SUCCESS. Some smartphones that had fingerprint scanners before Android officially supported it will return BIOMETRIC_ERROR_HW_UNAVAILABLE. Generally speaking, those smartphones were released in APIs before 23 and could be updated to 23 (like the Samsung Galaxy Note 4). They can’t be used by the official API because they don’t attend to some specifications given by the Android M Compatibility Definition Document, including this one:

“MUST have a hardware-backed keystore implementation, and perform the fingerprint matching in a Trusted Execution Environment (TEE) or on a chip with a secure channel to the TEE.”

Step 2: Create a BiometricPrompt instance

We need to create an instance of BiometricPrompt that will be responsible to prompt the user for authentication. Its constructor has three parameters:

  • Fragment or FragmentActivity: A reference to the client’s activity
  • Executor: An executor to handle callback events
  • BiometricPrompt.AuthenticationCallback: An object to receive authentication events

The first two arguments are quite direct. For the first argument, use the calling FragmentActivity or Fragment. For the second one, you should use the executor that fits best your needs. Here, for simplicity, we’re going to use the mainExecutor.

The third argument deserves more attention. It will receive the authentication results (such as if there was an error, or if it was canceled or successful) and can give us a CryptoObject associated with the authentication. We’ll come back to the CryptoObject in a later blog post, but for now, here’s a simple example which doesn’t need it:

val biometricPrompt = BiometricPrompt(
  this, // assuming it's being called inside an Activity
  mainExecutor,
  callback
)

For the callback, we can use:

val callback = object : BiometricPrompt.AuthenticationCallback() {

  override fun onAuthenticationSucceeded(
    result: BiometricPrompt.AuthenticationResult
  ) {
    // user authenticated
    // now you can allow him to perform that sensible action
    someSensibleAction()
  }

  override fun onAuthenticationError(
    errorCode: Int,
    errString: CharSequence
  ) {
    // called when an unrecoverable error has been encountered
    // and the operation is complete
    // example: user clicked the negative button or
    // tried too many times and is locked up
  }

  override fun onAuthenticationFailed() {
    // called when biometric was valid but not recognized
    // example: the user tried to authenticate
    // with a finger that isn't registered
  }

}

The dialog displays the correct message (errString) by itself while taking into consideration the user’s Locale, so it’s pretty much automatic. You might need to redirect the user to some other flow in case of an error. The possible error codes are listed here. It’s worth noting that when the user presses the negative button, it’ll result in onAuthenticationError with ERROR_NEGATIVE_BUTTON.

Step 3: Build the authentication dialog

The dialog is built using the BiometricPrompt.PromptInfo.Builder. It follows the commonly seen builder pattern and can be as simple and direct as:

val promptInfo = BiometricPrompt.PromptInfo.Builder()
  .setTitle("Title")
  .setNegativeButtonText("Negative Button Text")
  .build()

But it also provides some optional methods for displaying a description and assistive text, such as allowing the device’s credential authentication (PIN, pattern or password). Those methods are described here in the documentation. Notice that if you enable the use of the device’s credentials to authenticate, you can’t set a negative button.

Step 4: Authenticate

Now, with two objects at hand, we can call biometricPrompt.authenticate(promptInfo) and perform the actual authentication. This will display the authentication dialog and send its events to the callback specified. Here is a simple example of how it might look:

 

That is a simple way to implement fingerprint authentication in Android.


Need help with your Android app project?

ArcTouch’s Android developers have been building lovable apps for more than a decade. If you need help with your Android project or some advice on how to support the latest features in Android 10, contact us to set up a free consultation.