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

> Learn about the custom-token-exchange Action trigger's API object.

# API Object

The API object for the custom-token-exchange Actions trigger includes:

## `api.access`

Modify the access of the token exchange request, such as rejecting the request.

### `api.access.deny(code, reason)`

Mark the current token exchange as denied.

If the request is being denied due to an invalid subject token, we recommend that api.access.rejectInvalidSubjectToken be used instead,
to distinguish between brute force attempts on the subject token, and other reasons to deny the request.

```js theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // 1. Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // 2. Apply your authorization policy on the user
  const isAuthorized = await authorizeAccess(subject_token.sub);
  if (!isAuthorized) {
    api.access.deny('Unauthorized_login', 'User cannot login due to reason: X');
  }

  // if user is authorized, go on as indicated here

};
```

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="code" type="string">
    The error code justifying the rejection of the token exchange. Can be invalid\_request, server\_error, or any custom code
  </ParamField>

  <ParamField body="reason" type="string">
    A human-readable explanation for rejecting the token exchange request.
  </ParamField>
</Expandable>

### `api.access.rejectInvalidSubjectToken(reason)`

Mark the provided subject token from the request as invalid. This will cause the request to be
rejected with an "invalid\_request" error code.

This will signal to the Attack Protection features that an invalid subject token has been provided,
so that protections to prevent brute force attacks on the subject token can be applied.

```js theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  try {
    // Validate subject_token
    const subject_token = await validateToken(event.transaction.subject_token, jwksUri);
    // set the user for the transaction
    api.authentication.setUserById(subject_token.id);

  } catch (error) {
    if (error.message === 'Invalid Token') {
      // If specifically the problem is the subject_token is invalid
      console.error('Invalid Token error');
      api.access.rejectInvalidSubjectToken('Invalid subject_token');
    } else {
      // if there is any other unexpected error, throw a server error
      throw error;
    }
  }

};
```

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="reason" type="string">
    A human-readable explanation for rejecting the token exchange request.
  </ParamField>
</Expandable>

## `api.authentication`

Indicate the result of the authentication of the subject token, to specify the user whom tokens will be issued for.

### `api.authentication.setUserById(user_id)`

Indicate the user corresponding to the subject\_token, by providing the userId. The token exchange request will issue tokens for this user.
This must be an existing user.
Note: Exactly one of api.authentication.setUserByConnection api.authentication.setUserById must be called by the Custom Token Exchange action.

```js theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // 1. Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // 2. Apply your authorization policy on the user
  const isAuthorized = await authorizeAccess(subject_token.sub);
  if (!isAuthorized) {
    api.access.deny('Unauthorized_login', 'User cannot login due to reason: X');
  }

  // 3. Set the user for the transaction
  api.authentication.setUserById(subject_token.sub);

  return;
};
```

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="user_id" type="string">
    The ID of the user; must be an existing user.
  </ParamField>
</Expandable>

### `api.authentication.setUserByConnection(connection_name, user_attributes, options)`

Indicate the user corresponding to the subject\_token, by providing a connection and user attributes.
The token exchange request will issue tokens for this user.

This can be either an existing user, or a new user. If the user does not exist, it will be created.
The user\_id property of the user\_profile will be used to determine if the user already exists.

Note: Exactly one of api.authentication.setUserByConnection api.authentication.setUserById must be called by the Custom Token Exchange action.

```js Set user by connection with full profile attributes theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // 1. Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // 2. Apply your authorization policy on the user
  const isAuthorized = await authorizeAccess(subject_token.sub);
  if (!isAuthorized) {
    api.access.deny('Unauthorized_login', 'User cannot login due to reason: X');
  }

  // 3. Set the user for the transaction
  api.authentication.setUserByConnection(
    'My Connection',
    {
      user_id: subject_token.sub,
      email: subject_token.email,
      email_verified: subject_token.email_verified,
      phone_number: subject_token.phone_number,
      phone_verified: subject_token.phone_number_verified,
      username: subject_token.preferred_username,
      name: subject_token.name,
      given_name: subject_token.given_name,
      family_name: subject_token.family_name,
      nickname: subject_token.nickname,
      verify_email: false
    },
    {
      creationBehavior: 'create_if_not_exists',
      updateBehavior: 'none'
    }
  );

  return;
};
```

```js Create a user without verifying email theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // Create a user but don't verify email
  api.authentication.setUserByConnection(
    'My Connection',
    {
      user_id: subject_token.sub,
      email: subject_token.email,
      email_verified: false,
      verify_email: false
    },
    {
      creationBehavior: 'create_if_not_exists',
      updateBehavior: 'none'
    }
  );

  return;
};
```

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="connection_name" type="string">
    Name of the connection the user should be stored in.
  </ParamField>

  <ParamField body="user_attributes" type="customtokenexchangesetuserbyconnectionuserattributes">
    The user's profile attributes, including user\_id, and optionally other attributes such as email, name, etc.

    The user\_id field is required, and should be the unique identifier of the user within the connection;
    this will be used to determine if the user exists or should be created. In existing users, this user\_id
    can be found by inspecting the identities array of the normalized user profile.

    If the user already exists, the following user attributes cannot be updated: email, email\_verified, phone, phone\_verified, username.
    If these do not match the existing user, an error will be returned.

    <Expandable title="user_attributes properties">
      <ParamField body="email" type="string">
        The user's email.
        Optional.
      </ParamField>

      <ParamField body="email_verified" type="boolean">
        Whether this email address is verified (true) or unverified (false).
        Optional.
      </ParamField>

      <ParamField body="family_name" type="string">
        The user's family name(s).
        Optional.
      </ParamField>

      <ParamField body="given_name" type="string">
        The user's given name(s).
        Optional.
      </ParamField>

      <ParamField body="name" type="string">
        The user's full name.
        Optional.
      </ParamField>

      <ParamField body="nickname" type="string">
        The user's nickname.
        Optional.
      </ParamField>

      <ParamField body="phone_number" type="string">
        The user's phone number (following the E.164 recommendation).
        Optional.
      </ParamField>

      <ParamField body="phone_verified" type="boolean">
        Whether this phone number has been verified (true) or not (false).
        Optional.
      </ParamField>

      <ParamField body="picture" type="string">
        A URI pointing to the user's picture.
        Optional.
      </ParamField>

      <ParamField body="user_id" type="string">
        The user's unique identifier within the connection.
      </ParamField>

      <ParamField body="username" type="string">
        The user's username.
        Optional.
      </ParamField>

      <ParamField body="verify_email" type="boolean">
        Whether the user will receive a verification email after creation (true) or no email (false).
        Optional.
      </ParamField>
    </Expandable>
  </ParamField>

  <ParamField body="options" type="customtokenexchangesetuserbyconnectionoptions">
    Options to control the behavior of the setUserByConnection command.

    * `creationBehavior` - behavior to apply if no user with the specified user\_id exists in the connection.
      Can be 'create\_if\_not\_exists', which will cause a new user to be created using the supplied user attributes;
      or 'none', which will result in no user being created and an error being returned if no user exists.

    * `updateBehavior` - Behavior to apply if a user with specified user\_id already exists in the connection.
      Can be 'replace', which results in the existing user's attributes being replaced with the specified
      user attributes; or 'none' which means the existing user will not be modified.

    <Expandable title="options properties">
      <ParamField body="creationBehavior" type="string">
        Behavior to apply if no user with the specified user\_id exists in the connection.
        Allowed values: `create_if_not_exists`, `none`
      </ParamField>

      <ParamField body="updateBehavior" type="string">
        Behavior to apply if a user with specified user\_id already exists in the connection.
        Allowed values: `none`, `replace`
      </ParamField>
    </Expandable>
  </ParamField>
</Expandable>

### `api.authentication.setOrganization(organization_id_or_name)`

Set the organization for the user associated with the token exchange.

```js theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // 1. Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // 2. Apply your authorization policy on the user
  const isAuthorized = await authorizeAccess(subject_token.sub);
  if (!isAuthorized) {
    api.access.deny('Unauthorized_login', 'User cannot login due to reason: X');
  }

  // 3. Set the organization for the transaction
  api.authentication.setOrganization('org_xS525r979AS33MSf');

  // 4. Set the user for the transaction. You may also use setUserByConnection()
  api.authentication.setUserById(subject_token.sub);

  return;
};
```

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="organization_id_or_name" type="string">
    The ID or name of the organization to set for the user.
  </ParamField>
</Expandable>

### `api.authentication.setActor(actor)`

Set the actor for the token exchange to represent the entity acting on behalf of the subject.
Must be used alongside the setUserById or SetUserByConnection commands. Calling setActor is optional.
Receiving an actor\_token in the request does not automatically produce an act claim; the Action must explicitly call this method.
Refresh tokens are not issued when an actor is set for the transaction.

```js theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {

  // 1. Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);
  const actor_token = await validateToken(event.transaction.actor_token, jwksUri);

  // 2. Set the actor for the transaction
  api.authentication.setActor({ sub: actor_token.sub });

  // 3. Set the user for the transaction
  api.authentication.setUserById(subject_token.sub);

  return;
};
```

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="actor" type="actorparams">
    A nested object representing a delegation chain. Up to 4 additional act levels are allowed
    (5 actors total, including the root actor).  For each level, the `sub` field is required; up to 5 additional
    custom properties (string, boolean, or number values) may be provided.

    <Expandable title="actor properties">
      <ParamField body="sub" type="string" />

      <ParamField body="act" type="dictionary">
        Optional.

        <Expandable title="act properties">
          <ParamField body="sub" type="string" />

          <ParamField body="act" type="dictionary">
            Optional.

            <Expandable title="act properties">
              <ParamField body="sub" type="string" />

              <ParamField body="act" type="dictionary">
                Optional.

                <Expandable title="act properties">
                  <ParamField body="sub" type="string" />

                  <ParamField body="act" type="dictionary">
                    Optional.

                    <Expandable title="act properties">
                      <ParamField body="sub" type="string" />

                      <ParamField body="act" type="dictionary">
                        Optional.
                      </ParamField>
                    </Expandable>
                  </ParamField>
                </Expandable>
              </ParamField>
            </Expandable>
          </ParamField>
        </Expandable>
      </ParamField>
    </Expandable>
  </ParamField>
</Expandable>

## `api.user`

Request changes to the user corresponding to the subject token.

### `api.user.setAppMetadata(key, value)`

Set application-specific metadata for the user corresponding to the subject token.

```js theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {
  // Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // set the user for the transaction
  api.authentication.setUserById(subject_token.id);

  // set user group based on info contained in subject_token
  api.user.setAppMetadata('group', subject_token.group);

  return;
};
```

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="key" type="string">
    The metadata property to be set.
  </ParamField>

  <ParamField body="value" type="unknown">
    The value of the metadata property. This may be set to `null` to remove the
    metadata property.
  </ParamField>
</Expandable>

### `api.user.setUserMetadata(key, value)`

Set general metadata for the user corresponding to the subject token.

```js theme={null}
exports.onExecuteCustomTokenExchange = async (event, api) => {
  // Validate subject_token
  const subject_token = await validateToken(event.transaction.subject_token, jwksUri);

  // set the user for the transaction
  api.authentication.setUserById(subject_token.id);

  // set user preferred_locale based on info contained in subject_token
  api.user.setUserMetadata('preferred_locale', subject_token.locale);

  return;
};
```

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="key" type="string">
    The metadata property to be set.
  </ParamField>

  <ParamField body="value" type="unknown">
    The value of the metadata property. This may be set to `null` to remove the
    metadata property.
  </ParamField>
</Expandable>

## `api.cache`

Store and retrieve data that persists across executions.

### `api.cache.delete(key)`

Delete a record describing a cached value at the supplied
key if it exists.

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="key" type="string">
    The key of the cache record to delete.
  </ParamField>
</Expandable>

### `api.cache.get(key)`

Retrieve a record describing a cached value at the supplied key,
if it exists. If a record is found, the cached value can be found
at the `value` property of the returned object.

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="key" type="string">
    The key of the record stored in the cache.
  </ParamField>
</Expandable>

### `api.cache.set(key, value, options)`

Store or update a string value in the cache at the specified key.

Values stored in this cache are scoped to the Trigger in which they
are set. They are subject to the [Actions Cache Limits](https://auth0.com/docs/customize/actions/limitations).

Values stored in this way will have lifetimes of *up to* the specified
`ttl` or `expires_at` values. If no lifetime is specified, a default of
lifetime of 15 minutes will be used. Lifetimes may not exceed the maximum
duration listed at [Actions Cache Limits](https://auth0.com/docs/customize/actions/limitations).

**Important**: This cache is designed for short-lived, ephemeral data. Items may not be
available in later transactions even if they are within their supplied their lifetime.

**Parameters**

<Expandable title="Parameters" defaultOpen>
  <ParamField body="key" type="string">
    The key of the record to be stored.
  </ParamField>

  <ParamField body="value" type="string">
    The value of the record to be stored.
  </ParamField>

  <ParamField body="options" type="cachesetoptions">
    Options for adjusting cache behavior.
    Optional.

    <Expandable title="options properties">
      <ParamField body="expires_at" type="number">
        The absolute expiry time in milliseconds since the unix epoch.
        While cached records may be evicted earlier, they will
        never remain beyond the supplied `expires_at`.

        *Note*: This value should not be supplied if a value was also
        provided for `ttl`. If both options are supplied, the
        earlier expiry of the two will be used.
        Optional.
      </ParamField>

      <ParamField body="ttl" type="number">
        The time-to-live value of this cache entry in milliseconds.
        While cached values may be evicted earlier, they will
        never remain beyond the supplied `ttl`.

        *Note*: This value should not be supplied if a value was also
        provided for `expires_at`. If both options are supplied, the
        earlier expiry of the two will be used.
        Optional.
      </ParamField>
    </Expandable>
  </ParamField>
</Expandable>
