Skip to content

Firebase Auth Phone — Complete Guide

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about Firebase Auth Phone. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Phone auth OTP never arrives, reCAPTCHA fails on older devices, or the verification ID expires before the user enters the code.

Wrong Approach ❌

// No timeout handling — OTP may expire
val options = PhoneAuthOptions.newBuilder(auth)
    .setPhoneNumber(phoneNumber)
    .setTimeout(30L, TimeUnit.SECONDS) // Too short!
    .setActivity(this)
    .setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
        override fun onVerificationCompleted(credential: PhoneAuthCredential) {}
        override fun onVerificationFailed(e: FirebaseException) {
            Toast.makeText(this@Activity, e.message, Toast.LENGTH_LONG).show()
        }
    })
    .build()
PhoneAuthProvider.verifyPhoneNumber(options)

Output: OTP expires before user enters it. Raw Firebase error messages.

Right Approach ✅

class PhoneAuthManager(private val activity: Activity) {
    private lateinit var storedVerificationId: String
    private var resendToken: PhoneAuthProvider.ForceResendingToken? = null
    private val auth = FirebaseAuth.getInstance()

    sealed class PhoneAuthState {
        object CodeSent : PhoneAuthState()
        data class Verified(val credential: PhoneAuthCredential) : PhoneAuthState()
        data class Error(val message: String) : PhoneAuthState()
    }

    fun sendOtp(phoneNumber: String, callback: (PhoneAuthState) -> Unit) {
        val options = PhoneAuthOptions.newBuilder(auth)
            .setPhoneNumber(phoneNumber)
            .setTimeout(60L, TimeUnit.SECONDS) // 60 seconds for user to enter code
            .setActivity(activity)
            .setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
                override fun onVerificationCompleted(credential: PhoneAuthCredential) {
                    // Auto-retrieved (instant verification or SMS auto-read)
                    signInWithPhone(credential)
                    callback(PhoneAuthState.Verified(credential))
                }

                override fun onVerificationFailed(e: FirebaseException) {
                    val message = when (e) {
                        is FirebaseAuthInvalidCredentialsException -> "Invalid phone format"
                        is FirebaseTooManyRequestsException -> "Too many attempts. Try later."
                        else -> "Verification failed: ${e.localizedMessage}"
                    }
                    callback(PhoneAuthState.Error(message))
                }

                override fun onCodeSent(
                    verificationId: String,
                    token: PhoneAuthProvider.ForceResendingToken
                ) {
                    storedVerificationId = verificationId
                    resendToken = token
                    callback(PhoneAuthState.CodeSent)
                }
            })
            .build()
        PhoneAuthProvider.verifyPhoneNumber(options)
    }

    fun verifyOtp(otp: String, callback: (PhoneAuthState) -> Unit) {
        val credential = PhoneAuthProvider.getCredential(storedVerificationId, otp)
        signInWithPhone(credential)
        callback(PhoneAuthState.Verified(credential))
    }

    fun resendOtp(phoneNumber: String, callback: (PhoneAuthState) -> Unit) {
        resendToken?.let { token ->
            PhoneAuthProvider.verifyPhoneNumber(
                phoneNumber, 60, TimeUnit.SECONDS, activity,
                object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
                    override fun onCodeSent(id: String, t: PhoneAuthProvider.ForceResendingToken) {
                        storedVerificationId = id
                        resendToken = t
                        callback(PhoneAuthState.CodeSent)
                    }
                    override fun onVerificationCompleted(c: PhoneAuthCredential) {}
                    override fun onVerificationFailed(e: FirebaseException) {
                        callback(PhoneAuthState.Error("Resend failed"))
                    }
                }, token
            )
        }
    }

    private fun signInWithPhone(credential: PhoneAuthCredential) {
        auth.signInWithCredential(credential)
    }
}

// SMS auto-read (API 23+)
// Set editText inputType to number, listen for SMS via BroadcastReceiver

Output: Phone auth works with proper OTP sending, verification, and resend.

Prevention

  • Use 60+ second timeout for OTP entry.
  • Handle FirebaseAuthInvalidCredentialsException and TooManyRequestsException.
  • Implement OTP auto-read with SMS Retriever API or BroadcastReceiver.
  • Use resendToken for safe OTP resending.

Common Mistakes with Firebase auth phone

  1. Forgetting deriving (Show, Eq) on custom data types needed for debugging
  2. Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
  3. Using head and tail instead of pattern matching, causing runtime errors on empty lists

These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### Why doesn't the OTP arrive?

Check: (1) Phone number format (E.164: +1XXXXXXXXXX), (2) SIM card in the device, (3) SMS is not blocked, (4) reCAPTCHA verification is passing.

### What is reCAPTCHA and why is it shown?

Firebase uses reCAPTCHA to prevent abuse on devices that don't support safety net. It's shown on the first verification. On subsequent verifications, the token is cached.

### How do I auto-read OTP from SMS?

Use the SMS Retriever API (Google Play Services) or SMS User Consent API. Match the 6-digit code from the SMS pattern and auto-fill the OTP input field.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro