> ## Documentation Index
> Fetch the complete documentation index at: https://stytch.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MFA frontend integration

> Integrate MFA with Stytch's pre-built UI components or headless JavaScript SDK.

In this guide, you'll learn how to create an MFA flow and enforce MFA in your application using Stytch's frontend JavaScript SDK. If you'd like to implement MFA, you'll need to use the frontend SDK's headless methods with your own UI, rather than the pre-built UI components.

<Steps>
  <Step title="Prompt users to authenticate their primary factor">
    First, you'll need to prompt users to complete their first method of authentication. For the purposes of this guide, we'll use Passwords as our primary authentication factor.

    We'll also assume that the user already has an account with a password and a [verified email address](/consumer-auth/authentication/passwords/email-verification/overview). If the user's email address has not yet been verified, you will receive a `too_many_unverified_factors `error upon adding a new phone number.

    Create a UI to prompt the user for their email address and password. Upon submission, call the [Authenticate password method](/api-reference/consumer/frontend-sdks/vanilla-js/methods/passwords/authenticate):

    ```js theme={null}
    import { StytchClient } from '@stytch/vanilla-js';

    const stytchClient = new StytchClient('PUBLIC_TOKEN');

    // In minutes, accepted range of 5 to 527040 minutes (366 days)
    const DESIRED_SESSION_LENGTH = 120;

    export const authenticatePassword = () => {
      stytchClient.passwords.authenticate({
        email: 'sandbox@stytch.com',
        password: 'PASSWORD',
        session_duration_minutes: DESIRED_SESSION_LENGTH
      })
      .then(resp => {
        // Stytch Session object
        const session = resp.session;
      })
      .catch(err => { console.log(err) });
    };
    ```

    In the Authenticate password response, you'll receive a `session` object with details about the user's new Stytch Session. Within the session object, you'll see an `authentication_factors` array with a `password` factor in it. Note that there is only one factor present in the `authentication_factors` array, indicating that the user has not yet completed MFA.

    ```js theme={null}
    "session": {
        ...
        "authentication_factors": [
            {
                "created_at": "2024-10-30T16:31:13Z",
                "delivery_method": "knowledge",
                "last_authenticated_at": "2024-10-30T16:31:13Z",
                "type": "password",
                "updated_at": "2024-10-30T16:31:13Z"
            }
        ],
        ...
    }
    ```
  </Step>

  <Step title="Prompt users to authenticate their secondary factor">
    In this guide, we'll use SMS OTP as our secondary factor.

    Passwords and SMS OTP are a good combination of factors, because they confirm your user's identity in two different ways. Note that Passwords and an email-based authentication method are **not** a secure combination of factors, given that a user's password can be reset if a bad actor has access to the user's email inbox.

    Create a UI for the user to enter their phone number if it's not already present on their Stytch User, and send the user an OTP code via the [Send OTP by SMS method](/api-reference/consumer/frontend-sdks/vanilla-js/methods/otps/send-via-sms):

    ```js theme={null}
    export const sendPasscode = () => {
      stytchClient.otps.sms.send('+15555555555')
      .then(resp => {
        // Save the method_id for use in the Authenticate OTP call
        const method_id = resp.method_id;
      })
      .catch(err => { console.log(err) });
    };
    ```

    Next, surface a UI for the user to input the OTP code that they receive, and call the [Authenticate OTP endpoint](/api-reference/consumer/frontend-sdks/vanilla-js/methods/otps/authenticate) with the `method_id` from the `stytchClient.otps.sms.send` response and the OTP code:

    ```js theme={null}
    export const authenticate = () => {
      stytchClient.otps.authenticate(
        'OTP_CODE',
        'METHOD_ID_FROM_SEND_OTP_RESPONSE'
      )
      .then(resp => {
        // Stytch Session object
        const session = resp.session;
      })
      .catch(err => { console.log(err) });
    };
    ```

    The new `session` object's `authentication_factors` array will now contain both a `password` factor and a new `otp` factor:

    ```js theme={null}
    "session": {
        ...
        "authentication_factors": [
            {
                "created_at": "2024-10-30T16:31:13Z",
                "delivery_method": "knowledge",
                "last_authenticated_at": "2024-10-30T16:31:13Z",
                "type": "password",
                "updated_at": "2024-10-30T16:31:13Z"
            },
            {
                "created_at": "2024-10-30T16:32:11Z",
                "delivery_method": "sms",
                "last_authenticated_at": "2024-10-30T16:32:11Z",
                "phone_number_factor": {
                    "phone_id": "phone-number-...",
                    "phone_number": "+15555555555"
                },
                "type": "otp",
                "updated_at": "2024-10-30T16:32:11Z"
            }
        ],
        ...
    },
    ```

    <Note>
      If you authenticate either factor on the backend, make sure to hydrate the frontend SDK with the updated session data by passing the newest `session_token` into the frontend [Update session method](/api-reference/consumer/frontend-sdks/vanilla-js/methods/sessions/update-session), followed by a call to the frontend [Authenticate session method](/api-reference/consumer/frontend-sdks/vanilla-js/methods/sessions/authenticate-session). For security purposes, the original session will be revoked shortly after the user authenticates their second factor.
    </Note>
  </Step>

  <Step title="Enforce MFA in your application's authorization logic">
    In order to enforce that users complete MFA before accessing protected content, you'll need to inspect the `authentication_factors` array after [authenticating the Stytch Session](/api-reference/consumer/frontend-sdks/vanilla-js/methods/sessions/authenticate-session) in your application's backend authorization logic and make sure that the required factors are present. Below is a simplified example of a backend authorization endpoint:

    ```js theme={null}
    const stytch = require('stytch');

    const stytchClient = new stytch.Client({
      project_id: 'PROJECT_ID',
      secret: 'SECRET',
    });

    export const authorizeRequest = (session_token) => {
      stytchClient.sessions.authenticate({session_token})
      .then(resp => {
        const authentication_factors = resp.session.authentication_factors;

        if (
          session.authentication_factors.length === 2 &&
          session.authentication_factors.find((i) => i.delivery_method === 'knowledge') &&
          session.authentication_factors.find((i) => i.delivery_method === 'sms')
        ) {
          // User has completed MFA; can proceed with request
        } else {
          // User has not completed MFA; send to login flow
        }
       })
      .catch(err => {
        // Session could not be successfully authenticated; send to login flow
      });
    }
    ```

    You can also gate frontend content based on the Session's authentication factors in order to achieve your desired UI. Below is a simplified example:

    ```javascript theme={null}
    import { StytchClient } from '@stytch/vanilla-js';

    const stytchClient = new StytchClient('PUBLIC_TOKEN');

    let session = stytchClient.session.getSync();
    const unsubscribe = stytchClient.session.onChange((newSession) => {
      session = newSession;
      if (session &&
          session.authentication_factors.length === 2 &&
          session.authentication_factors.find((i) => i.delivery_method === 'knowledge') &&
          session.authentication_factors.find((i) => i.delivery_method === 'sms')
      ) {
        // Can display gated content
      } else {
        // Display login flow
      }
    });

    window.addEventListener('beforeunload', () => {
      unsubscribe && unsubscribe();
    });
    ```
  </Step>
</Steps>
