Firebase throws auth/invalid-credential when a sign-in credential is wrong, malformed, or expired. With email enumeration protection on, it also covers wrong passwords and unknown emails.
Firebase Authentication throws `auth/invalid-credential` when the credential you pass to a sign-in, link, or re-authentication call fails validation. The most common modern source is email/password sign-in: since September 2023 new Firebase projects have **email enumeration protection** enabled by default, so `signInWithEmailAndPassword` returns this single error for both a wrong password and a non-existent account (the old `auth/wrong-password` and `auth/user-not-found` codes are intentionally collapsed to prevent attackers from probing which emails exist). The same error also appears with OAuth providers (Google, Apple, Facebook) when the credential object is malformed or missing required fields, when an ID token has expired, or when re-authenticating a user whose session is stale. In short, Firebase is telling you the credential it received is not something it can accept right now.
For signInWithEmailAndPassword, auth/invalid-credential almost always means the password is wrong or the account does not exist. With email enumeration protection enabled (the default for projects created since September 2023) Firebase deliberately returns one generic code for both so attackers cannot tell which emails are registered.
Do not try to distinguish the two cases or disable this protection just to get more detailed errors — that re-introduces an account enumeration vulnerability. Instead, show the user a single neutral message and let them retry or reset their password:
import { signInWithEmailAndPassword } from 'firebase/auth';
try {
await signInWithEmailAndPassword(auth, email, password);
} catch (error) {
if (error.code === 'auth/invalid-credential') {
// Could be a wrong password OR an unknown email — by design.
showMessage('Incorrect email or password. Try again or reset your password.');
} else {
throw error;
}
}If you genuinely need the older granular errors and accept the security trade-off, you can disable enumeration protection in Firebase Console > Authentication > Settings > User actions — but the neutral-message approach above is strongly preferred.
For OAuth providers, build the credential with exactly the fields that provider returns. A common mistake is passing a token under the wrong key or omitting idToken.
import { OAuthProvider, signInWithCredential } from 'firebase/auth';
// Apple: pass the idToken Apple returned. rawNonce must match the
// (unhashed) nonce you sent in the original authorization request.
const provider = new OAuthProvider('apple.com');
const credential = provider.credential({
idToken: appleIdToken,
rawNonce: rawNonce, // required if you sent a nonce
});
await signInWithCredential(auth, credential);Note: Apple Sign-In is verified through the idToken (and matching nonce), not an accessToken. Passing a bogus accessToken will not fix a malformed credential. For Google, use GoogleAuthProvider.credential(idToken, accessToken) with the tokens your client actually obtained.
An unenabled or misconfigured provider produces auth/invalid-credential.
1. Open Firebase Console > Authentication > Sign-in method.
2. Enable each provider you use (Email/Password, Google, Apple, Facebook, etc.).
3. For OAuth providers, confirm the OAuth client ID/secret matches what is configured in the provider's own dashboard.
4. For Apple, verify the Service ID, Team ID, Key ID, and private key in the Apple provider settings.
5. For mobile, confirm GoogleService-Info.plist (iOS) / google-services.json (Android) and SHA fingerprints match this Firebase project.
OAuth ID tokens are short-lived. If you cache a credential and reuse it later, validation can fail. Always obtain a fresh token from the provider immediately before calling signInWithCredential, rather than persisting and replaying an old one.
For re-authentication before a sensitive action, prompt the user for a fresh credential:
import { reauthenticateWithCredential, EmailAuthProvider } from 'firebase/auth';
const credential = EmailAuthProvider.credential(user.email, currentPassword);
try {
await reauthenticateWithCredential(user, credential);
} catch (error) {
if (error.code === 'auth/invalid-credential') {
showMessage('That password is incorrect. Please re-enter it to continue.');
}
}Multiple initializeApp calls or mixing credentials across projects can cause validation failures. Initialize once at module scope and reuse the instance:
import { initializeApp, getApps, getApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const firebaseConfig = { /* your project config */ };
const app = getApps().length ? getApp() : initializeApp(firebaseConfig);
export const auth = getAuth(app);Using getApps()/getApp() is important in hot-reload and SSR environments (e.g. Next.js) where modules can re-evaluate. Make sure the config corresponds to the same Firebase project as your providers and any Admin SDK service account.
Admin SDK token verification fails if the service account is from the wrong project or the server clock is skewed, since token expiry checks are time-sensitive.
Use a service account key from the matching project:
const admin = require('firebase-admin');
const serviceAccount = require('./service-account-key.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});Then confirm time is synchronized. On modern systemd-based Linux, enable NTP with timedatectl (the older ntpdate utility is deprecated and absent on most current distros):
# Check status
timedatectl status
# Enable automatic NTP synchronization
sudo timedatectl set-ntp trueKeep service account keys out of version control. Only regenerate a key (Console > Project Settings > Service Accounts > Generate New Private Key) if you suspect it was leaked or revoked.
Email enumeration protection is the key thing to understand here. When it is enabled (the default for Firebase projects created since September 2023), Firebase intentionally returns auth/invalid-credential for both wrong passwords and non-existent accounts, and fetchSignInMethodsForEmail returns an empty list regardless of which providers exist. This is a deliberate security feature; do not work around it by disabling the protection or by probing the account state, as that re-enables account enumeration. The legacy auth/invalid-login-credentials string seen in some older SDK/Identity Platform responses is the same underlying condition (invalid email/password under enumeration protection) — treat it as equivalent to auth/invalid-credential, not as a separate problem. Platform notes: for Apple Sign-In the credential is validated via the idToken and a matching rawNonce, not an access token; for Android verify your SHA-1/SHA-256 fingerprints in the console match the signing key of the build you are testing; for SSR frameworks initialize Firebase guarded by getApps() to avoid duplicate app instances. Anonymous accounts cannot be linked to a credential that already belongs to an existing account — check first or handle the auth/credential-already-in-use error separately.
auth/invalid-page-token: Next page token in listUsers() is invalid
How to fix "auth/invalid-page-token" when listing Firebase users
Analytics: Code 2 - Event name invalid
How to fix "Analytics: Code 2 - Event name invalid" in Firebase
messaging/UNSPECIFIED_ERROR: No additional information available
How to fix "messaging/UNSPECIFIED_ERROR: No additional information available" in Firebase Cloud Messaging
App Check: reCAPTCHA Score Too Low
App Check reCAPTCHA Score Too Low
storage/invalid-url: Invalid URL format for Cloud Storage reference
How to fix invalid URL format in Firebase Cloud Storage