Authentication in layman’s terms is the process of proving something to be valid. When we have a computer system and a designated end-user, we will want to assert the identity of that end-user. We accomplish this process by authentication. Authentication methods vary across programming languages, but in this article we will like to shed some light on Android authentication using Kotlin. Biometrics is the most common means of asserting the identity of an end-user. Therefore, we will guide you on how to authenticate fingerprints using plain old Kotlin.
Prerequisites
- Kotlin
- Code editor (preferably Android Studio)
- Coffee
Aim
This tutorial guides the reader on how to authenticate the identity of a user using their fingerprint.
Note: we will use a physical emulator that already serves as a fingerprint manager, so we will not implement that aspect in our project.
Creating a New Project
Firstly, we must create a new Kotlin project in our code editor. To do this, we will follow the steps below:
- We will start by selecting a new project in our Android Studio.
- After, we want a simple template; thus, we will select empty activity as our project template.
- Then we will need to set a name for our application. You can use any name of your choice, but we will call it FingerprintAuth for the tutorial’s sake.
- Finally, click on finish, and you are good to go!
We want to start by designing the user interface, but before proceeding, we will add the biometric dependency to our build.gradle(:app) file.
implementation("androidx.biometric:biometric:1.2.0-alpha04")
We want to add a button and text for our user interface. To do this, we will start by clearing the default Textview and add a button to our activity_main.xml file. Below is the implementation of the button:
<Button android:id="@+id/Authbtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Authenticate Now" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />
In Kotlin, we can add buttons also using a drag and drop method. In order to do this, head to the design section in your activity_main.xml, select palette, and you will see Android widgets to drag and drop into your design.
Next, we will add a Textview into our xml code and within it, we will specify its name, text size, and constraints. Below is the implementation of the Textview:
<TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Auth status" android:textColor="@color/white" android:textSize="25dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/Authbtn" />
Here is what our user interface looks like at the moment:
Binding the XML Layout
Some time ago, app developers widely used Kotlin synthetics to bind Android views, but now it has been deprecated. Also, one of the popular ways to bind views in Kotlin is using databinding (an alternative to Viewbinding) and findviewbyid.
This section entails all the functionalities of our project, and to implement them, we will head back to the MainActivity.kt file to set up the biometrics. We start by binding our views in our activity_main.xml file to the MainActivity.kt file. In order to do this, we will use Viewbinding.
Viewbinding is a feature that allows you to seamlessly interact with your views by creating a binding class for each XML layout file present in the module. Below is a step-by-step process on how to implement Viewbinding in Kotlin:
- Head to the build.gradle(:app) file and add the block below to the Android block. After, sync the gradle file.
buildFeatures { viewBinding true }
- Next, in the MainActivity.kt file, create a private lateinit var called binding and give it the type ActivityMainBinding, which is the binding class generated by Viewbinding.
- Finally, we will initialize the binding variable and set the content view. We have the implementation in the code below:
binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root)
Biometric Authentication
We have bound the XML layout to the Kotlin file, and now, we want to implement the functionalities for the biometric authenticator. Following this, we will need to create three classes; Executor, BiometricPrompt, BiometricPrompt.PromptInfo.
Thus, we will start by creating three private lateinit variables for these three classes (executor, biometricprompt, promptinfo), and they are as below:
private lateinit var executor: Executor private lateinit var biometricPrompt: androidx.biometric.BiometricPrompt private lateinit var promptInfo: androidx.biometric.BiometricPrompt.PromptInfo
- Executor — this object executes submitted runnable tasks.
- BiometricPrompt — this class manages the biometric dialog.
- BiometricPrompt.PromptInfo — this method configures how the biometric prompt should appear and behave.
Next, we will initialize the three variables and set a click listener for the button. Thus, once we click the button, it triggers the promptInfo.
We have the code below:
executor = ContextCompat.getMainExecutor(this) biometricPrompt =androidx.biometric.BiometricPrompt(this@MainActivity,executor,object:androidx.biometric.BiometricPrompt.AuthenticationCallback(){ @SuppressLint("SetTextI18n") override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { super.onAuthenticationError(errorCode, errString) binding.Authstatus.text = "Error, $errString" } @SuppressLint("SetTextI18n") override fun onAuthenticationSucceeded(result: androidx.biometric.BiometricPrompt.AuthenticationResult) { super.onAuthenticationSucceeded(result) binding.Authstatus.text = "Successful auth" } @SuppressLint("SetTextI18n") override fun onAuthenticationFailed() { super.onAuthenticationFailed() binding.Authstatus.text = "Authentication Failed" } }) promptInfo= androidx.biometric.BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric Authentication") .setSubtitle("Login using fingerprint or face") .setNegativeButtonText("Cancel") .build() binding.Authbtn.setOnClickListener { biometricPrompt.authenticate(promptInfo) }
Explaining the Code Snippet
- ontextCompat.getMainExecutor — this method returns an executor that will run the enqueued task on the primary thread associated with this context.
- AuthenticationCallback() — this is a nested class within BiometricPrompt, and it contains three public methods:
- onAuthenticationFailed()
- onAuthenticationError(int errorCode, CharSequence errString)
- onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result)
- BiometricPrompt.PromptInfo.Builder() — A builder sets individual options, and in this case, we are setting the following:
Now, our complete code and result for the MainActivity.kt file and project, respectively, will be as below:
package com.example.fingerprintauth import android.annotation.SuppressLint import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import com.example.fingerprintauth.databinding.ActivityMainBinding import java.util.concurrent.Executor class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private lateinit var executor: Executor private lateinit var biometricPrompt: androidx.biometric.BiometricPrompt private lateinit var promptInfo: androidx.biometric.BiometricPrompt.PromptInfo override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) executor = ContextCompat.getMainExecutor(this) biometricPrompt =androidx.biometric.BiometricPrompt(this@MainActivity,executor,object:androidx.biometric.BiometricPrompt.AuthenticationCallback(){ @SuppressLint("SetTextI18n") override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { super.onAuthenticationError(errorCode, errString) binding.Authstatus.text = "Error, $errString" } @SuppressLint("SetTextI18n") override fun onAuthenticationSucceeded(result: androidx.biometric.BiometricPrompt.AuthenticationResult) { super.onAuthenticationSucceeded(result) binding.Authstatus.text = "Successful auth" } @SuppressLint("SetTextI18n") override fun onAuthenticationFailed() { super.onAuthenticationFailed() binding.Authstatus.text = "Authentication Failed" } }) promptInfo= androidx.biometric.BiometricPrompt.PromptInfo.Builder() .setTitle("Biometric Authentication") .setSubtitle("Login using fingerprint or face") .setNegativeButtonText("Cancel") .build() binding.Authbtn.setOnClickListener { biometricPrompt.authenticate(promptInfo) } } }
Conclusion
Authentication in any form is necessary for any programming language when you want to assert the identity of a user. This tutorial has walked you through using authentication, specifically biometric authentication, with Kotlin. Thanks for reading, and happy coding!