Bird
Raised Fist0
Android Kotlinmobile~20 mins

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

Choose your learning style10 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.

Practice

(1/5)
1. What is the main purpose of using BiometricPrompt in an Android app?
easy
A. To access the device camera for scanning QR codes
B. To create a custom login screen with username and password
C. To show the system biometric authentication dialog to the user
D. To manage network connections securely

Solution

  1. Step 1: Understand the role of BiometricPrompt

    BiometricPrompt is a system API that shows a standard biometric authentication dialog to the user.
  2. Step 2: Identify its main use

    It is used specifically for biometric authentication like fingerprint or face recognition, not for login screens or camera access.
  3. Final Answer:

    To show the system biometric authentication dialog to the user -> Option C
  4. Quick Check:

    BiometricPrompt = system biometric dialog [OK]
Hint: BiometricPrompt always shows system biometric dialog [OK]
Common Mistakes:
  • Confusing biometric prompt with custom login UI
  • Thinking it manages network or camera
  • Assuming it handles password input
2. Which of the following is the correct way to create a BiometricPrompt instance in Kotlin?
easy
A. val biometricPrompt = BiometricPrompt.create(this, executor, callback)
B. val biometricPrompt = BiometricPrompt(this, executor, callback)
C. val biometricPrompt = BiometricPrompt.newInstance(this, executor, callback)
D. val biometricPrompt = BiometricPrompt.Builder(this).build()

Solution

  1. Step 1: Recall BiometricPrompt constructor usage

    In Kotlin, BiometricPrompt is created by calling its constructor with context, executor, and callback.
  2. Step 2: Check each option

    Only val biometricPrompt = BiometricPrompt(this, executor, callback) uses the correct constructor syntax. Options A, C, and D use non-existent methods.
  3. Final Answer:

    val biometricPrompt = BiometricPrompt(this, executor, callback) -> Option B
  4. Quick Check:

    Constructor call = val biometricPrompt = BiometricPrompt(this, executor, callback) [OK]
Hint: Use constructor directly, no create() or newInstance() methods [OK]
Common Mistakes:
  • Using non-existent factory methods
  • Trying to use Builder pattern which BiometricPrompt doesn't have
  • Confusing with other Android APIs
3. Given this Kotlin code snippet, what will be printed if the user successfully authenticates biometrically?
val callback = object : BiometricPrompt.AuthenticationCallback() {
  override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
    println("Success")
  }
  override fun onAuthenticationFailed() {
    println("Failed")
  }
}
// Assume biometricPrompt is initialized and authenticate() is called with this callback
medium
A. Failed
B. Error
C. Nothing
D. Success

Solution

  1. Step 1: Understand callback methods

    The onAuthenticationSucceeded method runs when biometric authentication is successful.
  2. Step 2: Identify printed output on success

    Inside onAuthenticationSucceeded, it prints "Success". So on success, "Success" is printed.
  3. Final Answer:

    Success -> Option D
  4. Quick Check:

    Success callback prints "Success" [OK]
Hint: Success callback prints "Success" exactly [OK]
Common Mistakes:
  • Confusing onAuthenticationFailed with success
  • Assuming error prints on success
  • Thinking no output occurs
4. What is wrong with this Kotlin code snippet for biometric authentication?
val biometricPrompt = BiometricPrompt(this, executor, callback)
biometricPrompt.authenticate(promptInfo)

// callback object missing override for onAuthenticationError
medium
A. It will work but won't handle errors properly
B. It will crash because onAuthenticationError is not overridden
C. It will not compile due to missing method
D. It will authenticate without any callback

Solution

  1. Step 1: Check callback completeness

    Overriding onAuthenticationError is recommended but not mandatory; missing it won't crash or prevent compilation.
  2. Step 2: Understand effect of missing error override

    Without onAuthenticationError, errors won't be handled gracefully, but authentication still runs.
  3. Final Answer:

    It will work but won't handle errors properly -> Option A
  4. Quick Check:

    Missing error override = no error handling [OK]
Hint: Missing error override causes no error handling, not crash [OK]
Common Mistakes:
  • Assuming missing override causes crash
  • Thinking code won't compile
  • Believing authentication runs without callback
5. You want to add biometric authentication to your app but only if the device supports it and the user has enrolled biometrics. Which Kotlin code snippet correctly checks these conditions before showing the prompt?
val biometricManager = BiometricManager.from(this)
val canAuthenticate = biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
if (/* condition here */) {
  biometricPrompt.authenticate(promptInfo)
}
hard
A. canAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
B. canAuthenticate != BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
C. canAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
D. canAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE

Solution

  1. Step 1: Understand canAuthenticate return values

    canAuthenticate returns BIOMETRIC_SUCCESS if biometrics are available and enrolled.
  2. Step 2: Choose correct condition to proceed

    Only when canAuthenticate == BIOMETRIC_SUCCESS should the app show the biometric prompt.
  3. Final Answer:

    canAuthenticate == BiometricManager.BIOMETRIC_SUCCESS -> Option A
  4. Quick Check:

    Check for BIOMETRIC_SUCCESS before authenticating [OK]
Hint: Authenticate only if canAuthenticate equals BIOMETRIC_SUCCESS [OK]
Common Mistakes:
  • Checking for error codes instead of success
  • Ignoring enrollment status
  • Using wrong constants for condition
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard