All You Need to Know About Android’s Biometric Library
By: Anant Raman
Tags: Biometric Authentication, Kotlin, Android, Biometrics

Android has released a new biometric authentication library from Android API level 28 or Android 9). Android’s biometric library is an extension to the existing Figure print authentication library (available on Android API level 23 or Android 6 and above). For biometric authentication, the device must have at least an Android API level 23 or above.

How is it different from Android’s Fingerprint library?

The existing fingerprint library only has the support for fingerprint authentication but this library even supports Face authentication and Iris authentication. Unlike IOS which gives us feature to choose between Face lock or fingerprint, we do not have the authority in android. The reason for this is android supports a large number of devices that have a variety of hardware used in the device. Giving this authority to the user would be a compromise with security. Only those device which has a strong rated face or iris scan hardware can support face or iris authentication. At present, very few devices have that hardware. So you might rarely see the devices supporting Face or Iris authentication. (I have read it on the internet that presently just Pixel 4 supports the face authentication feature but soon more devices will come into the market.)

In the figure print library, we were required to design our own custom UI for the prompt screen. The biometric library provides us with a native UI for the prompt screen. On one hand, it saves the developer’s time and energy to design the UI, it provides us with limited modifications to its native UI. We can only modify the title, subtitle, and the text on the negative button without changing the dialog’s design. We can’t set properties on the prompt that aren’t exposed through its Builder — the UI is provided by the system and is designed to be uniform throughout all apps. This is sort of the main point of this API, this way the user becomes familiar with the prompt and knows that whatever they’re interacting with is safe to use.

To sum up, the following are the limitation of the biometric library :

There is no biometric support for devices below API level 23 (or Android 6).

If the device supports multiple biometrics, the user can‘t specify a default/preferred method in the settings. The biometrics library doesn’t give us the right to choose what method of authentication can be used like fingerprint, face, or iris. It will just confirm if authentication is a success or failure.

It provides us with limited modifications to its native UI. We can only modify the title, subtitle, and the text on the negative button without changing the dialog’s native UI.

Enough talk, let's get started :

We need to add the following dependency to our app’s build.gradle file

dependencies {

implementation 'androidx.biometric:biometric:1.0.1'

}

Now we need to check this through our code if the authentication via biometric is possible or not. The following situations are to be checked :

The device is running Android 6.0 or higher

The device features a fingerprint sensor

The user has granted your app permission to access the fingerprint sensor.

The user has registered at least one fingerprint on their device.

Seems like a trouble, but our library comes to rescue for this

val biometricManager = BiometricManager.from(this)

when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {

BiometricManager.BIOMETRIC_SUCCESS ->

Log.d("MY_APP_TAG", "App can authenticate using biometrics.")

BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->

Log.e("MY_APP_TAG", "No biometric features available on this device.")

BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE ->

Log.e("MY_APP_TAG", "Biometric features are currently unavailable.")

BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {

// Prompts the user to create credentials that your app accepts.

val enrollIntent = Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply {

putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,

BIOMETRIC_STRONG or DEVICE_CREDENTIAL)

}

startActivityForResult(enrollIntent, REQUEST_CODE)

}

}

Once you have checked if the user can authenticate using biometric, we can use the following code to show the biometric prompt screen on the device.



private lateinit var biometricPrompt: BiometricPrompt

private lateinit var promptInfo: BiometricPrompt.PromptInfo



override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_login)

executor = ContextCompat.getMainExecutor(this)

biometricPrompt = BiometricPrompt(this, executor,

object : BiometricPrompt.AuthenticationCallback() {

override fun onAuthenticationError(errorCode: Int,

errString: CharSequence) {

super.onAuthenticationError(errorCode, errString)

Toast.makeText(applicationContext,

"Authentication error: $errString", Toast.LENGTH_SHORT)

.show()

}



override fun onAuthenticationSucceeded(

result: BiometricPrompt.AuthenticationResult) {

super.onAuthenticationSucceeded(result)

Toast.makeText(applicationContext,

"Authentication succeeded!", Toast.LENGTH_SHORT)

.show()

}



override fun onAuthenticationFailed() {

super.onAuthenticationFailed()

Toast.makeText(applicationContext, "Authentication failed",

Toast.LENGTH_SHORT)

.show()

}

})



promptInfo = BiometricPrompt.PromptInfo.Builder()

.setTitle("Biometric login for my app")

.setSubtitle("Log in using your biometric credential")

.setNegativeButtonText("Use account password")

.build()



// Prompt appears when user clicks "Log in".

// Consider integrating with the keystore to unlock

// if needed by your app.

val biometricLoginButton =

findViewById<Button>(R.id.biometric_login)

biometricLoginButton.setOnClickListener {

biometricPrompt.authenticate(promptInfo)

}

} private lateinit var executor: Executorprivate lateinit var biometricPrompt: BiometricPromptprivate lateinit var promptInfo: BiometricPrompt.PromptInfooverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_login)executor = ContextCompat.getMainExecutor(this)biometricPrompt = BiometricPrompt(this, executor,object : BiometricPrompt.AuthenticationCallback() {override fun onAuthenticationError(errorCode: Int,errString: CharSequence) {super.onAuthenticationError(errorCode, errString)Toast.makeText(applicationContext,"Authentication error: $errString", Toast.LENGTH_SHORT).show()override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {super.onAuthenticationSucceeded(result)Toast.makeText(applicationContext,"Authentication succeeded!", Toast.LENGTH_SHORT).show()override fun onAuthenticationFailed() {super.onAuthenticationFailed()Toast.makeText(applicationContext, "Authentication failed",Toast.LENGTH_SHORT).show()})promptInfo = BiometricPrompt.PromptInfo.Builder().setTitle("Biometric login for my app").setSubtitle("Log in using your biometric credential").setNegativeButtonText("Use account password").build()// Prompt appears when user clicks "Log in".// Consider integrating with the keystore to unlock cryptographic operations // if needed by your app.val biometricLoginButton =findViewById (R.id.biometric_login)biometricLoginButton.setOnClickListener {biometricPrompt.authenticate(promptInfo)

Once you implemented this, you are good to go.