You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Request a documented, cross-platform shape for the MultiFactorError that FirebaseAuthentication.signIn* re-throws when MFA is required, so JS callers
can identify the first-factor account without first completing the second
factor. Specifically: surface email and uid (Firebase Auth localId) of
the account on every platform (web, iOS, Android).
Use case
Implementing a recovery-code 2FA bypass on the login screen. When a sign-in
attempt raises auth/multi-factor-auth-required, the user clicks "Use a
recovery code instead" and submits a code to a Cloud Function Callable. The
Callable verifies the hashed code and mints an Auth custom token via admin.auth().createCustomToken(uid) — the client then calls signInWithCustomToken(auth, token) to complete sign-in, bypassing the second
factor for this session (the user has lost their authenticator).
The catch: Firebase has not minted an ID token between first and second
factor, so the Callable's request.auth?.uid is null. It MUST identify the
user some other way. The MFA error is the only signal the client has.
Current behaviour (web)
The Firebase JS SDK preserves the raw Identity Toolkit REST response on a private field of the error object:
Callers are forced to reach into customData._serverResponse — an
undocumented private field — to get the account email and uid. The leading
underscore signals "don't use this", and the field could disappear in any JS
SDK release.
For password sign-in MFA, customData.email is populated. For federated providers (Google, Apple), customData.email is null — the
JS SDK does not project the federated provider's email into the public
field. The only reliable surface for federated-OAuth MFA is the private _serverResponse.
Current behaviour (native, expected once #346 lands)
iOS and Android native Firebase Auth SDKs raise their own MFA error types:
iOS: FIRMultiFactorError with info on userInfo dictionary
Android: FirebaseAuthMultiFactorException with getter methods on the
resolver
These are different shapes from the JS SDK error. When the plugin
re-throws across the Capacitor bridge, the underlying native fields don't
make it to the JS side — so even the private _serverResponse escape hatch
that works on web won't help on native.
Preferred solution
Add a documented JS-side error contract for FirebaseAuthentication's MFA
rejections, populated identically across all three platforms:
interfaceFirebaseAuthenticationMultiFactorErrorextendsFirebaseError{code: 'auth/multi-factor-auth-required';customData: {appName: string;/** * First-factor account email. Populated for both password and * federated providers (Google, Apple). Falls back to null only when * the IdP does not return an email claim. */email: string|null;/** * First-factor account Firebase Auth uid (the REST API's `localId`). * Stable across email changes — the canonical identity hint. */uid: string|null;/** Optional: enrolled second-factor hints for UI selection. */factorHints?: ReadonlyArray<{uid: string;displayName?: string;factorId: 'phone'|'totp';}>;};}
Implementation sketch:
Web (plugin's existing web wrapper): read err.customData._serverResponse.email / .localId and project them into
the public customData.email / customData.uid fields before re-throwing.
This insulates callers from JS SDK private-field drift.
iOS (when native MFA lands per feat: Enabling multi-factor authentication #346): read error.userInfo[FIRAuthErrorUserInfoEmailKey] for email; for uid, lift
from FIRMultiFactorResolver.currentUser.uid if accessible at error-throw
time (or surface it once the resolver is constructed JS-side).
Android: same idea — read the native exception's email getter and the
resolver's user uid.
Alternative options
No response
Additional context
Why this matters
Without uid on the error, the only identity hint the client has is email.
That works for typical users, but breaks on this edge case:
A user changes their primary email via verifyBeforeUpdateEmail after
enrolling MFA. Their Google/Apple provider is still linked with the original email — Firebase Auth doesn't update provider emails when
primary email changes (spec-correct Firebase behaviour).
Google sign-in still works (matches by Google sub claim, not email).
But the MFA-required error carries the Google provider's email, and
server-side adminAuth().getUserByEmail() matches primary emails only
— so the user cannot be resolved by their federated email. The
recovery-code Callable rejects with auth/user-not-found before ever
checking the code.
With uid on the error, the server resolves via adminAuth().getUser(uid)
which is identity-agnostic, and the recovery flow works.
Repro
Add Google as a federated provider for a Firebase Auth user.
Enrol TOTP MFA on that user.
Call FirebaseAuthentication.signInWithGoogle() from a Capacitor web build.
Inspect the caught error:
error.customData.email === null (no public surface)
error.customData._serverResponse exists, contains email and localId (private surface — works today on web, undocumented).
Related
feat: Enabling multi-factor authentication #346 — enabling native MFA on iOS. This issue is the API-shape companion:
even after native MFA support lands, the JS-side error contract needs to
be standardised across platforms so callers don't have to write three
platform branches.
Plugin(s)
Current problem
Summary
Request a documented, cross-platform shape for the
MultiFactorErrorthatFirebaseAuthentication.signIn*re-throws when MFA is required, so JS callerscan identify the first-factor account without first completing the second
factor. Specifically: surface
emailanduid(Firebase AuthlocalId) ofthe account on every platform (web, iOS, Android).
Use case
Implementing a recovery-code 2FA bypass on the login screen. When a sign-in
attempt raises
auth/multi-factor-auth-required, the user clicks "Use arecovery code instead" and submits a code to a Cloud Function Callable. The
Callable verifies the hashed code and mints an Auth custom token via
admin.auth().createCustomToken(uid)— the client then callssignInWithCustomToken(auth, token)to complete sign-in, bypassing the secondfactor for this session (the user has lost their authenticator).
The catch: Firebase has not minted an ID token between first and second
factor, so the Callable's
request.auth?.uidisnull. It MUST identify theuser some other way. The MFA error is the only signal the client has.
Current behaviour (web)
The Firebase JS SDK preserves the raw Identity Toolkit REST response on a
private field of the error object:
Callers are forced to reach into
customData._serverResponse— anundocumented private field — to get the account email and uid. The leading
underscore signals "don't use this", and the field could disappear in any JS
SDK release.
For password sign-in MFA,
customData.emailis populated. Forfederated providers (Google, Apple),
customData.emailisnull— theJS SDK does not project the federated provider's email into the public
field. The only reliable surface for federated-OAuth MFA is the private
_serverResponse.Current behaviour (native, expected once #346 lands)
iOS and Android native Firebase Auth SDKs raise their own MFA error types:
FIRMultiFactorErrorwith info onuserInfodictionaryFirebaseAuthMultiFactorExceptionwith getter methods on theresolver
These are different shapes from the JS SDK error. When the plugin
re-throws across the Capacitor bridge, the underlying native fields don't
make it to the JS side — so even the private
_serverResponseescape hatchthat works on web won't help on native.
Preferred solution
Add a documented JS-side error contract for
FirebaseAuthentication's MFArejections, populated identically across all three platforms:
Implementation sketch:
err.customData._serverResponse.email/.localIdand project them intothe public
customData.email/customData.uidfields before re-throwing.This insulates callers from JS SDK private-field drift.
error.userInfo[FIRAuthErrorUserInfoEmailKey]for email; for uid, liftfrom
FIRMultiFactorResolver.currentUser.uidif accessible at error-throwtime (or surface it once the resolver is constructed JS-side).
resolver's user uid.
Alternative options
No response
Additional context
Why this matters
Without
uidon the error, the only identity hint the client has isemail.That works for typical users, but breaks on this edge case:
With
uidon the error, the server resolves viaadminAuth().getUser(uid)which is identity-agnostic, and the recovery flow works.
Repro
FirebaseAuthentication.signInWithGoogle()from a Capacitor web build.error.customData.email === null(no public surface)error.customData._serverResponseexists, containsemailandlocalId(private surface — works today on web, undocumented).Related
even after native MFA support lands, the JS-side error contract needs to
be standardised across platforms so callers don't have to write three
platform branches.
Before submitting