-
Notifications
You must be signed in to change notification settings - Fork 993
Description
Operating System
Sonoma 14.5
Environment (if applicable)
Chrome Version 131
Firebase SDK Version
firebase@11.0.2
Firebase SDK Product(s)
Auth
Project Tooling
React App (18.2.0)
Webpack
Detailed Problem Description
Our application is built with email/password (+ optional MFA) auth. Our customers have long requested SSO login with Microsoft.
In the process of building out the UI and flows, the process will fail when MFA was enabled on the base (email auth) account.
Digging into the error, it fails because Firebase is requesting MFA re-authentication for a second time after linkWithPopup has completed.
To be clear I've already successfully re-authenticated with password/MFA before calling linkWithPopup.
- In the Firebase auth UI I can see the account is already linked
- If refresh the tab, the account will then be linked without needing to go through the second MFA flow.
Workaround
When the second MFA error is thrown, the page is hard refreshed so the user doesn't need to go through the MFA flow a second time.
Given that you can refresh to avoid the second MFA flow, this does not add any extra security
This works with no issue when using single factor authentication on the email auth account.
Steps and code to reproduce issue
Error
ConnectedAccounts.tsx:35 FirebaseError: Firebase: Error (auth/multi-factor-auth-required).
at createErrorInternal (assert.ts:146:1)
at _fail (assert.ts:65:1)
at _performSignInRequest (index.ts:252:1)
at async _logoutIfInvalidated (invalidation.ts:32:1)
at async _link$1 (link_unlink.ts:66:1)
at async PopupOperation.onAuthEvent (abstract_popup_redirect_operation.ts:103:1)
Code
import {
EmailAuthProvider,
getAuth,
linkWithPopup,
OAuthProvider,
reauthenticateWithCredential,
} from "firebase/auth";
const microsoftProvider = new OAuthProvider("microsoft.com").setCustomParameters({
prompt: "consent", // Force re-consent.
});
function ConnectedAccounts() {
async function reauthenticate(password: string) {
const credential = EmailAuthProvider.credential(getAuth().currentUser!.email!, password);
await reauthenticateWithCredential(getAuth().currentUser!, credential);
}
async function linkWithMicrosoft() {
try {
await linkWithPopup(getAuth().currentUser!, microsoftProvider);
} catch (error: any) {
if (error?.code === "auth/multi-factor-auth-required") {
// Fore refresh page when this error gets hit by link being called
// window.location.reload(); // WORKAROUND
} else {
throw error;
}
}
}
const myPassword = "somePassword";
async function handleLink() {
try {
await reauthenticate(myPassword);
await linkWithMicrosoft();
} catch (error: any) {
if (error?.code === "auth/multi-factor-auth-required") {
// trigger and complete MFA flow
} else {
throw error;
}
}
}
return <button onClick={handleLink}>Link to Microsoft</button>;
}- Login by email
- Call
reauthenticate(password) - Complete Password and SMS MFA flow successfully
- Popup tab appears
- Consent to linking in Microsoft UI
- Popup tab auto closes
- Second
"auth/multi-factor-auth-required"error is thrown in thelinkfunctions - At this point the account will show as linked in the Firebase auth UI
- Refresh the page and the account will be linked correctly