Firebase Phone Auth

Firebase Phone Authentication verifies users by sending a one-time code to their mobile number via SMS. The user enters that code in your app, and Firebase confirms their identity. This method works especially well for mobile apps and regions where email accounts are less common.

How SMS Verification Works

Think of phone auth as a combination lock delivered by postal mail. You request the code (send a letter). The code arrives at the physical address (SMS to the phone). You enter the code (confirm you received it). The lock opens (Firebase grants access).

User enters phone number: +91 98765 43210
           |
           v
Firebase sends 6-digit OTP via SMS
           |
           v
User reads SMS and enters the code in your app
           |
           v
Firebase verifies the code
           |
           v
Valid? YES --> Creates or logs in user
Valid? NO  --> Returns error (wrong code / expired)

Enabling Phone Auth in the Console

Go to Authentication > Sign-in method in the Firebase Console. Click Phone, toggle Enable on, and click Save. Phone auth is now active for your project.

Note: Phone authentication is available on the Blaze (pay-as-you-go) plan. The Spark (free) plan has limited SMS quotas for testing purposes. Each SMS verification costs a small amount in production, varying by country.

reCAPTCHA Verification

Phone auth requires a reCAPTCHA verification to prevent bots from abusing your SMS quota. Firebase handles reCAPTCHA automatically using a widget called RecaptchaVerifier. You need a visible button or container in your HTML for the reCAPTCHA widget to attach to.

<!-- The reCAPTCHA widget renders inside this button -->
<button id="send-otp-btn">Send OTP</button>
<input id="phone-input" type="tel" placeholder="+91XXXXXXXXXX" />
<input id="otp-input" type="text" placeholder="Enter OTP" />
<button id="verify-btn">Verify</button>
<p id="phone-message"></p>

Step 1 — Setting Up RecaptchaVerifier

import {
  RecaptchaVerifier,
  signInWithPhoneNumber
} from "firebase/auth";
import { auth } from "./firebase";

let confirmationResult;

// Set up reCAPTCHA — run this when the page loads
function setupRecaptcha() {
  window.recaptchaVerifier = new RecaptchaVerifier(
    auth,
    "send-otp-btn",  // ID of the button element
    {
      size: "invisible", // or "normal" for a visible checkbox
      callback: () => {
        // reCAPTCHA solved — proceed with sending OTP
        sendOTP();
      }
    }
  );
}

setupRecaptcha();

The invisible size runs reCAPTCHA silently in the background. Most users never see a challenge. The normal size shows a checkbox that users must tick — useful as a fallback for high-risk environments.

Step 2 — Sending the OTP

async function sendOTP() {
  const phoneNumber = document.getElementById("phone-input").value;
  const appVerifier = window.recaptchaVerifier;
  const message = document.getElementById("phone-message");

  try {
    confirmationResult = await signInWithPhoneNumber(
      auth, phoneNumber, appVerifier
    );
    message.textContent = "OTP sent! Check your SMS.";
    document.getElementById("verify-btn").style.display = "block";
  } catch (error) {
    message.textContent = "Error: " + error.message;
    // Reset reCAPTCHA on failure
    window.recaptchaVerifier.render().then((widgetId) => {
      window.recaptchaVerifier.reset(widgetId);
    });
  }
}

document.getElementById("send-otp-btn").addEventListener("click", sendOTP);

Firebase sends the OTP to the number and returns a confirmationResult object. Store this object — you need it in the next step to verify the code the user enters.

Step 3 — Verifying the OTP

document.getElementById("verify-btn").addEventListener("click", async () => {
  const otp = document.getElementById("otp-input").value;
  const message = document.getElementById("phone-message");

  try {
    const result = await confirmationResult.confirm(otp);
    const user = result.user;
    message.textContent = "Verified! UID: " + user.uid;
  } catch (error) {
    if (error.code === "auth/invalid-verification-code") {
      message.textContent = "Wrong code. Please try again.";
    } else if (error.code === "auth/code-expired") {
      message.textContent = "Code expired. Request a new one.";
    } else {
      message.textContent = "Verification failed: " + error.message;
    }
  }
});

Phone Number Format

Firebase requires phone numbers in E.164 international format: a plus sign, country code, and the number with no spaces or dashes.

Correct format examples:
+919876543210   (India)
+14155552671    (United States)
+447911123456   (United Kingdom)

Incorrect formats:
09876543210     (missing country code)
+91 98765 43210 (spaces not allowed)

Validate and format the phone number before sending it to Firebase. You can use a library like libphonenumber-js to handle international formatting reliably.

Testing Phone Auth Without Real SMS

Firebase provides test phone numbers so you can develop and test without sending real SMS messages and incurring charges. Go to Authentication > Sign-in method > Phone and scroll to Phone numbers for testing.

Add a test number and a fixed test OTP. For example:

Test phone: +1 650-555-1234
Test OTP: 123456

When your app sends an OTP to this number, Firebase never sends a real SMS. It silently accepts the test OTP you defined. This saves money during development and speeds up testing.

Phone Auth Error Codes

  • auth/invalid-phone-number — the phone number format is wrong
  • auth/too-many-requests — too many attempts from this number or device
  • auth/quota-exceeded — SMS quota for the project is exhausted
  • auth/invalid-verification-code — the OTP entered is incorrect
  • auth/code-expired — OTP validity period (5 minutes) has passed

Key Takeaway

Firebase Phone Authentication uses SMS OTPs to verify users in three steps: set up RecaptchaVerifier, call signInWithPhoneNumber to send the code, then call confirmationResult.confirm with the code the user enters. Always use international E.164 phone format. Set up test phone numbers in the console to avoid SMS charges during development.

Leave a Comment

Your email address will not be published. Required fields are marked *