Introduction to Firebase Auth Troubleshooting
Firebase Authentication is one of the most widely used authentication services in modern web and mobile development, powering millions of applications with email/password logins, OAuth providers, phone authentication, and anonymous sign-in. However, like any complex system, it can fail in subtle and frustrating ways. Whether you are dealing with cryptic error codes, token refresh failures, or sign-in flows that mysteriously break in production, understanding how to diagnose and fix these issues is an essential skill for any developer working with Firebase.
This tutorial walks through the most common Firebase Auth problems, explains why they happen, and provides practical, copy-paste-ready solutions. By the end, you will have a troubleshooting toolkit that covers configuration errors, runtime exceptions, token management, and production-only failures.
Why Troubleshooting Firebase Auth Matters
Authentication is the gateway to your application. When it breaks, users cannot access their accounts, data becomes inaccessible, and trust erodes quickly. Unlike UI bugs that might be cosmetic, auth failures are often total blockers. Worse, many Firebase Auth issues only surface in specific environments — for example, they may work perfectly on localhost but fail on a deployed domain because of authorized domain restrictions or OAuth redirect mismatches.
A systematic approach to troubleshooting saves hours of trial and error. Instead of randomly changing configuration values, you will learn to read error messages precisely, inspect network requests, and validate your Firebase project setup against the requirements of each auth method.
Common Issue 1: "auth/configuration-not-found" and Invalid Config
The Problem
This error typically appears immediately after calling signInWithEmailAndPassword or any auth method. It means Firebase cannot find a valid authentication configuration for your project. The most common cause is a mismatch between your Firebase config object and the actual project, or that Authentication has not been enabled in the Firebase console.
The Solution
First, verify that Authentication is enabled in your Firebase Console under the Authentication section. Then, double-check your config object:
// Correct Firebase initialization
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
apiKey: "AIzaSyXXXXXXXXXXXXXXXXXXXX",
authDomain: "your-project.firebaseapp.com",
projectId: "your-project",
storageBucket: "your-project.appspot.com",
messagingSenderId: "1234567890",
appId: "1:1234567890:web:abcdef123456"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
Common mistakes include copying the config from a different project, using the legacy measurementId as the appId, or forgetting to call initializeApp before accessing auth. Always copy the config directly from Project Settings in the Firebase console.
Common Issue 2: "auth/unauthorized-domain" on Redirect Sign-In
The Problem
When using signInWithRedirect or OAuth providers like Google, GitHub, or Apple, you may see an error stating that the domain is not authorized. This happens because Firebase maintains an allowlist of domains permitted to perform authentication redirects. localhost is authorized by default, but production domains must be added manually.
The Solution
Navigate to Firebase Console → Authentication → Settings → Authorized domains and add your production domain. For example, if your app is hosted at https://myapp.example.com, add myapp.example.com to the list. Do not include the protocol or path.
// Example of a redirect-based sign-in that requires authorized domains
import { getAuth, GoogleAuthProvider, signInWithRedirect, getRedirectResult } from "firebase/auth";
const auth = getAuth();
const provider = new GoogleAuthProvider();
// Trigger redirect
signInWithRedirect(auth, provider);
// Handle result on page load after redirect
getRedirectResult(auth)
.then((result) => {
if (result.user) {
console.log("Signed in as:", result.user.displayName);
}
})
.catch((error) => {
console.error("Redirect error:", error.code, error.message);
});
If you are deploying to multiple environments (staging, production, preview branches), remember to add each unique domain. Firebase Hosting preview channels generate unique URLs that must be authorized individually.
Common Issue 3: "auth/invalid-email" and "auth/wrong-password" Errors
The Problem
These are user-facing validation errors. auth/invalid-email fires when the email string is malformed, while auth/wrong-password fires when the password does not match the stored credential. The challenge is presenting these errors to users in a helpful way rather than raw error codes.
The Solution
Always wrap auth calls in try/catch blocks and map error codes to human-readable messages:
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
const auth = getAuth();
async function login(email, password) {
try {
const userCredential = await signInWithEmailAndPassword(auth, email, password);
return { success: true, user: userCredential.user };
} catch (error) {
const errorMessages = {
"auth/invalid-email": "Please enter a valid email address.",
"auth/user-disabled": "This account has been disabled.",
"auth/user-not-found": "No account found with this email.",
"auth/wrong-password": "Incorrect password. Please try again.",
"auth/too-many-requests": "Too many attempts. Try again later.",
"auth/invalid-credential": "Invalid email or password."
};
const message = errorMessages[error.code] || "An unexpected error occurred. Please try again.";
return { success: false, error: message, code: error.code };
}
}
Note that Firebase has consolidated some error codes in recent SDK versions. In newer versions, both auth/user-not-found and auth/wrong-password may return as auth/invalid-credential for security reasons, to prevent user enumeration attacks. Always test against the SDK version you are using.
Common Issue 4: Token Refresh Failures
The Problem
Firebase ID tokens expire after one hour. The SDK automatically refreshes them, but sometimes the refresh fails silently. This manifests as API calls returning 401 Unauthorized, or the user appearing logged in on the client but rejected by your backend. Common causes include revoked sessions, deleted users, or network issues during refresh.
The Solution
Use an auth state listener to detect when the user session becomes invalid, and force a token refresh when making backend calls:
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
// Listen for auth state changes
onAuthStateChanged(auth, (user) => {
if (user) {
console.log("User is signed in:", user.uid);
} else {
console.log("User is signed out or session expired");
// Redirect to login or show sign-in screen
}
});
// Force refresh token before calling your backend
async function callBackendApi() {
const user = auth.currentUser;
if (!user) {
throw new Error("No authenticated user");
}
try {
const token = await user.getIdToken(true); // true forces refresh
const response = await fetch("https://your-api.example.com/data", {
headers: {
"Authorization": `Bearer ${token}`
}
});
if (response.status === 401) {
// Token may be revoked; sign out and redirect
await auth.signOut();
throw new Error("Session expired. Please sign in again.");
}
return response.json();
} catch (error) {
console.error("API call failed:", error);
throw error;
}
}
The true argument in getIdToken(true) forces a refresh regardless of token age. Use this sparingly, as it counts against your refresh token quota. For routine calls, call getIdToken() without arguments, which returns the cached token if still valid.
Common Issue 5: Email Verification Not Working
The Problem
You send a verification email using sendEmailVerification, but the user never receives it, or clicking the link does not verify the account. This often stems from incorrect action URL configuration, email deliverability issues, or the user being signed out when clicking the verification link.
The Solution
Customize the action URL and handle the email verification redirect properly:
import { getAuth, sendEmailVerification, applyActionCode } from "firebase/auth";
const auth = getAuth();
// Send verification email with custom action URL
async function sendVerification() {
const user = auth.currentUser;
if (!user) return;
try {
await sendEmailVerification(user, {
url: "https://your-app.example.com/verify-complete",
handleCodeInApp: false
});
console.log("Verification email sent");
} catch (error) {
console.error("Failed to send verification:", error.code);
}
}
// Handle the verification redirect on your landing page
async function handleVerificationRedirect(actionCode) {
try {
await applyActionCode(auth, actionCode);
console.log("Email verified successfully");
// Update UI or redirect user
} catch (error) {
console.error("Verification failed:", error.code);
}
}
If emails are not arriving, check the Firebase Console under Authentication → Templates to verify the sender domain. For production apps, consider configuring a custom SMTP provider instead of the default Firebase email service, which has strict rate limits and may land in spam folders.
Common Issue 6: Anonymous Auth Upgrade Issues
The Problem
Anonymous auth lets users try your app without signing up. When they later sign in with a permanent credential, you want to preserve their anonymous data. However, naive implementations either create a duplicate account or lose the anonymous user's data.
The Solution
Use credential linking to upgrade an anonymous user to a permanent account:
import {
getAuth,
signInAnonymously,
EmailAuthProvider,
linkWithCredential
} from "firebase/auth";
const auth = getAuth();
// Step 1: Sign in anonymously
async function startAnonymous() {
try {
const result = await signInAnonymously(auth);
console.log("Anonymous UID:", result.user.uid);
} catch (error) {
console.error("Anonymous sign-in failed:", error.code);
}
}
// Step 2: Upgrade to permanent account
async function upgradeToPermanent(email, password) {
const user = auth.currentUser;
if (!user || !user.isAnonymous) {
console.error("No anonymous user to upgrade");
return;
}
try {
const credential = EmailAuthProvider.credential(email, password);
const result = await linkWithCredential(user, credential);
console.log("Upgraded successfully. UID preserved:", result.user.uid);
// The UID stays the same, so all anonymous data is preserved
} catch (error) {
if (error.code === "auth/email-already-in-use") {
console.error("This email is already registered. Consider account merging.");
} else {
console.error("Upgrade failed:", error.code);
}
}
}
The key insight is that linkWithCredential preserves the same user UID, so any Firestore or Realtime Database data written under the anonymous UID remains accessible. If the email is already in use, you will need a more complex merge strategy involving signing in as the existing user and copying data over.
Common Issue 7: Phone Authentication Fails
The Problem
Phone auth is notoriously tricky. Common failures include reCAPTCHA not rendering, SMS not arriving, or the verification code being rejected. These issues often relate to domain configuration, reCAPTCHA setup, or testing restrictions.
The Solution
Ensure your domain is authorized, configure reCAPTCHA properly, and use test numbers during development:
import { getAuth, RecaptchaVerifier, signInWithPhoneNumber } from "firebase/auth";
const auth = getAuth();
// Set up invisible reCAPTCHA
function setupRecaptcha() {
window.recaptchaVerifier = new RecaptchaVerifier(auth, "sign-in-button", {
size: "invisible",
callback: () => {
// reCAPTCHA solved, proceed with sign-in
sendOtp("+1234567890");
}
});
}
async function sendOtp(phoneNumber) {
try {
const appVerifier = window.recaptchaVerifier;
const confirmationResult = await signInWithPhoneNumber(auth, phoneNumber, appVerifier);
window.confirmationResult = confirmationResult;
console.log("SMS sent");
} catch (error) {
console.error("Phone auth error:", error.code);
// Common codes: auth/invalid-phone-number, auth/too-many-requests
window.recaptchaVerifier.render().then((widgetId) => {
grecaptcha.reset(widgetId);
});
}
}
async function verifyCode(code) {
try {
const result = await window.confirmationResult.confirm(code);
console.log("Phone verified. User:", result.user.uid);
} catch (error) {
console.error("Invalid code:", error.code);
}
}
For development, add test phone numbers and verification codes in Firebase Console → Authentication → Sign-in method → Phone. This bypasses actual SMS sending and avoids rate limits. Remember to remove test numbers before production deployment.
Best Practices for Firebase Auth Troubleshooting
Always Handle Errors Explicitly
Never leave auth calls without error handling. Firebase throws detailed error objects with code, message, and customData properties. Log the full error during development:
try {
await someAuthOperation();
} catch (error) {
console.error("Code:", error.code);
console.error("Message:", error.message);
console.error("Custom data:", error.customData);
console.error("Full error:", error);
}
Use Emulators During Development
The Firebase Auth Emulator lets you test auth flows without hitting production quotas or sending real emails. Configure your app to use the emulator in development:
import { getAuth, connectAuthEmulator } from "firebase/auth";
const auth = getAuth();
if (window.location.hostname === "localhost") {
connectAuthEmulator(auth, "http://localhost:9099", { disableWarnings: true });
}
Monitor Auth State Proactively
Set up a global auth state listener early in your app lifecycle. This ensures you always know the current auth state and can react to session expirations immediately rather than discovering them on a failed API call.
Keep SDK Versions Updated
Firebase frequently patches auth-related bugs and deprecates error codes. Pin your SDK versions in package.json but review the release notes regularly. Breaking changes in auth behavior are documented in the Firebase migration guides.
Implement Rate Limit Awareness
Firebase enforces rate limits on sign-in attempts, password resets, and SMS sends. The auth/too-many-requests error indicates you have hit a limit. Implement exponential backoff for retries and surface a clear message to users rather than silently retrying.
Conclusion
Troubleshooting Firebase Authentication becomes far more manageable when you approach it systematically. Most issues fall into a handful of categories: configuration mismatches, unauthorized domains, token management, and provider-specific quirks like phone verification or email deliverability. By reading error codes carefully, using the Firebase Emulator during development, handling errors with user-friendly messages, and implementing proactive auth state monitoring, you can resolve the vast majority of auth problems quickly. Remember that authentication is not just about getting users signed in — it is about keeping them signed in reliably and recovering gracefully when something goes wrong. With the patterns and code examples in this tutorial, you now have a solid foundation for diagnosing and fixing the most common Firebase Auth issues across web and mobile applications.