Phone Number Verification via SMS in Android Using Firebase

Phone Number Verification via SMS in Android Using Firebase

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 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 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:

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:

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:

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:

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:

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.

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:

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:

PhoneAuthProvider.getInstance()
 .verifyPhoneNumber(phoneNumber, 60, TimeUnit.SECONDS, this, phoneCallbacks, resendToken)

Other Resources

To learn more about Firebase phone authentication, check out the official docs. For more on the PhoneAuthProvider class, you can also head over to those docs here.


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.