Skip to main content

Implementing Biometric Authentication

1. Checking Biometric Availability

Property Description Values
BiometricManager Class used to check biometric authentication support BiometricManager
canAuthenticate() Checks if biometric authentication is available int

Example: Checking Biometric Availability

import android.hardware.biometrics.BiometricManager
import android.os.Build

val biometricManager = BiometricManager.from(context)
val canAuthenticate = biometricManager.canAuthenticate()

when (canAuthenticate) {
    BiometricManager.BIOMETRIC_SUCCESS -> {
        // Biometric authentication is available
    }
    BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
        // No biometric hardware available
    }
    BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
        // Biometric hardware is unavailable
    }
    BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> {
        // Biometric authentication is not supported
    }
    BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
        // No biometrics enrolled
    }
}
Note: Requires android.permission.USE_BIOMETRIC or android.permission.USE_FINGERPRINT depending on Android version.

2. Creating BiometricPrompt Instance

Example: Creating BiometricPrompt Instance

import android.hardware.biometrics.BiometricPrompt
import android.os.CancellationSignal
import androidx.fragment.app.Fragment

class BiometricAuthFragment : Fragment() {
    private lateinit var biometricPrompt: BiometricPrompt
    private lateinit var promptInfo: BiometricPrompt.PromptInfo

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        biometricPrompt = BiometricPrompt(this, object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationSucceeded(
                result: BiometricPrompt.AuthenticationResult
            ) {
                // Handle successful authentication
                handleSuccessfulAuth()
            }

            override fun onAuthenticationFailed() {
                // Handle failed authentication
                handleFailedAuth()
            }

            override fun onAuthenticationError(
                errorCode: Int,
                errString: CharSequence
            ) {
                // Handle authentication error
                handleError(errorCode, errString)
            }
        })

        promptInfo = BiometricPrompt.PromptInfo.Builder()
            .setTitle("Biometric Authentication")
            .setSubtitle("Scan your fingerprint or face")
            .setDescription("Use your biometric credentials to authenticate")
            .setConfirmationRequired(true)
            .setNegativeButtonText("Use Password")
            .build()
    }

    private fun handleSuccessfulAuth() {
        // Proceed with authenticated action
    }

    private fun handleFailedAuth() {
        // Retry or show error
    }

    private fun handleError(errorCode: Int, errString: CharSequence) {
        // Handle error appropriately
    }
}
Property Description Values
PromptInfo.Builder().setTitle() Sets the title of the biometric prompt String
PromptInfo.Builder().setSubtitle() Sets the subtitle of the biometric prompt String
PromptInfo.Builder().setDescription() Sets the description of the biometric prompt String
PromptInfo.Builder().setConfirmationRequired() Indicates whether confirmation is required after authentication Boolean
PromptInfo.Builder().setNegativeButtonText() Sets the text for the negative button (e.g., "Use Password") String
Note: The BiometricPrompt requires the USE_BIOMETRIC permission in the manifest.
Warning: Ensure that the device has enrolled biometrics before attempting to use BiometricPrompt.

3. Configuring Prompt Info

Property Description Values
title Title to display in the biometric prompt "Authenticate to access your account"
subtitle Subtitle to display in the biometric prompt "Touch fingerprint sensor"
description Description to display in the biometric prompt "Use your fingerprint to unlock"
negativeButton Text for the negative button (e.g., "Cancel") "Cancel"

Example: Setting up Biometric Prompt Info

val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("Authenticate to access your account")
    .setSubtitle("Touch fingerprint sensor")
    .setDescription("Use your fingerprint to unlock")
    .setNegativeButtonText("Cancel")
    .build()

4. Authenticating Users

Property Description Example
BiometricManager Manages biometric authentication capabilities and availability val manager = BiometricManager.from(context)
BiometricPrompt Handles the authentication flow and user interaction val prompt = BiometricPrompt(this, executor, callback)
PromptInfo Configuration for the biometric prompt UI PromptInfo.Builder().setTitle("Sign in").build()

Example: Basic Biometric Authentication Setup

class MainActivity : AppCompatActivity() {
    private lateinit var biometricPrompt: BiometricPrompt
    private lateinit var promptInfo: BiometricPrompt.PromptInfo

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val executor = ContextCompat.getMainExecutor(this)
        biometricPrompt = BiometricPrompt(this, executor, object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                // Handle successful authentication
                Toast.makeText(this@MainActivity, "Authentication successful", Toast.LENGTH_SHORT).show()
            }

            override fun onAuthenticationFailed() {
                // Handle failed authentication
                Toast.makeText(this@MainActivity, "Authentication failed", Toast.LENGTH_SHORT).show()
            }
        })

        promptInfo = BiometricPrompt.PromptInfo.Builder()
            .setTitle("Sign in with fingerprint")
            .setSubtitle("Use your fingerprint to authenticate")
            .setNegativeButtonText("Cancel")
            .build()
    }

    private fun authenticateUser() {
        biometricPrompt.authenticate(promptInfo)
    }
}
Note: Ensure you have the necessary permissions in your AndroidManifest.xml: <uses-permission android:name="android.permission.USE_BIOMETRIC" />
Warning: On devices without biometric hardware, BiometricManager will return BIOMETRIC_ERROR_HW_UNAVAILABLE. Always check capabilities before attempting authentication.
Status Availability Notes
NEW Android 6.0+ Biometric authentication support added in Android 6.0 (API level 23)
DEPRECATED Android 10+ Legacy BiometricPrompt methods are deprecated in favor of new API

5. Handling Authentication Results

Result Type Description Properties
BiometricPrompt.AuthenticationResult Represents the result of a successful biometric authentication cryptoObject, authTime
BiometricPrompt.AuthenticationCallback Interface for handling authentication events onAuthenticationSucceeded(), onAuthenticationFailed(), onAuthenticationError()

Example: Handling Authentication Success

class AuthenticationCallback : BiometricPrompt.AuthenticationCallback() {
    override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
        val cryptoObject = result.cryptoObject
        // Process successful authentication
        handleSuccessfulAuth(cryptoObject)
    }

    override fun onAuthenticationFailed() {
        // Handle failed authentication
        showFailureMessage()
    }

    override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
        // Handle authentication error
        handleError(errorCode, errString)
    }
}
Note: The cryptoObject contains the cryptographic information needed to decrypt or verify data.
Warning: Always validate the authentication result before proceeding with sensitive operations.

6. Using CryptoObject

Property Description Values
CryptoObject Wrapper for cryptographic objects used in biometric authentication CryptoObject

Example: Creating CryptoObject

val cryptoObject = BiometricPrompt.CryptoObject(cipher)
Note: The CryptoObject is used to provide cryptographic context for biometric authentication, ensuring secure handling of sensitive data.

7. Handling Biometric Errors

Error Code Description Handling Recommendation
BIOMETRIC_ERROR_HW_UNAVAILABLE Biometric hardware is unavailable Inform user to check device compatibility or try again later
BIOMETRIC_ERROR_UNABLE_TO_PROCESS Biometric sensor is unable to process the current image Ask user to reposition finger or clean sensor
BIOMETRIC_ERROR_TIMEOUT Operation timed out waiting for biometric input Inform user and allow retry after a delay
BIOMETRIC_ERROR_NO_BIOMETRICS No biometric enrolled on the device Guide user to set up biometrics in system settings
BIOMETRIC_ERROR_LOCKOUT Too many failed attempts; biometric is temporarily locked out Inform user and suggest waiting or using alternative auth method
BIOMETRIC_ERROR_LOCKOUT_PERMANENT Lockout is permanent due to too many failed attempts Redirect user to use alternative authentication method or reset
BIOMETRIC_ERROR_USER_CANCELED User canceled the biometric operation Gracefully exit and allow user to retry if desired
BIOMETRIC_ERROR_HW_NOT_PRESENT Biometric hardware is not present on the device Inform user that biometric authentication is not supported

Example: Error Handling in Biometric Prompt

val biometricPrompt = BiometricPrompt(this, ContextCompat.getMainExecutor(this),
    object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
            when (errorCode) {
                BiometricPrompt.ERROR_HW_UNAVAILABLE -> {
                    // Handle hardware unavailable
                    Toast.makeText(this@MainActivity, "Biometric hardware not available", Toast.LENGTH_LONG).show()
                }
                BiometricPrompt.ERROR_LOCKOUT -> {
                    // Handle lockout
                    Toast.makeText(this@MainActivity, "Too many failed attempts. Try again later.", Toast.LENGTH_LONG).show()
                }
                else -> {
                    // Handle other errors
                    Toast.makeText(this@MainActivity, "Authentication error: $errString", Toast.LENGTH_LONG).show()
                }
            }
        }

        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            // Handle successful authentication
            Toast.makeText(this@MainActivity, "Authentication successful", Toast.LENGTH_SHORT).show()
        }

        override fun onAuthenticationFailed() {
            // Handle failed authentication
            Toast.makeText(this@MainActivity, "Authentication failed", Toast.LENGTH_SHORT).show()
        }
    })

8. Supporting Device Credentials

Device credentials provide an alternative to biometric authentication for users who prefer or need to use PINs, patterns, or passwords.

Property Description Values
DeviceCredential Represents the device's built-in authentication methods (PIN, Pattern, Password) DeviceCredential
BiometricPrompt Class for managing biometric authentication flow BiometricPrompt
CryptoObject Wraps cryptographic objects for secure authentication CryptoObject

Example: Using Device Credentials

val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("Authenticate with Device Credential")
    .setSubtitle("Use your device PIN or password")
    .setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
    .build()

val biometricPrompt = BiometricPrompt(this, object : BiometricPrompt.AuthenticationCallback() {
    override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
        // Handle successful authentication
    }

    override fun onAuthenticationFailed() {
        // Handle failed authentication
    }
})

biometricPrompt.authenticate(promptInfo)
Note: Device credentials are supported on Android 10 (API level 29) and higher.

Key Points

  • Device credentials provide a fallback for biometric authentication.
  • Use BiometricManager.Authenticators.DEVICE_CREDENTIAL to enable device credential support.
  • Users can authenticate using PIN, pattern, or password.

9. Implementing Biometric Enrollment

Biometric enrollment in Android involves registering a user's biometric credentials (fingerprint, face, etc.) for future authentication. The process typically involves using the BiometricPrompt API to guide users through enrollment.

Example: Starting Biometric Enrollment

import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import android.content.Context
import android.os.CancellationSignal

class BiometricEnrollmentHelper(private val context: Context) {

    fun startEnrollment() {
        val biometricManager = BiometricManager.from(context)
        val enrollmentStatus = biometricManager.canAuthenticate()

        when (enrollmentStatus) {
            BiometricManager.BIOMETRIC_SUCCESS -> {
                // Start enrollment process
                val promptInfo = BiometricPrompt.PromptInfo.Builder()
                    .setTitle("Enroll Fingerprint")
                    .setSubtitle("Scan your fingerprint to enroll")
                    .setConfirmationRequired(false)
                    .build()

                val biometricPrompt = BiometricPrompt(this, object : BiometricPrompt.AuthenticationCallback() {
                    override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                        // Enrollment successful
                    }

                    override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                        // Handle error
                    }
                })

                biometricPrompt.authenticate(promptInfo, CancellationSignal())
            }
            else -> {
                // Handle unsupported or unavailable biometric
            }
        }
    }
}
Property Description Notes
canAuthenticate() Checks if biometric authentication is available Returns BIOMETRIC_SUCCESS if ready for enrollment
PromptInfo.Builder() Builds configuration for the biometric prompt Allows setting title, subtitle, and confirmation settings
setConfirmationRequired(false) Controls whether confirmation is needed after enrollment Set to false for enrollment flow
Note: Enrollment requires appropriate permissions in AndroidManifest.xml, including USE_FINGERPRINT or USE_BIOMETRIC.
Warning: Users must explicitly consent to biometric enrollment. Do not proceed without user interaction.

10. Testing Biometric Authentication

Example: Testing Biometric Authentication Flow

class BiometricAuthTest {
    @Test
    fun testBiometricAuthenticationSuccess() {
        val activity = Robolectric.buildActivity(MainActivity::class.java).create().get()
        val biometricPrompt = BiometricPrompt.Builder(activity)
            .setTitle("Authenticate")
            .setSubtitle("Scan your fingerprint")
            .setDescription("Touch sensor to authenticate")
            .build()

        val callback = object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationSucceeded(
                result: BiometricPrompt.AuthenticationResult
            ) {
                assertTrue("Authentication should succeed", true)
            }

            override fun onAuthenticationFailed() {
                fail("Authentication failed unexpectedly")
            }
        }

        biometricPrompt.authenticate(
            CancellationSignal(),
            ContextCompat.getMainExecutor(activity),
            callback
        )
    }
}
Test Case Description Status
Successful Authentication Simulates successful biometric scan with valid credentials NEW
Failed Authentication Simulates failed biometric scan with invalid credentials NEW
Device Not Supported Verifies behavior when device lacks biometric hardware LEGACY
Note: Ensure that your testing environment has appropriate biometric simulation enabled.
Warning: Do not use real biometric data in automated tests; always simulate or mock.

Testing Best Practices

  • Use Robolectric or Espresso for UI testing
  • Mock biometric hardware to avoid device dependency
  • Test both success and failure scenarios
  • Validate fallback mechanisms for unsupported devices

Was this article helpful?