Biometric authentication implementation

Alex Chen Jan 2026
2 tabs
package com.example.myapp.utils

import android.content.Context
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class BiometricAuthManager @Inject constructor(
    private val context: Context
) {

    fun canAuthenticate(): BiometricStatus {
        val biometricManager = BiometricManager.from(context)
        return when (biometricManager.canAuthenticate(
            BiometricManager.Authenticators.BIOMETRIC_STRONG or
            BiometricManager.Authenticators.DEVICE_CREDENTIAL
        )) {
            BiometricManager.BIOMETRIC_SUCCESS ->
                BiometricStatus.AVAILABLE
            BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->
                BiometricStatus.NO_HARDWARE
            BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE ->
                BiometricStatus.HW_UNAVAILABLE
            BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED ->
                BiometricStatus.NONE_ENROLLED
            else -> BiometricStatus.UNKNOWN_ERROR
        }
    }

    fun showBiometricPrompt(
        activity: FragmentActivity,
        title: String,
        subtitle: String? = null,
        description: String? = null,
        onSuccess: () -> Unit,
        onError: (String) -> Unit,
        onFailure: () -> Unit = {}
    ) {
        val executor = ContextCompat.getMainExecutor(activity)

        val biometricPrompt = BiometricPrompt(
            activity,
            executor,
            object : BiometricPrompt.AuthenticationCallback() {
                override fun onAuthenticationError(
                    errorCode: Int,
                    errString: CharSequence
                ) {
                    super.onAuthenticationError(errorCode, errString)
                    when (errorCode) {
                        BiometricPrompt.ERROR_USER_CANCELED,
                        BiometricPrompt.ERROR_CANCELED ->
                            onError("Authentication cancelled")
                        BiometricPrompt.ERROR_LOCKOUT ->
                            onError("Too many attempts. Try again later.")
                        BiometricPrompt.ERROR_LOCKOUT_PERMANENT ->
                            onError("Biometric authentication disabled")
                        else ->
                            onError(errString.toString())
                    }
                }

                override fun onAuthenticationSucceeded(
                    result: BiometricPrompt.AuthenticationResult
                ) {
                    super.onAuthenticationSucceeded(result)
                    onSuccess()
                }

                override fun onAuthenticationFailed() {
                    super.onAuthenticationFailed()
                    onFailure()
                }
            }
        )

        val promptInfo = BiometricPrompt.PromptInfo.Builder()
            .setTitle(title)
            .apply {
                subtitle?.let { setSubtitle(it) }
                description?.let { setDescription(it) }
            }
            .setAllowedAuthenticators(
                BiometricManager.Authenticators.BIOMETRIC_STRONG or
                BiometricManager.Authenticators.DEVICE_CREDENTIAL
            )
            .build()

        biometricPrompt.authenticate(promptInfo)
    }
}

enum class BiometricStatus {
    AVAILABLE,
    NO_HARDWARE,
    HW_UNAVAILABLE,
    NONE_ENROLLED,
    UNKNOWN_ERROR
}
2 files · kotlin Explain with highlit

BiometricPrompt provides secure authentication via fingerprint, face, or iris. I create BiometricPrompt with callback handling success, error, and failure. PromptInfo configures title, subtitle, description, and allowed authenticators. Negative button text or setDeviceCredentialAllowed enables PIN/pattern fallback. CryptoObject secures operations with authenticated keys. Authentication types—BIOMETRICSTRONG, BIOMETRICWEAK, DEVICE_CREDENTIAL—define security levels. Error codes indicate why authentication failed. The API works across Android versions with androidx.biometric library. Biometric auth protects sensitive operations—payments, data access, logins. It provides convenient security without passwords, respecting user privacy and hardware capabilities.