2-Factor Authentication (Email/Password & Phone with Firebase Authentication on Android

We’ve all seen it at least once before: that little extra layer of security when we create an account on Gmail, Netflix, and possibly on every login into our online bank accounts. A verification code is sent to our device which we have to enter to get past that glorious second layer.

If you want this in your app and you’re having trouble finding out how, look no further. I’m here to tell you it’s piss easy and you can get it set up in a minute… well maybe a bit more than that, but not much more.

This variant of the verification code uses Firebase Authentication, a service from Firebase which lets you authenticate through different methods and providers, one of which is Phone Verification.

If you’ve never encountered Firebase before, don’t you worry fam. This tutorial is designed for the dummy who’s never encountered it…. no offense.

Add Firebase Authentication to your App

I explain this in slightly more depth here, but I’ll list a quick way to do this here that’ll barely take a minute. If you’re not yet connected to Firebase:

  1. Open the Firebase Assistant by going to Tools > Firebase
  2. Click on the Analytics Menu
  3. Click Connect to Firebase and choose to either create a new Firebase project or connect to an existing one.
  4. Click the button below the previous one to add the Firebase Core dependencies to your  build.gradle files.
  5. Go back to the Firebase Assistant home, go to Authentication and add the Authentication dependencies from there.

Or if you’re already connected to Firebase, you can just add this dependency to your app/build.gradle file:

implementation 'com.google.firebase:firebase-auth:16.1.0'

Now you’re good to go (ง ͠° ͟ل͜ ͡°)ง

A few things to note

You can use Firebase Authentication for a complete authentication solution, but you’ll want to pair up phone verification with any of its other sign-in methods (Recommended: the standard Email/Password) to keep your app well secure.

Otherwise, if you just want to use Firebase Auth for the phone verification, you can let the user sign-in through this way then pass the Firebase Authentication Token to your own Authentication Provider.

Enable Phone Verification on the Console

Head to the Firebase Console, open up your project there and go into Authentication (on the sidebar). Under Sign-In Method, enable ‘Phone’.

Initialise Firebase Auth

val auth = FirebaseAuth.getInstance()

Dump this as a global variable in your activity.

Send Verification Code to User’s Phone

Create an interface for your user to submit their phone number. For legal reasons or just best practice standards, inform your users if they use phone verification that they’ll be receiving an SMS for it and that standard rates apply.

Then pass in their phone number to PhoneAuthProvider.verifyPhoneNumber with a suitable timeout for your app.

        phoneNumber,      // Phone number to verify
        60,               // Timeout duration
        TimeUnit.SECONDS, // Unit of timeout
        this,             // Activity (for callback binding)
        callbacks) // OnVerificationStateChangedCallbacks

*Calling verifyPhoneNumber multiple times won’t trigger it while a request still hasn’t timed out. 

Receiving Verified Code and Creating a Credential

Notice in the verifyPhoneNumber method, you pass in a callbacks object.

callbacks = object: PhoneAuthProvider.OnVerificationStateChangedCallbacks() {...}

This has 3 important methods you’ll be overriding:


override fun onCodeSent(verificationId: String?, token: PhoneAuthProvider.ForceResendingToken) {
    // The SMS verification code has been sent to the provided phone number, we
    // now need to ask the user to enter the code and then construct a credential
    // by combining the code with a verification ID.
    Log.d(TAG, "onCodeSent:" + verificationId!!)

    // Save verification ID and resending token so we can use them later
    storedVerificationId = verificationId
    resendToken = token

    // ...

When the code is sent, you’ll get a verificationId.

val credential = PhoneAuthProvider.getCredential(verificationId!!, code)

You’ll be able to combine this and the verification code sent to the user’s phone to create a credential which you can use to authenticate the user.


override fun onVerificationCompleted(credential: PhoneAuthCredential) {
    // This callback will be invoked in two situations:
    // 1 - Instant verification. In some cases the phone number can be instantly
    //     verified without needing to send or enter a verification code.
    // 2 - Auto-retrieval. On some devices Google Play services can automatically
    //     detect the incoming verification SMS and perform verification without
    //     user action.
    Log.d(TAG, "onVerificationCompleted:$credential")


In the two situations listed above, verification will happen in the background (so your user doesn’t need to manually enter the verification code) and you get a credential straight like that. Wow.


override fun onVerificationFailed(e: FirebaseException) {
    // This callback is invoked in an invalid request for verification is made,
    // for instance if the the phone number format is not valid.
    Log.w(TAG, "onVerificationFailed", e)

    if (e is FirebaseAuthInvalidCredentialsException) {
        // Invalid request
        // ...
    } else if (e is FirebaseTooManyRequestsException) {
        // The SMS quota for the project has been exceeded
        // ...

    // Show a message and update the UI
    // ...

Because ? happens.

Signing in with the Credential

    .addOnCompleteListener(this) { task ->
        if (task.isSuccessful) {
            // Sign in success, update UI with the signed-in user's information
            Log.d(TAG, "signInWithCredential:success")

            val user = task.result?.user
            // ...
        } else {
            // Sign in failed, display a message and update the UI
            Log.w(TAG, "signInWithCredential:failure", task.exception)
            if (task.exception is FirebaseAuthInvalidCredentialsException) {
                // The verification code entered was invalid

After running this method, your user might just be signed in. Hooray! Hold your horses though, now we’re going to combine this with other Authentication methods to meet our security standards (Firebase or not).

Linking Phone Verification with other Sign-In Methods

Option 1: Firebase Email/Password Auth

By no regular standards should 2-factor authentication be possible with Firebase Authentication, but we’re going to need some help from the Database (or Firestore if that’s your thing).

Normally, signing-in through different methods gives the user different user IDs or “accounts” should I say. To optimize things, Firebase lets you link multiple providers to authenticate to the same account, and that’s done through the simple method:


Doing so links together the credentials from the different providers. Now when the user adds his phone number, we link it to the current account and add it to the database.

private fun enableTwoFactorAuthentication() {
            .addOnSuccessListener { result ->
                val userRef = database.child("users/${result.user}")

Now when the user signs in, we check the database if two-factor is enabled, then we immediately sign out and send the phone verification.

auth.signInWithEmailAndPassword(email, password).addOnSuccessListener {
    database.child("users/${it.user.uid}").addListenerForSingleValueEvent(object: ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            val twoFactorEnabled = snapshot.child("twoFactorEnabled").value as Boolean
            val phoneNumber = snapshot.child("twoFactorEnabled").value as String

            if (twoFactorEnabled) {

And voila! A two-factor authentication solution.

Now keep in mind this isn’t a perfect solution. It isn’t two-factor authentication in its purest form as it’s performed through a series of UI gimmicks. It does still do the trick though. It should increase your app’s security a fair bit and it’s so much quicker to implement than “true” two-factor solutions out there.

Option 2: Your own Authentication Server.

The best way to do things here is to get the Authentication Token of your authenticated user (this string is unique to each user and a new one can be generated upon request, like a hash fingerprint) and pass it to your own servers to identify the user authenticated on Firebase.