0
0
Android Kotlinmobile~20 mins

Biometric authentication in Android Kotlin - Mini App: Build & Ship

Choose your learning style9 modes available
Build: BiometricAuthScreen
This screen allows users to authenticate using their fingerprint or face recognition. It shows a button to start biometric authentication and displays the result message.
Target UI
-------------------------
| Biometric Authentication |
|-------------------------|
|                         |
|  [Authenticate Button]  |
|                         |
|  Status: Not authenticated |
|                         |
-------------------------
Add a button labeled 'Authenticate' that triggers biometric authentication.
Use Android BiometricPrompt API to authenticate the user.
Display the authentication status below the button: 'Not authenticated', 'Authentication succeeded', or 'Authentication failed'.
Handle errors gracefully and update the status accordingly.
Starter Code
Android Kotlin
package com.example.biometricauth

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

class BiometricAuthScreen : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      BiometricAuthApp()
    }
  }
}

@Composable
fun BiometricAuthApp() {
  var status by remember { mutableStateOf("Not authenticated") }

  Column(
    modifier = Modifier.fillMaxSize().padding(16.dp)
  ) {
    Button(onClick = {
      // TODO: Add biometric authentication logic here
    }) {
      Text("Authenticate")
    }

    Text(text = "Status: $status", modifier = Modifier.padding(top = 16.dp))
  }
}
Task 1
Task 2
Solution
Android Kotlin
package com.example.biometricauth

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat

class BiometricAuthScreen : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      BiometricAuthApp()
    }
  }
}

@Composable
fun BiometricAuthApp() {
  val context = LocalContext.current
  var status by remember { mutableStateOf("Not authenticated") }

  val executor = ContextCompat.getMainExecutor(context)

  val biometricPrompt = BiometricPrompt(
    context as ComponentActivity,
    executor,
    object : BiometricPrompt.AuthenticationCallback() {
      override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
        super.onAuthenticationSucceeded(result)
        status = "Authentication succeeded"
      }

      override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
        super.onAuthenticationError(errorCode, errString)
        status = "Authentication error: $errString"
      }

      override fun onAuthenticationFailed() {
        super.onAuthenticationFailed()
        status = "Authentication failed"
      }
    }
  )

  val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("Biometric Authentication")
    .setSubtitle("Log in using your biometric credential")
    .setNegativeButtonText("Cancel")
    .build()

  Column(
    modifier = Modifier.fillMaxSize().padding(16.dp)
  ) {
    Button(onClick = {
      biometricPrompt.authenticate(promptInfo)
    }) {
      Text("Authenticate")
    }

    Text(text = "Status: $status", modifier = Modifier.padding(top = 16.dp))
  }
}

This solution uses Android's BiometricPrompt API to perform biometric authentication.

We create a BiometricPrompt with an AuthenticationCallback to handle success, failure, and errors. The status state updates accordingly to show the user the current authentication state.

The PromptInfo configures the dialog shown to the user with a title, subtitle, and cancel button.

When the user taps the 'Authenticate' button, biometricPrompt.authenticate(promptInfo) starts the biometric authentication flow.

This approach ensures a simple UI with clear feedback and uses modern Android biometric APIs.

Final Result
Completed Screen
-------------------------
| Biometric Authentication |
|-------------------------|
|                         |
|  [Authenticate Button]  |
|                         |
|  Status: Authentication succeeded |
|                         |
-------------------------
User taps 'Authenticate' button.
System shows biometric prompt (fingerprint or face).
If authentication succeeds, status text updates to 'Authentication succeeded'.
If authentication fails or errors, status text updates accordingly.
Stretch Goal
Add a fallback option to authenticate with device PIN or password if biometric authentication is not available or fails.
💡 Hint
Use BiometricPrompt.PromptInfo.Builder().setDeviceCredentialAllowed(true) to enable device credential fallback.