Skip to main content
Auth0 provides a number of API methods to use with your Token Exchange Action. You should implement an Action that decodes and validates the subject token based on the subject_token_type. This will provide you with information about the user for the transaction. With this information, your code should also enforce the authorization policy needed for the transaction. Once you are sure the transaction can proceed, you can confirm it by setting the corresponding user. Auth0 will then issue access, ID, and refresh tokens for this user. You can think of this as a way to authenticate users. Each Custom Token Exchange transaction generates a tenant event log. Successful transactions generate event logs of type secte, while failed transactions generate event logs of type fecte. Use these log types to help you understand any errors you may receive. Errors from the /oauth/token endpoint reveal less details.
Custom Token Exchange gives you the added flexibility to set the user for the transaction by taking on the additional responsibility of securely validating the corresponding subject token that identifies the user for the transaction.Remember that subject tokens used with Custom Token Exchange can be any token format or type you require, as long as your Action code can interpret them. You must implement strong validation of the tokens you receive and accept. If you fail to do so, you open yourself up to different attack vectors, such as spoofing or replay attacks, resulting in bad actors being able to authenticate with someone else’s user ID.To learn about different options for implementing secure validation of your subject tokens, read and apply the recommendations included in Example Use Cases and Code Samples. Make sure you also take into consideration and apply Attack Protection capabilities.

api.authentication.setUserById(user_id)

Sets user attributes based on a specified user ID for any connection type. This allows you to specify an existing user without updating the profile. This method fails if the user does not exist or is blocked.
ParameterDescription
user_idThe user ID, such as auth0|55562040asf0aef.
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;
};

api.authentication.setUserByConnection(connection_name, user_profile, options)

Sets a user and their associated profile attributes in a specified connection. This is equivalent to returning the specified user profile from the federated IdP (or the corresponding Custom DB with Import Users to Auth0 disabled) when a user logs into this connection. You can configure whether this operation should create the user if it does not exist, and whether it should update the profile using the provided user profile attributes. The login count will be incremented for each user that is logged in via setUserByConnection(). This method always fails for blocked users.
Custom Token Exchange EA currently supports setUserByConnection() for Auth0 database connections, Enterprise and Social connections, and Custom DBs with Import Users to Auth0 disabled.
ParameterDescription
connection_nameThe name of the connection where the user profile will be set. Limited to 512 characters.
user_profileAn object containing the user profile attributes to be set. Limited to 24 properties.
optionsAn object specifying update and creation behavior.

{updateBehavior: ‘replace’ | ‘none’,creationBehavior: ‘create_if_not_exists’ | ‘none’,}

If the user exists, updateBahaviour does the following:
  • replace: the user attributes and the user_id for the provided connection are replaced (existing user attributes that are not provided will be removed from the user. Partial updates are not supported).
  • none: if the user exists, the profile is not updated. If the user does not exist, it will be created with the provided profile attributes depending on creationBehavior configuration.
  • If the user does not exist, creationBehavior does the following:
    • create_if_not_exists: create the user
    • none: does not create the user and return with an error
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;
};
Supported user profile attributes
The setUserByConnection() method allows you to set profile attributes supported by the Update a User endpoint:
  • user_id (required): user’s unique identifier for this connection/provider. It is typically the user ID provided by the external identity provider for the connection. This is the only required parameter when both creationBehaviour and updateBehaviour are set to none.
  • email
  • email_verified. Defaults to false.
  • username
  • phone_number
  • phone_verified. Defaults to false.
  • name
  • given_name
  • family_name
  • nickname
  • picture
Use metadata fields if you need to set attributes not considered in the above list.
Supported connection strategies
The current version provides support for the following connection strategies. The setUserByConnection() method fails for other strategies. Please contact Auth0 support to request adding support for other strategies. Auth0 database connections: Enterprise connections: Social connections:
  • Custom Social Connections
  • Google
  • Apple
  • Facebook
  • Github
  • Windowslive
Creation behavior
Users are dynamically created only when creationBehavior is set to create_if_not_exists. When creating users:
  • You must provide an identifier as configured by your connection. By default, an email is required.
  • For connections that use Flexible Identifiers and Attributes, you may provide a username and phone number if the corresponding attribute is enabled for the connection.
  • For connections that don’t use Flexible Identifiers and Attributes:
    • You must provide an email address.
    • You may provide a username when the connection’s Require Username is set to true. To learn more, read Adding Username for Database Connections.
    • You cannot provide a phone_number.
    • You may specify email_verified and phone_verified.
A random password is generated for users dynamically created in Auth0 database connections. There are different options to trigger a password reset flow when needed after user creation.
Set creationBehavior to none when you want to log the user in but don’t want to create the user if they do not already exist in the connection.Future iterations of Custom Token Exchange will make the email attribute optional dependent on the connection configuration.
Update behavior
User profile is updated only when updateBehavior is set to replace. The following attributes cannot be modified and Auth0 returns an error when trying to change its value:
  • email
  • username
  • phone_number
  • email_verified
  • phone_verified
If you want to use setUserByConnection() to update a user profile that already contains email, username, or phone_number attributes, you must pass these attributes with the same value they already have. Otherwise, the method returns an error. Additionally, this action does not update the upstream Enterprise/Social IdP or the Custom DB, so make your updates match the latest version of the profile on the IdP/Custom DB if you want them to be in sync.
Set updateBehavior to none when you want to log the user in but don’t want to change any profile attributes if they already exist in the connection.
Email verification
Auth0 automatically sends verification emails when you create a user with email_verified=false. You can override this behavior by specifying verify_email=false as a user profile attribute. It won’t be stored as part of the user profile.
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;
};
If you have configured and enabled a welcome email template, Auth0 automatically sends a welcome email to newly created users when no email verification is sent.

api.authentication.setOrganization(organization)

Sets the organization for the transaction. Useful when you want to set an organization even when the incoming Custom Token Exchange request does not explicitly indicate one. When the incoming request includes an organization parameter and you set one with this method, both need to match to the same organization or the Action will fail.
ParameterDescription
organization

String. If the Use Organization Names in Authentication API is allowed for your tenant, you can provide the org_id or the organization name as the value for the parameter. If it is not, you have to provide the org_id or the method will fail.

Example

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.setOrginzation('org_xS525r979AS33MSf');

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

  return;
};

Application configuration

To be able to set an organization to the transaction, the client making the Custom Token Exchange request must be able to use Organizations. Make sure organization_usage is set to allow or require. To learn more, read Define Organization Behaviour.

Membership validation

For the setOrganization() method to succeed, the following rules regarding connections and organization membership apply:
  • The connection must be enabled for the target organization. To determine that:
    • When using setUserById(): The system relies on the user’s profile primary connection.
    • When using setUserByConnection(): The system uses the specific connection provided in the method call, even if it is a secondary connection for the user in the case of linked accounts.
  • The user must be a member of the organization:
    • setUserById(): Auto-membership does not apply. The user must already be a member of the organization for the transaction to succeed.
    • setUserByConnection(): Auto-membership applies if configured for the corresponding connection. If the user is not a member and auto-membership is enabled, they are automatically added, and the transaction succeeds. If auto-membership is disabled and the user is not a member, the transaction fails.
Organization sign-up
User creation is permitted irrespective of the organization sign-up configuration on the connection.

Set metadata

Unlike the Update a User endpoint, the setUserByConnection() method does not allow you to set user or application metadata. Instead, you can use api.user.setAppMetadata. To learn how to correctly use metadata, read How Metadata Works in User Profiles. For metadata best practices, read How to Manage User Metadata with the Post-login Trigger.

api.user.setAppMetadata(name, value)

Sets application metadata for the user that is logging in. This method follows a merge behavior, so you can indicate the new attributes to add or the ones to be updated without affecting the existing ones. To remove an attribute, set its value to null.
ParametersDescription
nameString. The name of the metadata property.
valueString, object or array. The name of the metadata property.
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 contaiened in subject_token
  api.user.setAppMetadata('group', subject_token.group);

  return;
};

api.user.setUserMetadata(name, value)

Sets general metadata for the user that is logging in. This method follows a merge behavior, so you can indicate the new attributes to add or the ones to be updated without affecting the already existing ones. To remove an attribute, set its value to null.
ParametersDescription
nameString. The name of the metadata property.
valueString, object or array. The name of the metadata property.
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 contaiened in subject_token
  api.user.setUserMetadata('preferred_locale', subject_token.locale);

  return;
};

api.access.deny(code, reason)

Denies the login transaction and returns an error to the caller.
ParameterDescription
codeA string returned in the error property in the response.

Two standard error codes can be used:
  • invalid_request: Returns a 400 status code
  • server_error: Returns 500 status code

If you use your own error code, it returns a 400 status code.
reasonA string returned in the error_description property in the response.
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

};

api.access.rejectInvalidSubjectToken(reason)

Denies the transaction and increments the counter of failed attempts for the external IP from which the request is coming. The Custom Token Exchange request is rejected with a 400 Bad Request error response with the error code invalid_request. When the maximum number of failed attempts is reached, Auth0 blocks traffic for a period of time for all Custom Token Exchange requests coming from that IP with a 429 Too Many Requests error response with error code too_many_attempts. To learn more read Attack Protection. Use this method whenever you receive a Custom Token Exchange request with a subject token that is not properly signed/encrypted or expired, or under any circumstance that points to any non-legitimate usage such as in a spoofing or replay attack. This allows Auth0 to activate protection as per your configuration. By default, Suspicious IP Throttling allows for 10 max attempts at a rate of 6 attempts/hour. To learn more, read Attack Protection.
ParameterDescription
reasonA string returned in the error_description property in the response
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;
    }
  }

};

api.cache

Store and retrieve data that persists across executions. These methods are useful for caching data used for subject token validation, such as public keys for signature validation. This can help improve performance when fetching the keys from a jwks-uri.

api.cache.delete(key)

Delete a record describing a cached value at the supplied key if it exists. Returns a CacheWriteResult object with type: "success" if a value was removed from the cache. A failed operation returns type: "error". For errors, the returned object will have a code property that indicates the nature of the failure.
ParameterDescription
keyString. The key of the record stored in the cache.

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. Returns a cache record if an item is found in the cache for the supplied key. Cache records are objects with a value property holding the cached value as well as an expires_at property indicating the maximum expiry of the record in milliseconds since the Unix epoch. 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 lifetime.
ParameterDescription
keyString. The key of the record stored in the cache.

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. 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 lifetime of 15 minutes will be used. Lifetimes may not exceed the maximum duration listed at Actions Cache Limits. Returns CacheWriteSuccess if the values are stored successfully. Otherwise, you will receive CacheWriteError.
ParameterDescription
keyString. The key of the record stored in the cache.
valueString. The value of the record to be stored.
optionsOptional object. Options for adjusting cache behavior.
options.expires_atOptional 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.
options.ttlOptional 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.