0

I want to set up SMS based MFA in my ReactJS app. Users of the app sign in with email/password and this is all handled by firebase. I've started to set up MFA, so far users can enable it on their account. I would like to let them remove MFA also, and have set up a series of steps to handle this (user clicks disable, then confirm, enters their password to reauthenticate, enters an SMS verification code). After the verification code is entered, the feedback message states that MFA is already disabled, but it is not. It's still enabled in the firebase console, and shows as active (conditionally formatted button) in the parent element.

Below is the code that should turn off MFA:

  const handleDisableMFA = async () => {
    setError("");
    setIsLoading(true);
  
    try {
      const currentUser = auth.currentUser;
      if (!currentUser) {
        throw new Error("User not found");
      }
  
      console.log("Current user:", currentUser);
  
      // Refresh the user's token to ensure we have the latest MFA status
      console.log("Reloading user data...");
      await currentUser.reload();
      console.log("User data reloaded");
  
      const isMFAEnabled = await verifyMFAStatus();
      
      if (!isMFAEnabled) {
        console.log("MFA is not enabled for this user");
        setSuccess("MFA has been disabled.");
        setIsMFAEnabled(false);
        setTimeout(onClose, 2000);
        return;
      }
  
      console.log("Attempting to disable MFA...");
      const multiFactorUser = multiFactor(currentUser);
      const enrolledFactors = multiFactorUser.enrolledFactors;
  
      if (!enrolledFactors || enrolledFactors.length === 0) {
        console.log("No enrolled factors found, but MFA was reported as enabled. This is unexpected.");
        throw new Error("Inconsistent MFA state detected");
      }
  
      // Unenroll from all factors
      for (const factor of enrolledFactors) {
        console.log(`Attempting to unenroll factor: ${factor.uid}`);
        await multiFactorUser.unenroll(factor);
        console.log(`Successfully unenrolled factor: ${factor.uid}`);
      }
  
      // Force token refresh and reload user data
      console.log("Refreshing user token and reloading data...");
      await currentUser.getIdToken(true);
      await currentUser.reload();
  
      // Verify MFA is disabled
      const mfaStillEnabled = await verifyMFAStatus();
      if (!mfaStillEnabled) {
        console.log("All MFA factors successfully removed");
        setSuccess("MFA has been successfully disabled.");
        setIsMFAEnabled(false);
        // Note: We're not updating any context here
      } else {
        throw new Error("Failed to disable all MFA factors");
      }
    } catch (error) {
      console.error("Error in handleDisableMFA:", error);
      handleFirebaseError(error);
    } finally {
      setIsLoading(false);
      setTimeout(onClose, 2000);
    }
  };

I have been back and forward with GPT and Claude over this, hence the comments in the code.

In the DOM, the modal reports that MFA has been disabled, and the button in the parent element changes correctly, but refreshing the page reverts back to the enabled state.

Any help would be greatly appreciated

Before disabling MFA

After "disabling" MFA

1
  • What is happening in the verifyMFAStatus method? If you're seeing MFA is already disabled as the response from the UI, then it's possible that function is returning something falsey incorrectly. Commented Sep 11, 2024 at 17:00

1 Answer 1

0

Using the modular API you can do this:

import { multiFactor, PhoneMultiFactorGenerator } from "@firebase/auth";

...

const factors = multiFactor(this.user).enrolledFactors;
const phoneFactor = factors.find(
      (factor) => factor.factorId === PhoneMultiFactorGenerator.FACTOR_ID
    );
await multiFactor(this.user).unenroll(phoneFactor);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.