Show a biometric authentication dialog

I method of protecting sensitive information or premium content within your app is to request biometric authentication, such equally using face recognition or fingerprint recognition. This guide explains how to back up biometric login flows in your app.

Declare the types of hallmark that your app supports

To define the types of authentication that your app supports, use the BiometricManager.Authenticators interface. The arrangement allows you to declare the post-obit types of hallmark:

BIOMETRIC_STRONG
Authentication using a Class 3 biometric, as defined on the Android 11 compatibility definition page.
BIOMETRIC_WEAK
Authentication using a Class 2 biometric, as defined on the Android 11 compatibility definition folio.
DEVICE_CREDENTIAL
Authentication using a screen lock credential – the user's Pin, pattern, or countersign.

In order to enroll an authenticator, the user needs to create a Pivot, pattern, or password. If the user doesn't already have one, the biometric enrollment flow prompts them to create one.

To define the types of biometric authentication that your app accepts, laissez passer an authentication blazon or a bitwise combination of types into the setAllowedAuthenticators() method. The following lawmaking snippet shows how to support authentication using either a Class 3 biometric or a screen lock credential.

Kotlin

// Allows user to authenticate using either a Class 3 biometric or // their lock screen credential (Pivot, design, or password). promptInfo = BiometricPrompt.PromptInfo.Builder()         .setTitle("Biometric login for my app")         .setSubtitle("Log in using your biometric credential")                        // Can't call setNegativeButtonText() and         // setAllowedAuthenticators(... or DEVICE_CREDENTIAL) at the same fourth dimension.         // .setNegativeButtonText("Use account password")         .setAllowedAuthenticators(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)                        .build()                      

Java

// Allows user to authenticate using either a Class three biometric or // their lock screen credential (Pin, pattern, or countersign). promptInfo = new BiometricPrompt.PromptInfo.Builder()         .setTitle("Biometric login for my app")         .setSubtitle("Log in using your biometric credential")                        // Can't call setNegativeButtonText() and         // setAllowedAuthenticators(...|DEVICE_CREDENTIAL) at the same time.         // .setNegativeButtonText("Use account password")         .setAllowedAuthenticators(BIOMETRIC_STRONG | DEVICE_CREDENTIAL)                        .build();                      

Bank check that biometric authentication is available

After y'all make up one's mind which authentication elements your app supports, cheque whether these elements are available. To exercise and so, pass the same bitwise combination of types that you previously declared into the canAuthenticate() method. If necessary, invoke the ACTION_BIOMETRIC_ENROLL intent action. In the intent extra, provide the set of authenticators that your app accepts. This intent prompts the user to register credentials for an authenticator that your app accepts.

Kotlin

val biometricManager = BiometricManager.from(this) when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {     BiometricManager.BIOMETRIC_SUCCESS ->         Log.d("MY_APP_TAG", "App can authenticate using biometrics.")     BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->         Log.e("MY_APP_TAG", "No biometric features bachelor on this device.")     BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE ->         Log.eastward("MY_APP_TAG", "Biometric features are currently unavailable.")     BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {         // Prompts the user to create credentials that your app accepts.         val enrollIntent = Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply {             putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,                 BIOMETRIC_STRONG or DEVICE_CREDENTIAL)         }         startActivityForResult(enrollIntent,                      REQUEST_CODE)     } }                    

Java

BiometricManager biometricManager = BiometricManager.from(this); switch (biometricManager.canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL)) {     example BiometricManager.BIOMETRIC_SUCCESS:         Log.d("MY_APP_TAG", "App tin can authenticate using biometrics.");         intermission;     example BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:         Log.e("MY_APP_TAG", "No biometric features available on this device.");         interruption;     case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:         Log.e("MY_APP_TAG", "Biometric features are currently unavailable.");         break;     instance BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:         // Prompts the user to create credentials that your app accepts.         final Intent enrollIntent = new Intent(Settings.ACTION_BIOMETRIC_ENROLL);         enrollIntent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,                 BIOMETRIC_STRONG | DEVICE_CREDENTIAL);         startActivityForResult(enrollIntent,                      REQUEST_CODE);         intermission; }                    

Determine how the user authenticated

After the user authenticates, y'all tin bank check whether the user authenticated using a device credential or a biometric credential past calling getAuthenticationType().

Display the login prompt

To display a organisation prompt that requests the user to authenticate using biometric credentials, use the Biometric library. This system-provided dialog is consistent across the apps that utilise it, creating a more trustworthy user experience. An case dialog appears in Figure i.

Screenshot showing dialog

Figure 1. Organization dialog requesting biometric authentication

To add together biometric hallmark to your app using the Biometric library, complete the following steps:

  1. In your app module's build.gradle file, add a dependency on the androidx.biometric library.

  2. In the activity or fragment that hosts the biometric login dialog, display the dialog using the logic shown in the following code snippet:

    Kotlin

    private lateinit var executor: Executor private lateinit var biometricPrompt: BiometricPrompt private lateinit var promptInfo: BiometricPrompt.PromptInfo  override fun onCreate(savedInstanceState: Packet?) {     super.onCreate(savedInstanceState)     setContentView(R.layout.activity_login)     executor = ContextCompat.getMainExecutor(this)     biometricPrompt = BiometricPrompt(this, executor,             object : BiometricPrompt.AuthenticationCallback() {         override fun onAuthenticationError(errorCode: Int,                 errString: CharSequence) {             super.onAuthenticationError(errorCode, errString)             Toast.makeText(applicationContext,                 "Authentication fault: $errString", Toast.LENGTH_SHORT)                 .show()         }          override fun onAuthenticationSucceeded(                 consequence: BiometricPrompt.AuthenticationResult) {             super.onAuthenticationSucceeded(result)             Toast.makeText(applicationContext,                 "Authentication succeeded!", Toast.LENGTH_SHORT)                 .prove()         }          override fun onAuthenticationFailed() {             super.onAuthenticationFailed()             Toast.makeText(applicationContext, "Authentication failed",                 Toast.LENGTH_SHORT)                 .bear witness()         }     })      promptInfo = BiometricPrompt.PromptInfo.Builder()             .setTitle("Biometric login for my app")             .setSubtitle("Log in using your biometric credential")             .setNegativeButtonText("Use account password")             .build()      // Prompt appears when user clicks "Log in".     // Consider integrating with the keystore to unlock cryptographic operations,     // if needed past your app.     val biometricLoginButton =             findViewById<Button>(R.id.biometric_login)     biometricLoginButton.setOnClickListener {         biometricPrompt.cosign(promptInfo)     } }                        

    Java

    private Executor executor; individual BiometricPrompt biometricPrompt; private BiometricPrompt.PromptInfo promptInfo;  @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_login);     executor = ContextCompat.getMainExecutor(this);     biometricPrompt = new BiometricPrompt(MainActivity.this,             executor, new BiometricPrompt.AuthenticationCallback() {         @Override         public void onAuthenticationError(int errorCode,                 @NonNull CharSequence errString) {             super.onAuthenticationError(errorCode, errString);             Toast.makeText(getApplicationContext(),                 "Hallmark error: " + errString, Toast.LENGTH_SHORT)                 .evidence();         }          @Override         public void onAuthenticationSucceeded(                 @NonNull BiometricPrompt.AuthenticationResult result) {             super.onAuthenticationSucceeded(result);             Toast.makeText(getApplicationContext(),                 "Authentication succeeded!", Toast.LENGTH_SHORT).evidence();         }          @Override         public void onAuthenticationFailed() {             super.onAuthenticationFailed();             Toast.makeText(getApplicationContext(), "Hallmark failed",                 Toast.LENGTH_SHORT)                 .show();         }     });      promptInfo = new BiometricPrompt.PromptInfo.Builder()             .setTitle("Biometric login for my app")             .setSubtitle("Log in using your biometric credential")             .setNegativeButtonText("Utilize account password")             .build();      // Prompt appears when user clicks "Log in".     // Consider integrating with the keystore to unlock cryptographic operations,     // if needed past your app.     Button biometricLoginButton = findViewById(R.id.biometric_login);     biometricLoginButton.setOnClickListener(view -> {             biometricPrompt.authenticate(promptInfo);     }); }                        

Use a cryptographic solution that depends on authentication

To further protect sensitive information within your app, yous tin can contain cryptography into your biometric authentication workflow using an instance of CryptoObject. The framework supports the following cryptographic objects: Signature, Nil, and Mac.

After the user authenticates successfully using a biometric prompt, your app can perform a cryptographic performance. For example, if you authenticate using a Cipher object, your app tin and so perform encryption and decryption using a SecretKey object.

The post-obit sections go through examples of using a Cipher object and a SecretKey object to encrypt data. Each example makes utilize of the following methods:

Kotlin

private fun generateSecretKey(keyGenParameterSpec: KeyGenParameterSpec) {     val keyGenerator = KeyGenerator.getInstance(             KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")     keyGenerator.init(keyGenParameterSpec)     keyGenerator.generateKey() }  individual fun getSecretKey(): SecretKey {     val keyStore = KeyStore.getInstance("AndroidKeyStore")      // Before the keystore tin be accessed, it must be loaded.     keyStore.load(null)     return keyStore.getKey(KEY_NAME, null) as SecretKey }  individual fun getCipher(): Zippo {     return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"             + KeyProperties.BLOCK_MODE_CBC + "/"             + KeyProperties.ENCRYPTION_PADDING_PKCS7) }                    

Java

private void generateSecretKey(KeyGenParameterSpec keyGenParameterSpec) {     KeyGenerator keyGenerator = KeyGenerator.getInstance(             KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");     keyGenerator.init(keyGenParameterSpec);     keyGenerator.generateKey(); }  private SecretKey getSecretKey() {     KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");      // Before the keystore tin be accessed, it must exist loaded.     keyStore.load(null);     return ((SecretKey)keyStore.getKey(KEY_NAME, zilch)); }  private Cipher getCipher() {     return Goose egg.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"             + KeyProperties.BLOCK_MODE_CBC + "/"             + KeyProperties.ENCRYPTION_PADDING_PKCS7); }                    

Authenticate using only biometric credentials

If your app uses a undercover fundamental that requires biometric credentials to unlock, the user must authenticate their biometric credentials each fourth dimension before your app accesses the key.

To encrypt sensitive information but after the user authenticates using biometric credentials, complete the post-obit steps:

  1. Generate a fundamental that uses the following KeyGenParameterSpec configuration:

    Kotlin

    generateSecretKey(KeyGenParameterSpec.Architect(                          KEY_NAME,         KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)         .setBlockModes(KeyProperties.BLOCK_MODE_CBC)         .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)         .setUserAuthenticationRequired(true)         // Invalidate the keys if the user has registered a new biometric         // credential, such equally a new fingerprint. Tin can phone call this method only         // on Android vii.0 (API level 24) or higher. The variable         // "invalidatedByBiometricEnrollment" is true past default.         .setInvalidatedByBiometricEnrollment(true)         .build())                        

    Java

    generateSecretKey(new KeyGenParameterSpec.Builder(                          KEY_NAME,         KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)         .setBlockModes(KeyProperties.BLOCK_MODE_CBC)         .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)         .setUserAuthenticationRequired(truthful)         // Invalidate the keys if the user has registered a new biometric         // credential, such equally a new fingerprint. Can call this method just         // on Android 7.0 (API level 24) or higher. The variable         // "invalidatedByBiometricEnrollment" is true by default.         .setInvalidatedByBiometricEnrollment(true)         .build());                        
  2. Start a biometric hallmark workflow that incorporates a naught:

    Kotlin

    biometricLoginButton.setOnClickListener {     // Exceptions are unhandled inside this snippet.     val naught = getCipher()     val secretKey = getSecretKey()     cipher.init(Null.ENCRYPT_MODE, secretKey)     biometricPrompt.authenticate(promptInfo,             BiometricPrompt.CryptoObject(cipher)) }                        

    Java

    biometricLoginButton.setOnClickListener(view -> {     // Exceptions are unhandled within this snippet.     Nix naught = getCipher();     SecretKey secretKey = getSecretKey();     cipher.init(Cipher.ENCRYPT_MODE, secretKey);     biometricPrompt.cosign(promptInfo,             new BiometricPrompt.CryptoObject(cipher)); });                        
  3. Within your biometric authentication callbacks, apply the underground central to encrypt the sensitive data:

    Kotlin

    override fun onAuthenticationSucceeded(         upshot: BiometricPrompt.AuthenticationResult) {     val encryptedInfo: ByteArray = upshot.cryptoObject.cipher?.doFinal(                          plaintext-string.toByteArray(Charset.defaultCharset())     )     Log.d("MY_APP_TAG", "Encrypted information: " +             Arrays.toString(encryptedInfo)) }                        

    Coffee

    @Override public void onAuthenticationSucceeded(         @NonNull BiometricPrompt.AuthenticationResult effect) {     // NullPointerException is unhandled; use Objects.requireNonNull().     byte[] encryptedInfo = result.getCryptoObject().getCipher().doFinal(                          plaintext-string.getBytes(Charset.defaultCharset()));     Log.d("MY_APP_TAG", "Encrypted information: " +             Arrays.toString(encryptedInfo)); }

Authenticate using either biometric or lock screen credentials

You can utilize a secret fundamental that allows for authentication using either biometric credentials or lock screen credentials (Pin, pattern, or countersign). When configuring this primal, specify a validity time period. During this time flow, your app can perform multiple cryptographic operations without the user needing to re-authenticate.

To encrypt sensitive information after the user authenticates using biometric or lock screen credentials, complete the following steps:

  1. Generate a key that uses the following KeyGenParameterSpec configuration:

    Kotlin

    generateSecretKey(KeyGenParameterSpec.Builder(                          KEY_NAME,     KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)     .setBlockModes(KeyProperties.BLOCK_MODE_CBC)     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)     .setUserAuthenticationRequired(true)     .setUserAuthenticationParameters(VALIDITY_DURATION_SECONDS,                          ALLOWED_AUTHENTICATORS)     .build())                        

    Java

    generateSecretKey(new KeyGenParameterSpec.Architect(                          KEY_NAME,     KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)     .setBlockModes(KeyProperties.BLOCK_MODE_CBC)     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)     .setUserAuthenticationRequired(truthful)     .setUserAuthenticationParameters(VALIDITY_DURATION_SECONDS,                          ALLOWED_AUTHENTICATORS)     .build());                        
  2. Inside a fourth dimension period of VALIDITY_DURATION_SECONDS after the user authenticates, encrypt the sensitive data:

    Kotlin

    individual fun encryptSecretInformation() {     // Exceptions are unhandled for getCipher() and getSecretKey().     val cipher = getCipher()     val secretKey = getSecretKey()     endeavour {         naught.init(Nothing.ENCRYPT_MODE, secretKey)         val encryptedInfo: ByteArray = cipher.doFinal(                          plaintext-cord.toByteArray(Charset.defaultCharset()))         Log.d("MY_APP_TAG", "Encrypted information: " +                 Arrays.toString(encryptedInfo))     } catch (due east: InvalidKeyException) {         Log.due east("MY_APP_TAG", "Key is invalid.")     } grab (e: UserNotAuthenticatedException) {         Log.d("MY_APP_TAG", "The key's validity timed out.")         biometricPrompt.authenticate(promptInfo)     }                        

    Java

    private void encryptSecretInformation() {     // Exceptions are unhandled for getCipher() and getSecretKey().     Cipher zero = getCipher();     SecretKey secretKey = getSecretKey();     attempt {         // NullPointerException is unhandled; use Objects.requireNonNull().         ciper.init(Cipher.ENCRYPT_MODE, secretKey);         byte[] encryptedInfo = nix.doFinal(                          plaintext-string.getBytes(Charset.defaultCharset()));     } catch (InvalidKeyException e) {         Log.e("MY_APP_TAG", "Key is invalid.");     } catch (UserNotAuthenticatedException e) {         Log.d("MY_APP_TAG", "The primal's validity timed out.");         biometricPrompt.authenticate(promptInfo);     } }                        

Authenticate using auth-per-use keys

Y'all tin can provide support for auth-per-utilise keys within your instance of BiometricPrompt. Such a fundamental requires the user to present either a biometric credential or a device credential each time your app needs to access information that'due south guarded by that key. Auth-per-use keys can exist useful for high-value transactions, such as making a large payment or updating a person's wellness records.

To associate a BiometricPrompt object with an auth-per-employ key, add lawmaking similar to the following:

Kotlin

val authPerOpKeyGenParameterSpec =         KeyGenParameterSpec.Builder("myKeystoreAlias",                      key-purpose)     // Accept either a biometric credential or a device credential.     // To accept only one blazon of credential, include just that type every bit the     // second statement.     .setUserAuthenticationParameters(0 /* duration */,             KeyProperties.AUTH_BIOMETRIC_STRONG or             KeyProperties.AUTH_DEVICE_CREDENTIAL)     .build()                    

Java

KeyGenParameterSpec authPerOpKeyGenParameterSpec =         new KeyGenParameterSpec.Architect("myKeystoreAlias",                      key-purpose)     // Have either a biometric credential or a device credential.     // To accept just one type of credential, include only that type as the     // 2d argument.     .setUserAuthenticationParameters(0 /* duration */,             KeyProperties.AUTH_BIOMETRIC_STRONG |             KeyProperties.AUTH_DEVICE_CREDENTIAL)     .build();                    

Cosign without explicit user action

By default, the system requires users to perform a specific action, such as pressing a push button, after their biometric credentials are accepted. This configuration is preferable if your app is showing the dialog to confirm a sensitive or high-risk action, such as making a purchase.

If your app shows a biometric authentication dialog for a lower-risk action, however, y'all can provide a hint to the organisation that the user doesn't need to confirm authentication. This hint can permit the user to view content in your app more than speedily afterwards re-authenticating using a passive modality, such equally face- or iris-based recognition. To provide this hint, pass false into the setConfirmationRequired() method.

Figure 2 shows two versions of the aforementioned dialog. I version requires an explicit user action, and the other version doesn't.

The following code snippet shows how to nowadays a dialog that doesn't require an explicit user action to consummate the authentication procedure:

Kotlin

// Allows user to cosign without performing an action, such as pressing a // push button, later their biometric credential is accepted. promptInfo = BiometricPrompt.PromptInfo.Architect()         .setTitle("Biometric login for my app")         .setSubtitle("Log in using your biometric credential")         .setNegativeButtonText("Utilise account countersign")                      .setConfirmationRequired(fake)                      .build()                    

Java

// Allows user to cosign without performing an action, such as pressing a // button, after their biometric credential is accepted. promptInfo = new BiometricPrompt.PromptInfo.Builder()         .setTitle("Biometric login for my app")         .setSubtitle("Log in using your biometric credential")         .setNegativeButtonText("Use account countersign")                      .setConfirmationRequired(false)                      .build();                    

Allow for fallback to non-biometric credentials

If yous desire your app to allow hallmark using either biometric or device credentials, you tin declare that your app supports device credentials past including DEVICE_CREDENTIAL in the set of values that you pass into setAllowedAuthenticators().

If your app currently uses createConfirmDeviceCredentialIntent() or setDeviceCredentialAllowed() to provide this adequacy, switch to using setAllowedAuthenticators().

Boosted resources

To learn more about biometric authentication on Android, consult the post-obit resources.

Blog posts

  • Migrating from FingerprintManager to BiometricPrompt