> ## 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 backend integration

> Integrate MFA with Stytch's backend SDKs for complete control over your authentication flow.

In this guide, you'll learn how to create an MFA flow and enforce MFA in your application using the Stytch API or our backend SDKs.

<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 endpoint](/api-reference/consumer/api/passwords/authenticate). Include a `session_duration_minutes` parameter so that a new Stytch Session is created:

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

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

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

    const params = {
      email: 'sandbox@stytch.com',
      password: 'PASSWORD',
      session_duration_minutes: DESIRED_SESSION_LENGTH
    };

    stytchClient.passwords.authenticate(params)
      .then(resp => {
        // Stytch Session object
        const session = resp.session;
        // Save the session_token for use in Step 2
        const session_token = resp.session_token;
      })
      .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 right now, indicating that the user has only authenticated their primary factor and 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"
                }
            ],
            ...
        },
        "session_token": "...",
        "session_jwt": "...",
        ...
    }
    ```

    The Authenticate password response will also contain a `session_token` and a `session_jwt`. Save the `session_token` for use in **Step 2**.
  </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 endpoint](/api-reference/consumer/api/otp/via-sms/send). Include the `session_token` from **Step 1** in your request:

    ```js theme={null}
    const params = {
      phone_number: 'USER_PHONE_NUMBER',
      session_token: 'SESSION_TOKEN_FROM_STEP_1'
    };

    stytchClient.otps.sms.send(params)
      .then(resp => {
        // Save the phone_id for use in the Authenticate OTP call
        const method_id = resp.phone_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/api/otp/authenticate) with the `phone_id` from the `stytchClient.otps.sms.send` response, the OTP code, and the `session_token` from **Step 1**:

    ```js theme={null}
    const params = {
      method_id: "PHONE_ID_FROM_SEND_OTP_RESPONSE",
      code: "OTP_CODE",
      session_token: "SESSION_TOKEN_FROM_STEP_1"
    };

    stytchClient.otps.authenticate(params)
      .then(resp => {
        // New Stytch Session object
        const session = resp.session;
        // New session token; update the session token stored on the frontend
        const session_token = resp.session_token;
        // New session JWT; update the session JWT stored on the frontend if you use JWTs
        const session_jwt = resp.session_jwt;
      })
      .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>
      Remember to update any `session_token` and `session_jwt` values stored on the frontend to the new ones returned in the Authenticate OTP response. 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/api/sessions/authenticate-session) in your application's authorization logic and make sure that the required factors are present. Below is a simplified example:

    ```js theme={null}
    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 MFA flow
        }
       })
      .catch(err => {
        // Session could not be successfully authenticated; send to login flow
      });
    }
    ```
  </Step>

  <Step title="(Optional) Add step-up authentication">
    You may also choose to implement step-up authentication, where you prompt users to authenticate a second factor before allowing them to take particularly sensitive actions.

    When using step-up authentication, we generally recommend inspecting the `last_authenticated_at` value on each authentication factor and prompting the user to reauthenticate their secondary factor if too much time has passed since they last did so:

    ```js theme={null}
    export const authorizeSensitiveRequest = (session_token) => {
      stytchClient.sessions.authenticate({session_token})
        .then(resp => {
          const authentication_factors = resp.session.authentication_factors;

          if (
            authentication_factors.length === 2 &&
            authentication_factors.find((i) => i.delivery_method === 'knowledge') &&
            authentication_factors.find((i) => (
              i.delivery_method === 'sms' &&
              // Check if SMS OTP was completed within the last 5 minutes
              Date.now() - new Date(i.last_authenticated_at).getTime() < 5 * 60 * 1000
            ))
          ) {
            // User has completed SMS OTP flow within the past 5 minutes; can proceed with request
          } else {
            // User has not completed SMS OTP flow within the past 5 minutes; deny request and trigger 2FA flow
          }
         })
        .catch(err => {
          // Session could not be successfully authenticated; send to login flow
        });
    }
    ```
  </Step>
</Steps>

## What's next?

You can now customize your MFA setup to use the primary and secondary factors of your choice using the same general strategy outlined above.

Check out the SMS MFA section of our Stytch Demo app for an example of Consumer MFA in practice:

* [Hosted demo](https://www.stytchdemo.com/recipes/smsmfa)
* [Source code](https://github.com/stytchauth/stytch-nextjs-integration/tree/main/pages/recipes/api-sms-mfa)
