Firebase Auth Phone — Complete Guide
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
FirebaseAuthInvalidCredentialsExceptionandTooManyRequestsException. - Implement OTP auto-read with SMS Retriever API or BroadcastReceiver.
- Use
resendTokenfor safe OTP resending.
Common Mistakes with Firebase auth phone
- Forgetting
deriving (Show, Eq)on custom data types needed for debugging - Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
- Using
headandtailinstead 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
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro