Phone Number Verification via SMS in Android Using Firebase
Tags: androidappdevelopement, kotlin, firebase, userauthentification
Published: 2021-10-22 20:18:51

Validating identity is incredibly important in mobile development, and assuring new user signups are real human beings is critical. Developers need reliable ways to confirm the identities of their users to prevent security issues.

Developers can implement verification systems using phone numbers in two main ways: either by calling or by sending an SMS containing a code that the user must input.

But implementing such a system from scratch can be tedious and time-consuming, given that there are a lot of components that need to be established. Luckily, [Firebase phone authentication](https://firebase.google.com/docs/auth/android/phone-auth) offers such a system for Android and iOS, along with free quota for SMS verification messages (10,000 free per month).

In this article, we’ll implement SMS phone verification in Android using this tool from Firebase.

>*This article presumes that you have some experience in building Android apps in Kotlin.*

#Project Setup
First, we need to set up a Firebase project. If you’re new to Firebase [here’s a quick tutorial](https://firebase.google.com/docs/android/setup) to show you how to set up a project and link it with your application. So start by going to the console and creating a new project. After that, under the develop section, choose authentication, and then from the main screen, choose Sign-in Methods to enable the phone sign-in provider.

You’ll also need to install a dependency to use Firebase authentication services:
```kotlin
implementation ‘com.google.firebase:firebase-auth:19.0.0’
``` 

#Initialzation
Now go to your desired activity where you want phone verification to happen. Here, we start by initializing the instance with some parameters:
``` kotlin
var auth = FirebaseAuth.getInstance() auth.setLanguageCode(Locale.getDefault().language)
```

We need to create an instance of `FirebaseAuth` and initialize it with the `FirebaseAuth.getInstance()` method. You can set the language of the SMS text that will be sent with the `setLanuageCode()` method.

We’ll also need two more global variables that will help us save the validation token and then resend token—the former will be used when the user enters the code within the SMS to be validated by the Firebase backend, and the latter is an optional one we’ll use when we want to resend the same SMS if the user doesn’t receive any message (due to various problems). Here are those variables:
```kotlin
private var storedVerificationId: String? = null 
private lateinit var resendToken: PhoneAuthProvider.ForceResendingToken
```
#Send SMS
To send the SMS, we call a method called `verifyPhoneNumber()` from the instance of the `PhoneAuthProvider`, which takes several parameters. The most important one is the phone number. The first step is to set up a mechanism to retrieve a user’s phone number. When you have this parameter, here’s the code to send an SMS:
```kotlin
PhoneAuthProvider.getInstance().verifyPhoneNumber(phoneNumber, 60, TimeUnit.SECONDS, this, phoneCallbacks)
```
In the above code, you can see multiple parameters are passed to the `verifyPhoneNumber()` methods:
* The `60` is a timeout parameter, which is the maximum amount of time you’re willing to wait for SMS auto-retrieval to be completed by the library.
* Then the `Unit` in which our timeout is expressed (in this case, seconds).
* `this` represents the activity to which the callbacks are scoped.
* The final parameter is the callbacks, which are discussed next, in which we’re going to create a variable called `phoneCallbacks` to be passed in the `verifyPhoneNumber()` method.

#Callbacks
After that `verifyPhoneNumber()` is called, we need to handle the result of this request. For this, we need to create an instance of an abstract class `calledOnVerificationStateChangedCallbacks`, implement its methods, and then pass this instance to the previous method `verifyPhoneNumber()`.

In Kotlin (as in many other languages), you can’t inherit multiple classes, but you can implement many interfaces. Since we can’t extend this abstract class in our activity (because it’s already inherited by the `AppCompactActivity`), we can refactor our code to make it look cleaner.

To do this, we can create a separate class called `PhoneCallbacks` in which we also create an interface, and extend the previous abstract class using the newly created interface:
```kotlin
import com.google.firebase.FirebaseException
import com.google.firebase.auth.PhoneAuthCredential
import com.google.firebase.auth.PhoneAuthProvider

class PhoneCallbacks(private val listener : PhoneCallbacksListener) : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

    interface PhoneCallbacksListener {
        fun onVerificationCompleted(credential: PhoneAuthCredential?)
        fun onVerificationFailed(exception: FirebaseException?)
        fun onCodeSent(
            verificationId: String?,
            token: PhoneAuthProvider.ForceResendingToken?
        )
    }

    override fun onVerificationCompleted(phoneAuthCredential: PhoneAuthCredential?) {
        listener.onVerificationCompleted(phoneAuthCredential)
    }

    override fun onVerificationFailed(exception: FirebaseException?) {
        listener.onVerificationFailed(exception)
    }

    override fun onCodeSent(verificationId: String?, token: PhoneAuthProvider.ForceResendingToken?) {
        listener.onCodeSent(verificationId,token)
    }
}
```
With that, we create an instance of the `PhoneCallbacks` class in our desired activity, and let this activity implement the `PhoneCallbacksListener`:
`val phoneCallbacks = PhoneCallbacks(this)`

The `PhoneCallbacksListener` interface contains 3 methods:
* `onCodeSent` triggers when the SMS is sent to the user's phone, it will include a verification ID and the resend token that needs to be saved. So in this stage, we should save both of them in the global variables that we made earlier.

* `onVerficationCompleted` will be triggered when an SMS is auto-retrieved or the phone number has been instantly verified. This basically means that the result of the verification is here, and we need just to check if it was successful or not and then proceed normally.

* `onVerficationFaild` is triggered when an error occurs during phone number verification. This can happen for multiple reasons (internet connection issues, mobile network issues, Firebase issues, and many more). This method will be helpful for debugging if a problem occurs.

Here’s an implementation sample —we’ll return to the `signInWithPhoneAuthCredntial()` method shortly.
```kotlin
override fun onVerificationCompleted(credential: PhoneAuthCredential?) {
        if (credential != null && storedVerificationId != null)
            signInWithPhoneAuthCredential(credential)
        else {
            Toast.makeText(
                this,
                "Something went wrong, please try later.",
                Toast.LENGTH_LONG).show()
            finish()
        }
    }


override fun onVerificationFailed(exception: FirebaseException?) {
        Toast.makeText(
            this, "Something went wrong, try again later please.",
            Toast.LENGTH_LONG).show()
        finish()
}

override fun onCodeSent(
        verificationId: String?,
        token: PhoneAuthProvider.ForceResendingToken?
    ) {
        storedVerificationId = verificationId!!
        resendToken = token!!
    }
```
#Verify the Code
In the `OnVerificationCompleted` method implemented in the activity, we need to make an action after the verification (successful or unsuccessful). To make the code cleaner we can define a method called `signInWithPhoneAuthCredntial()` that takes the `PhoneAuthCredential` parameter and completes the required verification.

Based on that check you can either redirect the user or do whatever you’d like if it was successful. Otherwise you can ask the user to re-enter the code or something similar. Here’s an implementation sample of this code verification process:

```kotlin
private fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential) {
        auth.signInWithCredential(credential)
            .addOnCompleteListener(this) {
                if (it.isSuccessful) {
                  // Successful mean that the phone is verified, so do whatever you want here.
                } else {
                    Toast.makeText(
                        this,
                        "Something went wrong, try again later please.",
                        Toast.LENGTH_LONG).show()
                }
            }
    }
```
#Resend Feature
Under some circumstances, the SMS might not be sent, so it’s essential that we have a way to resend the SMS. Some apps create an option called resend disable it at the beginning. 30 seconds or so after sending the first SMS, they enable it so the user can request a new code if they didn’t get the first SMS.

To make such a feature in our app, you need to set up a well-designed user interface to make it easier for the user to resend the SMS (I made simple resend button), When the user taps this resend button, we call again the `verifyPhoneNumber()` method with all the previous parameters (phone number, timeout, etc..). But this time we use the resend token, which is stored in `resentToken` variable that we mentioned in the initialization step. Here’s the code snippet:
```kotlin
PhoneAuthProvider.getInstance()
 .verifyPhoneNumber(phoneNumber, 60, TimeUnit.SECONDS, this, phoneCallbacks, resendToken)
```
#Other Resources 
To learn more about Firebase phone authentication, check out the official [docs](https://firebase.google.com/docs/auth/android/phone-auth). For more on the `PhoneAuthProvider` class, you can also head over to those docs [here](https://firebase.google.com/docs/reference/android/com/google/firebase/auth/PhoneAuthProvider.html).

****

Finally, I want to mention again that Firebase phone verification allows just 10,000 SMS per month—otherwise, you have to pay if you exceed this quota.

I hope you enjoyed this article. Keep in mind that there are other services like Facebook that also provide phone verification that can be used in multiple ways. So experiment it and see what fits your solution and platform the best.