> ## Documentation Index
> Fetch the complete documentation index at: https://docs-dev-actions-triggers-prototype.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Learn how an API can check if a user has logged in with Multi-factor Authentication by examining their access token.

# Configure Step-up Authentication for APIs

With step-up authentication, applications that allow access to different types of resources can require users to authenticate with a stronger mechanism to access sensitive information or perform certain transactions.

For example, a user of a banking app may be allowed to transfer money between accounts only after they have confirmed their identity using <Tooltip tip="Multi-factor authentication (MFA): User authentication process that uses a factor in addition to username and password such as a code via SMS." cta="View Glossary" href="/docs/glossary?term=multi-factor+authentication">multi-factor authentication</Tooltip> (MFA).

When your <Tooltip tip="Audience: Unique identifier of the audience for an issued token. Named aud in a token, its value contains the ID of either an application (Client ID) for an ID Token or an API (API Identifier) for an Access Token." cta="View Glossary" href="/docs/glossary?term=audience">audience</Tooltip> is an API, you can implement step-up authentication with Auth0 using scopes, <Tooltip tip="Access Token: Authorization credential, in the form of an opaque string or JWT, used to access an API." cta="View Glossary" href="/docs/glossary?term=access+tokens">access tokens</Tooltip>, and [Actions](/docs/customize/actions). When an application wants to access an API's protected resources, it must provide an access token. The resources that it will have access to depend on the permissions that are included in the access token. These permissions are defined as [scopes](/docs/get-started/apis/scopes/api-scopes).

## Validate access tokens for MFA

In addition to checking the scope, the API must [validate the access token](/docs/secure/tokens/access-tokens/validate-access-tokens) to:

* Verify the token's signature, used to verify that the sender of the token is who it says it is and to ensure that the message wasn't changed along the way.
* Validate the standard claims:

| Claim | Description                     |
| ----- | ------------------------------- |
| `exp` | Token expiration                |
| `iss` | Token issuer                    |
| `aud` | Intended recipient of the token |

## Scenario: Bank transactions with push notifications

In the following scenario, an application authenticates a user with username and password and then requests an account balance. Before retrieving the account balance information, the user must authenticate with Guardian push factor.

The banking API can accept two different levels of authorization: view account balance (scope `view:balance`) or transfer funds (scope `transfer:funds`). When the application asks the API to retrieve the user's balance, the access token should contain the `view:balance` scope. To transfer money to another account, the access token should contain the `transfer:funds` scope.

### Workflow

1. The user logs in to the application using username and password authentication. The standard login gives this user the ability to interact with the API and fetch their balance. This means that the access token that the app receives after the user authenticates contains the `view:balance` scope.
2. The application sends a request to the API to retrieve the balance, using the access token as credentials.
3. The API validates the token and sends the balance info to the application, so the user can view it.
4. The user wants to transfer funds from one account to another, which is deemed a high-value transaction that requires the `transfer:funds` scope. The application sends a request to the API using the same access token.
5. The API validates the token and denies access because the token is missing the required `transfer:funds` scope.
6. The application redirects to Auth0, where an Action is used to challenge the user to authenticate with MFA since a high-value scope was requested. Once the user successfully authenticates with MFA, a new access token that includes the correct scope is generated and sent to the application as part of the response.
7. The application sends another transfer funds request using the new access token, which includes the `transfer:funds` scope this time.
8. The API validates the token, discards it, and proceeds with the operation.

### Prerequisites

For this scenario, you must configure the following items in the Dashboard:

* [Register a Single-Page Web App](/docs/get-started/auth0-overview/create-applications/single-page-web-apps).
* [Create a database connection](https://manage.auth0.com/#/connections/database).
* [Register the API](/docs/get-started/auth0-overview/set-up-apis). Create two scopes: `view:balance` and `transfer:funds`.
* [Enable MFA](/docs/secure/multi-factor-authentication/enable-mfa) to use push notifications.

### Create an Action

Create an Action that challenges the user to authenticate with MFA when the `transfer:funds` scope is requested. Go to [Dashboard > Actions > Flows](https://manage.auth0.com/#/actions/flows) and create an Action that contains the following content:

```javascript theme={null}
{
exports.onExecutePostLogin = async (event, api) => {
  const CLIENTS_WITH_MFA = ['REPLACE_WITH_{yourClientId}'];
  // run only for the specified clients
  if (CLIENTS_WITH_MFA.includes(event.client.client_id)) {
    // ask for MFA only if scope transfer:funds was requested
    if (event.transaction.requested_scopes.indexOf('transfer:funds') > -1)
      api.multifactor.enable('any', { allowRememberBrowser: false });
    }
  }
},
```

* The `CLIENTS_WITH_MFA` variable contains the <Tooltip tip="Client ID: Identification value given to your registered resource from Auth0." cta="View Glossary" href="/docs/glossary?term=client+IDs">client IDs</Tooltip> of the applications you want this Action to apply to. You can remove this (and the `if` conditional that follows) if you don't need it.
* The `event.transaction.requested_scopes` property contains all the scopes for which the authentication request asked. If it includes the value `transfer:funds`, then we ask for MFA by setting the `context.multifactor` property to the appropriate value. In this case, we are asking for MFA using [push](/docs/secure/multi-factor-authentication/multi-factor-authentication-factors/configure-push-notifications-for-mfa).

### Configure app

Configure the app to send the appropriate authentication request to the API, depending on whether the user is attempting to perform the high-value transaction of transferring funds. Notice that the only difference between the two authentication requests (with or without MFA) is the scope.

* With MFA:

  export const codeExample1 = ` https://{yourDomain}/authorize?
  audience=https://my-banking-api&
  scope=openid%20view:balance%20transfer:funds&
  response_type=id_token%20token&
  client_id={yourClientId}&
  redirect_uri={https://yourApp/callback}&
  nonce=NONCE&
  state=OPAQUE_VALUE`;

<AuthCodeBlock children={codeExample1} language="text" />

* Without MFA:

  export const codeExample2 = ` https://{yourDomain}/authorize?
  audience=https://my-banking-api&
  scope=openid%20view:balance&
  response_type=id_token%20token&
  client_id={yourClientId}&
  redirect_uri={https://yourApp/callback}&
  nonce=NONCE&
  state=OPAQUE_VALUE`;

<AuthCodeBlock children={codeExample2} language="text" />

| Parameter       | Setting                                                                                                                                                                                                                                                                                                                       |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `audience`      | Set to the **Identifier** of your API (find it at [API Settings](https://manage.auth0.com/#/apis/)). We set ours to `https://my-banking-api`.                                                                                                                                                                                 |
| `response_type` | Set to `id_token token` so we get both an ID Token and an Access Token in the response.                                                                                                                                                                                                                                       |
| `client_id`     | Set to the Client ID of your application (find it at [Application Settings](https://manage.auth0.com/#/applications/\{yourClientId}/settings)).                                                                                                                                                                               |
| `redirect_uri`  | Set to a URL in your application that Auth0 should redirect back to after authentication (find it at [Application Settings](https://manage.auth0.com/#/applications/\{yourClientId}/settings)).                                                                                                                               |
| `nonce`         | Set to a secure string value which will be included in the response from Auth0. This is [used to prevent token replay attacks](/docs/get-started/authentication-and-authorization-flow/implicit-flow-with-form-post/mitigate-replay-attacks-when-using-the-implicit-flow) and is required for `response_type=id_token token`. |
| `state`         | Set to an opaque value that Auth0 includes when redirecting back to the application. This value must be used by the application to prevent CSRF attacks.                                                                                                                                                                      |

### Configure API

Configure the API to validate the incoming token and check the authorized permissions.

1. Configure two endpoints for our API:
   `GET /balance`: to retrieve the current balance
   `POST /transfer`: to transfer funds
2. Use `Node.js` and a number of modules:

   1. [express](https://expressjs.com/): adds the Express web application framework.
   2. [jwks-rsa](https://github.com/auth0/node-jwks-rsa): retrieves RSA signing keys from a **JWKS** (JSON Web Key Set) endpoint. Using `expressJwtSecret`, we can generate a secret provider that will issue the right signing key to `express-jwt` based on the `kid` in the JWT header.
   3. [express-jwt](https://github.com/auth0/express-jwt): lets you authenticate HTTP requests using JWT tokens in your Node.js applications. It provides several functions that make working with JWTs easier.
   4. [express-jwt-authz](https://github.com/auth0/express-jwt-authz): checks if the access token contains a specific scope.
3. Install the dependencies:
   `npm install express express-jwt jwks-rsa express-jwt-authz --save`
4. Define the API endpoints, create a middleware function to validate the access token, and secure the endpoints using that middleware. The code in your `server.js` file should look like the following sample script:

export const codeExample3 = `   // set dependencies
    const express = require('express');
    const app = express();
    const jwt = require('express-jwt');
    const jwksRsa = require('jwks-rsa');
    const jwtAuthz = require('express-jwt-authz');

    // Create middleware for checking the JWT
    const checkJwt = jwt({
      // Dynamically provide a signing key based on the kid in the header and the signing keys provided by the JWKS endpoint
      secret: jwksRsa.expressJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: \`https://{yourDomain}/.well-known/jwks.json\`
      }),

      // Validate the audience and the issuer
      audience: 'https://my-banking-api', // replace with your API's audience, available at Dashboard > APIs
      issuer: 'https://{yourDomain}/',
      algorithms: [ 'RS256' ] // we are using RS256 to sign our tokens
    });

    // create retrieve balance endpoint
    app.get('/balance', checkJwt, jwtAuthz(['view:balance']), function (req, res) {
      // code that retrieves the user's balance and sends it back to the calling app
      res.status(201).send({message: "This is the GET /balance endpoint"});
    });


    // create transfer funds endpoint
    app.post('/transfer', checkJwt, jwtAuthz(['transfer:funds']), function (req, res) {
      // code that transfers funds from one account to another
      res.status(201).send({message: "This is the POST /transfer endpoint"});
    });

    // launch the API Server at localhost:8080
    app.listen(8080);
    console.log('Listening on http://localhost:8080');
`;

<AuthCodeBlock children={codeExample3} language="javascript" />

Each time the API receives a request the following happens:

1. The endpoint calls the `checkJwt` middleware.
2. `express-jwt` decodes the token and passes the request, the header, and the payload to `jwksRsa.expressJwtSecret`.
3. `jwks-rsa` downloads all signing keys from the JWKS endpoint and checks if one of the signing keys matches the `kid` in the header of the access token. If none of the signing keys match the incoming `kid`, an error is thrown. If there is a match, we pass the right signing key to `express-jwt`.
4. `express-jwt` continues its own logic to validate the signature of the token, the expiration, audience, and the issuer.
5. `jwtAuthz` checks if the scope that the endpoint requires is part of the access token. If the specified scopes are missing from the access token, the request is rejected with a 403 error message.

## Learn more

* [Access Tokens](/docs/secure/tokens/access-tokens)
* [Validate Access Tokens](/docs/secure/tokens/access-tokens/validate-access-tokens)
* [Action Use Cases](/docs/customize/actions/use-cases)
* [API Scopes](/docs/get-started/apis/scopes/api-scopes)
* [Configure Step-up Authentication for Web Apps](/docs/secure/multi-factor-authentication/step-up-authentication/configure-step-up-authentication-for-web-apps)
