Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Applies to:
External tenants (learn more)
In this tutorial, you learn how to add multifactor authentication (MFA) in your React single-page application (SPA) by using native authentication's JavaScript SDK.
Just like in strong authentication method registration, MFA flow occurs in three scenarios:
- During sign-in: The user signs in and has a strong authentication method registered.
- After sign-up: After the user completes sign-up, they proceed to sign in. New users need to register a strong authentication method before any MFA challenge. Because the strong authentication method also gets verified during registration, they might not be prompted for an additional MFA challenge.
- After self-service password reset (SSPR): The user successfully resets their password and automatically proceeds to sign in. If the user has a strong authentication method registered, they're prompted to complete MFA challenge.
When MFA is required, the user chooses an MFA challenge method from a list of registered methods. Available options are email one-time passcode, SMS one-time passcode, or both, depending on what the user previously registered.
The flow diagram below illustrates the three scenarios:
Prerequisites
- Complete the steps in sign up, sign in, password reset and register strong authentication method tutorials.
- Visual Studio Code or another code editor.
- Node.js.
- Enable multifactor authentication (MFA) for your app.
Enable app to handle multifactor authentication
To enable MFA in your React app, update the app configuration by adding the required capability:
Locate the src/config/auth-config.ts file.
In the
customAuthobject, add or updatecapabilitiesproperty to include themfa_requiredvalue in the array as shown in the following code snippet:const customAuthConfig: CustomAuthConfiguration = { customAuth: { ... capabilities: ["mfa_required"], ... }, ... };
The capability value mfa_required informs Microsoft Entra that your app can handle an MFA flow. Learn more about native authentication challenge types and capabilities.
Create UI components
You require form components in your app to support MFA flow. Use the following steps to add these components to your app:
- Create a new folder, src/app/shared/components for reusable components (if it doesn’t already exist).
- In the new folder, create a file named MfaAuthMethodSelectionForm.tsx to display a form that allows users to select a registered strong authentication method. Add the code in MfaAuthMethodSelectionForm into the file.
- In the new folder, create another file named MfaChallengeForm.tsx to display a form for verifying the strong authentication method by using the one-time passcode that the user receives. Add the code in MfaChallengeForm into the file.
When needed, you can import and use reusable components in your sign-in, sign-in after sign-up, and sign-in after SSPR flows.
Handle multifactor authentication during sign-in
Update the src/app/sign-in/page.tsx file to enable your app to handle MFA flow during sign-in. See the complete code in page.tsx:
Import the required types and components as shown in the following code snippet:
import { CustomAuthPublicClientApplication, ICustomAuthPublicClientApplication, SignInCodeRequiredState, SignInCompletedState, AuthFlowStateBase, MfaAwaitingState, MfaVerificationRequiredState, } from "@azure/msal-browser/custom-auth"; import { MfaAuthMethodSelectionForm } from "../components/shared/MfaAuthMethodSelectionForm"; import { MfaChallengeForm } from "../components/shared/MfaChallengeForm";Add new state variables for MFA:
export default function SignIn() { ... // MFA states const [mfaAuthMethods, setMfaAuthMethods] = useState<AuthenticationMethod[]>([]); const [selectedMfaAuthMethod, setSelectedMfaAuthMethod] = useState<AuthenticationMethod | undefined>(undefined); const [mfaChallenge, setMfaChallenge] = useState(""); // ... initialization code }Update the
startSignIn,handlePasswordSubmitandhandleCodeSubmitfunctions to check if MFA is required:const startSignIn = async (e: React.FormEvent) => { // Start the sign-in flow const result = await authClient.signIn({ username, }); ... if (result.isMfaRequired()) { const methods = result.state.getAuthMethods(); setMfaAuthMethods(methods); setSelectedMfaAuthMethod(methods.length > 0 ? methods[0] : undefined); } ... }; const handlePasswordSubmit = async (e: React.FormEvent) => { if (signInState instanceof SignInPasswordRequiredState) { const result = await signInState.submitPassword(password); ... // Check for MFA requirement if (result.isMfaRequired()) { const methods = result.state.getAuthMethods(); setMfaAuthMethods(methods); setSelectedMfaAuthMethod(methods.length > 0 ? methods[0] : undefined); setSignInState(result.state); } ... } }; const handleCodeSubmit = async (e: React.FormEvent) => { if (signInState instanceof SignInCodeRequiredState) { const result = await signInState.submitCode(code); ... // Check for MFA requirement if (result.isMfaRequired()) { const methods = result.state.getAuthMethods(); setMfaAuthMethods(methods); setSelectedMfaAuthMethod(methods.length > 0 ? methods[0] : undefined); setSignInState(result.state); } ... } };In each of the function, notice that we check if MFA is required by using the following code snippet:
if (result.isMfaRequired()) {...}Add the handler for MFA challenge selection:
const handleMfaAuthMethodSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); setLoading(true); if (!selectedMfaAuthMethod) { setError("Please select an authentication method."); setLoading(false); return; } if (signInState instanceof MfaAwaitingState) { const result = await signInState.requestChallenge(selectedMfaAuthMethod.id); if (result.isFailed()) { if (result.error?.isInvalidInput()) { setError("Incorrect verification contact."); } else { setError( result.error?.errorData?.errorDescription || "An error occurred while verifying the authentication method" ); } } if (result.isVerificationRequired()) { setSignInState(result.state); } } setLoading(false); };Add the handler for MFA challenge verification:
const handleMfaChallengeSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); setLoading(true); if (!mfaChallenge) { setError("Please enter a code."); setLoading(false); return; } if (signInState instanceof MfaVerificationRequiredState) { const result = await signInState.submitChallenge(mfaChallenge); if (result.isFailed()) { if (result.error?.isIncorrectChallenge()) { setError("Incorrect code."); } else { setError( result.error?.errorData?.errorDescription || "An error occurred while verifying the challenge response" ); } } if (result.isCompleted()) { setData(result.data); setCurrentSignInStatus(true); setSignInState(result.state); } } setLoading(false); };Update the
renderForm()function to display the correct MFA challenge forms (MFA challenge method selection or MFA challenge method verification):const renderForm = () => { if (loadingAccountStatus) { return; } ... // Display MfaAuthMethodSelectionForm if the current state is MFA awaiting state if (signInState instanceof MfaAwaitingState) { return ( <MfaAuthMethodSelectionForm onSubmit={handleMfaAuthMethodSubmit} authMethods={mfaAuthMethods} selectedAuthMethod={selectedMfaAuthMethod} setSelectedAuthMethod={setSelectedMfaAuthMethod} loading={loading} styles={styles} /> ); } // Display MfaChallengeForm if the current state is MFA verification required state if (signInState instanceof MfaVerificationRequiredState) { return ( <MfaChallengeForm onSubmit={handleMfaChallengeSubmit} challenge={mfaChallenge} setChallenge={setMfaChallenge} loading={loading} styles={styles} /> ); } ... };
Handle multifactor authentication after sign-up or password reset
MFA flow after sign-up and password reset works similar to the MFA in the sign-in flow. After a successful sign-up or password reset, the SDK can automatically continue with the sign-in flow. If the user has a strong authentication method registered, the flow transitions to MFA challenge verification.
Handle multifactor authentication after sign-up
For MFA flow after sign-up, you need you update the /src/app/sign-up/page.tsx file. See the complete code in page.tsx:
Make sure you import the required types and components.
Handle MFA requirements states in a similar manner as it happens in the sign-in flow; after sign-up completes successfully, use the result to automatically trigger a sign-in flow as shown in the following code snippet:
// In your sign-up completion handler if (signUpState instanceof SignUpCompletedState) { // Continue with sign-in using the continuation token const result = await signUpState.signIn(); ... if (result.isMfaRequired()) { const methods = result.state.getAuthMethods(); setMfaAuthMethods(methods); setSelectedMfaAuthMethod(methods.length > 0 ? methods[0] : undefined); setSignUpState(state); } ... } // Then use the same renderForm logic to display MfaAuthMethodSelectionForm // and MfaChallengeForm components
Handle multifactor authentication after password reset
For MFA flow after SSPR, you need to update the /src/app/reset-password/page.tsx file. See the complete code in page.tsx:
Make sure you import the required types and components.
Handle MFA requirement states in a similar manner as in the sign-in flow. After SSPRS completes successfully, you can use the result to automatically trigger a sign-in as shown in the following code snippet:
// In your password reset completion handler if (resetPasswordState instanceof ResetPasswordCompletedState) { // Continue with sign-in using the continuation token const result = await signUpState.signIn(); ... if (result.isMfaRequired()) { const methods = result.state.getAuthMethods(); setMfaAuthMethods(methods); setSelectedMfaAuthMethod(methods.length > 0 ? methods[0] : undefined); setResetState(state); } ... } // Then use the same renderForm logic to display MfaAuthMethodSelectionForm // and MfaChallengeForm components
Run and test your app
Before you test your app, make sure you have a user account that has a registered strong authentication method. Use the steps in Run and test your app to run your app, but this time, test the MFA flow.