Implement Biometric Authentication in Android Apps
Implement Biometric Authentication in an Android application using Jetpack Compose.
Introduction
Biometric authentication provides a secure and user-friendly way to validate a user’s identity using fingerprint, face, or iris recognition. Android’s Biometric API simplifies this process, ensuring compatibility across devices. This blog will guide you through implementing biometric authentication in an Android app using Kotlin, with step-by-step instructions and code examples.
Prerequisites
Before we dive into the implementation, ensure your project meets the following requirements:
- Minimum SDK version: 30
- Ensure your device supports biometric authentication.
Step 1: Add Required Dependencies
Add the Biometric API dependency to your app-level build.gradle
file:
[versions]
biometric = "1.2.0-alpha05"
[libraries]
androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "biometric"
dependencies {
implementation(libs.androidx.biometric)
...
}
Step 2: Check Device Compatibility
Before implementing biometric authentication, check if the device supports biometrics:
private fun isBiometricAvailable(): Boolean {
val biometricManager = BiometricManager.from(this)
return when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
BiometricManager.BIOMETRIC_SUCCESS -> true
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
Log.d("BiometricAuth", "No biometric hardware available.")
false
}
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
Log.d("BiometricAuth", "Biometric hardware unavailable.")
false
}
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
Log.d("BiometricAuth", "No biometrics enrolled.")
false
}
else -> false
}
}
Step 3: Create a Biometric Prompt
To display the biometric authentication dialog, use BiometricPrompt
. It requires three components:
- Executor for handling callback responses.
- Callback for success, failure, and error handling.
- PromptInfo for customizing the dialog.
fun showBiometricPrompt(
activity: FragmentActivity,
onSuccess: () -> 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)
Log.d("BiometricAuth", "Error: $errString")
onFailure()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Log.d("BiometricAuth", "Authentication succeeded!")
onSuccess()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Log.d("BiometricAuth", "Authentication failed.")
onFailure()
}
})
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Biometric Authentication")
.setSubtitle("Log in using your biometric credentials")
.setNegativeButtonText("Cancel")
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
.build()
biometricPrompt.authenticate(promptInfo)
}
Step 4: Trigger the Biometric Prompt
Invoke the showBiometricPrompt()
method when needed. For example, you could tie it to a button click in your app:
Button(onClick = {
if (activity!=null) {
if (isBiometricAvailable(context)) {
showBiometricPrompt(
activity = activity,
onSuccess = { authenticationResult = "Authentication Succeeded!" },
onFailure = { authenticationResult = "Authentication Failed!" }
)
} else {
promptEnrollBiometric(context)
authenticationResult = "Biometric not available or not configured."
}
}
}) {
Text(text = "Authenticate")
}
}
Check if Biometric available or not:
fun isBiometricAvailable(context: Context): Boolean {
val biometricManager = BiometricManager.from(context)
return when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
BiometricManager.BIOMETRIC_SUCCESS -> true
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
Log.d("BiometricAuth", "No biometric hardware available.")
false
}
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
Log.d("BiometricAuth", "Biometric hardware unavailable.")
false
}
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
Log.d("BiometricAuth", "No biometrics enrolled.")
false
}
else -> false
}
}
Prompt the user to enroll biometric credentials:
private fun promptEnrollBiometric(context: Context) {
try {
val enrollIntent = Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply {
putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BiometricManager.Authenticators.BIOMETRIC_STRONG)
}
if (context is FragmentActivity) {
context.startActivity(enrollIntent)
}
} catch (e: ActivityNotFoundException) {
// Fallback to a more generic settings screen
val fallbackIntent = Intent(Settings.ACTION_SECURITY_SETTINGS)
if (context is FragmentActivity) {
context.startActivity(fallbackIntent)
}
}
}
Complete Code Example
Here’s the complete implementation in an Activity
:
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import com.example.biometricauthentication.ui.theme.BiometricAuthenticationTheme
class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
BiometricAuthenticationTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
BiometricAuthenticationDemo(modifier = Modifier.padding(innerPadding))
}
}
}
}
}
fun isBiometricAvailable(context: Context): Boolean {
val biometricManager = BiometricManager.from(context)
return when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
BiometricManager.BIOMETRIC_SUCCESS -> true
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
Log.d("BiometricAuth", "No biometric hardware available.")
false
}
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
Log.d("BiometricAuth", "Biometric hardware unavailable.")
false
}
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
Log.d("BiometricAuth", "No biometrics enrolled.")
false
}
else -> false
}
}
@Composable
fun BiometricAuthenticationDemo(modifier: Modifier) {
val context = LocalContext.current
val activity = context as? FragmentActivity // Safe cast to FragmentActivity
var authenticationResult by remember { mutableStateOf("Not Authenticated") }
Column(
modifier = modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Biometric Authentication",
style = MaterialTheme.typography.titleLarge
)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
if (activity != null) {
if (isBiometricAvailable(context)) {
promptEnrollBiometric(context)
/*showBiometricPrompt(
activity = activity,
onSuccess = { authenticationResult = "Authentication Succeeded!" },
onFailure = { authenticationResult = "Authentication Failed!" }
)*/
} else {
promptEnrollBiometric(context)
authenticationResult = "Biometric not available or not configured."
}
}
}) {
Text(text = "Authenticate")
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = authenticationResult,
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center
)
}
}
/**
* Prompt the user to enroll biometric credentials.
*/
fun promptEnrollBiometric(context: Context) {
try {
val enrollIntent = Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply {
putExtra(
Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
BiometricManager.Authenticators.BIOMETRIC_STRONG
)
}
if (context is FragmentActivity) {
context.startActivity(enrollIntent)
}
} catch (e: ActivityNotFoundException) {
// Fallback to a more generic settings screen
val fallbackIntent = Intent(Settings.ACTION_SECURITY_SETTINGS)
if (context is FragmentActivity) {
context.startActivity(fallbackIntent)
}
}
}
fun showBiometricPrompt(
activity: FragmentActivity,
onSuccess: () -> 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)
Log.d("BiometricAuth", "Error: $errString")
onFailure()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Log.d("BiometricAuth", "Authentication succeeded!")
onSuccess()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Log.d("BiometricAuth", "Authentication failed.")
onFailure()
}
})
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Biometric Authentication")
.setSubtitle("Log in using your biometric credentials")
.setNegativeButtonText("Cancel")
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
.build()
biometricPrompt.authenticate(promptInfo)
}
Conclusion
Jetpack Compose makes it simple and elegant to build modern Android UIs. By integrating biometric authentication, you can enhance your app’s security and provide a seamless user experience.
Feel free to fork this code or modify it as needed. Check out the Android Biometric API Documentation for more advanced use cases.
GitHub Repository
Thanks for reading this article. Don’t forget to clap👏/recommend as much as you can and also share📤 with your friends. It means a lot to me.
For more about programming, follow me , so you’ll get notified whenever a new article is posted. Check my portfoli website here .