Migrating from FingerprintManager to BiometricPrompt

Isai Damier
Android Developers
Published in
5 min readNov 26, 2019

--

The Android Framework and Security team recently released the AndroidX Biometric Library, a support library that supersedes all previous iterations of the API. The library makes all the features announced in Android 10 (API level 29) available all the way back to Android 6 (API level 23). The support library features these key advantages:

  • Developers no longer need to anticipate different API levels in their code because the library handles all API matching under the hood. For instance, the support library seamlessly uses FingerprintManager on API levels 23 to 27 and BiometricPrompt on API level 28 and above.
  • Developers no longer need to create their own authentication UI. The library provides a standard and familiar UI that matches a user’s biometric authentication form factor, such as fingerprint or face authentication.
  • Developers can check if a device supports biometric authentication with a single method call.

The number of conveniences that the library provides is great, and you can access all of them through the BiometricPrompt and BiometricManager classes!

If your app is still not using the AndroidX Biometric Library, this post will show you how to migrate.

Improving authentication security

According to the Android Compatibility Definition Document (Android CDD), biometric sensors can be classified as Strong or Weak depending on factors like the sensor’s spoof and imposter acceptance rate. What this means is that your code doesn’t need to determine how strong the biometric authentication is — the OEM implementation does that for you.

Therefore, to improve authentication security, it is common for apps to specify a CryptoObject when using biometric authentication. That’s because CryptoObjects work with KeyStore to provide an additional layer of security. For instance, a banking app may require a CryptoObject for decrypting sensitive data. Using a CryptoObject will guarantee the use of Strong biometric for authenticating into your app. Furthermore, using a CryptoObject provides additional peace of mind because it integrates with KeyStore, which provides resilience against attacks on compromised systems, such as rooted devices.

Hence an app has two possible implementations:

  1. Biometric authentication with a CryptoObject (recommended).
  2. Biometric authentication without a CryptoObject.

Migrating to BiometricPrompt with cryptography

Typically an app must create the following objects to support a FingerprintManager workflow:

  • A login Button, which the user will click to launch the fingerprint authentication UI.
  • A DialogFragment and its associated layout file. This represents your custom fingerprint UI.
  • Optional logic to allow a user to authenticate with an account password instead of FingerprintManager. You may provide this option for different reasons: maybe fingerprint authentication is not supported on the device, maybe the user forgot to setup fingerprint authentication, or maybe the user just prefers account password authentication.

To migrate from FingerprintManager to BiometricPrompt, complete the following steps:

1. Delete custom fingerprint UI

Right off the bat, you can throw away your fingerprint UI’s FragmentDialog and layout file. You won’t need them. The AndroidX Biometric Library comes with a standardized UI.

2. Add the Gradle dependency to your app module

At the time of this writing, the latest release of the library is 1.0.0.

3. Create an instance of BiometricPrompt

You should instantiate BiometricPrompt early in your Activity/Fragment lifecycle, preferably inside onCreate() or onCreateView(). This is a key difference between FingerprintManager and BiometricPrompt. It was OK to instantiate FingerprintManager just in time to call authenticate(). But for BiometricPrompt to send callbacks to the proper activity, say in case of configuration changes, you must instantiate it a bit before you need to call authenticate().

4. Build a PromptInfo object

BiometricPrompt.PromptInfo is a required parameter for authenticating with the BiometricPrompt API. It supplies important instructions to the prompt, such as whether explicit user confirmation is required (note: explicit confirmation is the default behavior, and should always be applied if the API is being used for payments or transactions — for use with app or account sign-in, the explicit confirmation can be set to false to enable a more streamlined experience). BiometricPrompt.PromptInfo also allows your app to provide additional context for the ongoing transaction by setting a title, subtitle, and other descriptive text to appear on the prompt.

Your BiometricPrompt.PromptInfo might look like this:

Recall this snippet from createBiometricPrompt() that shows the user the loginWithPassword() alternative:

5. Create a CryptoObject

Since you are migrating from a FingerprintManager implementation that includes a CryptoObject, then at some point in your old code, you were creating an instance of FingerprintManager.CryptoObject() to pass to fingerprintManager.authenticate(crypto, cancel, flags, callback, handler). When migrating to BiometricPrompt, you can still use the same parameters for creating your CryptoObject, except your CryptoObject is now a BiometricPrompt.CryptoObject.

6. Set up your View.OnClickListener

Now that you have an instance of BiometricPrompt and have built a PromptInfo object, you are ready to ask the user to authenticate. Commonly, when the user clicks on the button, one of two things can happen:

  • If the device supports biometric authentication, then the user sees the Biometric Library’s standard UI.
  • Otherwise, the user gets the custom “login with account password” UI. Also, even after the user gets the standard Biometric Library UI, they still have an opportunity to log in with an app-specific account password by clicking on the negative button in the UI.

Unlike with the FingerprintManager API, you can check whether a device supports biometric authentication with a single method call: BiometricManager.from(context).canAuthenticate(). This singular method call checks whether there is biometric hardware available on the device, whether the user has enrolled templates, or whether the user has enabled biometric authentication. If all three are not true, then the biometric prompt cannot be shown.

And that’s it for migration with cryptography. Once authentication is successful, the API will invoke the onAuthenticationSucceeded() callback method, and your app can proceed with showing private data!

Migrating to BiometricPrompt without cryptography

In the case where you are migrating from a FingerprintManager implementation that does not include a CryptoObject, then all you have to do is skip Step 5 above where we talked about creating a CryptoObject. And similar to how you were calling fingerprintManager.authenticate() with a null CryptoObject, just call biometricPrompt.authenticate() without a CryptoObject parameter.

This example uses the setNegativeButtonText(), which allows you to specify the label for a button that cancels biometric authentication and dismisses the prompt when pressed. When this happens, onAuthenticationError(errCode, errString) receives the error code ERROR_NEGATIVE_BUTTON, allowing your app to provide custom behavior when the button is pressed. One possible use, for example, is to label the button as “Use account password” and have it display an alternative login option in-app when pressed.

The alternative method is setDeviceCredentialAllowed(true), where users have the option to authenticate using their device password, PIN, or pattern instead of biometric credentials. The Biometric library will handle the authentication if setDeviceCredentialAllowed(true) is used. Note that setDeviceCredentialAllowed(true) is not compatible with CryptoObject. Also you can call either setNegativeButtonText() or setDeviceCredentialAllowed(true), but not both.

Inside the onAuthenticationSucceeded() callback method, you would proceed as you did in your old code. Since there is no CryptoObject, you likely don’t need to do anything with the AuthenticationResult.

Summary

And we’re done! In summary, to migrate from FingerprintManager to BiometricPrompt, you need to do the following:

  1. Throw away your custom fingerprint authentication UI.
  2. Instantiate BiometricPrompt inside your onCreate() or onCreateView() lifecycle method, while passing it an implementation of BiometricPrompt.AuthenticationCallback()
  3. Build a PromptInfo object.
  4. Create a BiometricPrompt.CryptoObject, if you want to use the recommended approach of including cryptography in your biometric authentication workflow.
  5. Call BiometricPrompt.authenticate().

For further reading, see the developer guide and API reference.

Happy coding!

--

--

Isai Damier
Android Developers

Android Engineer @ Google; founded geekviewpoint.com; Haitian; enjoy classical lit and chess. Twitter: @isaidamier