type CacheWriteErrorCode = | 'MaxSideEffectsExceeded' | 'CacheKeySizeExceeded' | 'CacheValueSizeExceeded' | 'CacheSizeExceeded' | 'ItemAlreadyExpired' | 'InvalidExpiry' | 'FailedToSetCacheRecord' | 'FailedToDeleteCacheRecord' | 'CacheKeyDoesNotExist'; /** * Details about a cached value. */ interface CacheRecord { /** * The cached value itself. */ value: string; /** * Expiry time in milliseconds since the unix epoch. */ expires_at: number; } interface CacheWriteSuccess { type: 'success'; record: CacheRecord; } interface CacheWriteError { type: 'error'; code: CacheWriteErrorCode; } type CacheWriteResult = CacheWriteSuccess | CacheWriteError; interface CacheDeleteSuccess { type: 'success'; } type CacheDeleteResult = CacheDeleteSuccess | CacheWriteError; interface CacheSetOptions { /** * 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. */ expires_at?: 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. */ ttl?: number; } /** * Methods and utilities to manage the Actions cache. */ interface CacheAPI { /** * Delete a record describing a cached value at the supplied * key if it exists. * * @param key The key of the cache record to delete. */ delete(key: string): CacheDeleteResult; /** * 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. * * @param key The key of the record stored in the cache. */ get(key: string): CacheRecord | undefined; /** * 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 {@link https://auth0.com/docs/customize/actions/limitations 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 of * lifetime of 15 minutes will be used. Lifetimes may not exceed the maximum * duration listed at {@link https://auth0.com/docs/customize/actions/limitations Actions Cache Limits}. * * **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. * * @param key The key of the record to be stored. * @param value The value of the record to be stored. * @param options Options for adjusting cache behavior. */ set(key: string, value: string, options?: CacheSetOptions): CacheWriteResult; } /** CustomTokenExchangeV1Event */ type CustomTokenExchangeV1Event = { /** Information about the Client with which this transaction was initiated. */ client: { /** The client id of the application the user is logging in to. */ client_id: string; /** An object for holding other application properties. */ metadata: { [additionalProperties: string]: string; }; /** The name of the application (as defined in the Dashboard). */ name: string; }; /** Details about the Organization associated with the current transaction. */ organization?: { /** The Organization identifier. */ id: string; /** The friendly name of the Organization. */ display_name: string; /** Metadata associated with the Organization. */ metadata: { [additionalProperties: string]: string; }; /** The name of the Organization. */ name: string; } & { [additionalProperties: string]: any; }; /** Details about the request that initiated the transaction. */ request: { /** The body of the POST request. This data will only be available during refresh token, Client Credential Exchange flows and PreUserRegistration Action. */ body: { [additionalProperties: string]: any; }; geoip: { cityName?: string; continentCode?: string; countryCode?: string; countryCode3?: string; countryName?: string; latitude?: number; longitude?: number; subdivisionCode?: string; subdivisionName?: string; timeZone?: string; } & { [additionalProperties: string]: any; }; /** The hostname that is being used for the authentication flow. */ hostname?: string; /** The originating IP address of the request. */ ip: string; /** The language requested by the browser. */ language?: string; /** The HTTP method used for the request */ method: string; /** The value of the `User-Agent` header received when initiating the transaction. */ user_agent?: string; }; /** Details about the resource server to which the access is being requested. */ resource_server: { /** The identifier of the resource server. For example: `https://your-api.example.com`. */ identifier: string; }; /** Details about the Tenant associated with the current transaction. */ tenant: { /** The name of the tenant. */ id: string; }; /** Details about the current custom token exchange transaction. */ transaction: { /** The actor token provided in the token exchange request. */ actor_token?: string; /** The type of the actor token provided in the token exchange request. */ actor_token_type?: string; /** The user represented by the actor token. This will only be present if the actor_token_type is urn:ietf:params:oauth:token-type:id_token and the actor token provided in the token exchange request is a valid Auth0 generated ID token. */ actor_token_user?: { /** Custom fields that store info about a user that influences the user's access, such as support plan, security roles, or access control groups. */ app_metadata: { [additionalProperties: string]: any; }; /** Timestamp indicating when the user profile was first created. */ created_at: string; /** (unique) User's email address. */ email?: string; /** Indicates whether the user has verified their email address. */ email_verified: boolean; /** User's family name. */ family_name?: string; /** User's given name. */ given_name?: string; /** Timestamp indicating the last time the user's password was reset/changed. At user creation, this field does not exist. This property is only available for Database connections. */ last_password_reset?: string; /** User's full name. */ name?: string; /** User's nickname. */ nickname?: string; /** User's phone number. */ phone_number?: string; /** Indicates whether the user has verified their phone number. */ phone_verified?: boolean; /** URL pointing to the [user's profile picture](https://auth0.com/docs/users/change-user-picture). */ picture?: string; /** Timestamp indicating when the user's profile was last updated/modified. */ updated_at: string; /** (unique) User's unique identifier. */ user_id: string; /** Custom fields that store info about a user that does not impact what they can or cannot access, such as work address, home address, or user preferences. */ user_metadata: { [additionalProperties: string]: any; }; /** (unique) User's username. */ username?: string; /** An array of authentication factors that the user has enrolled. */ enrolledFactors?: ({ /** The type of authentication factor such as `push-notification`, `phone`, `email`, `otp`, `webauthn-roaming` and `webauthn-platform`. */ type: string; /** Additional options describing this instance of the enrolled factor. */ options?: { [additionalProperties: string]: any; }; } & { [additionalProperties: string]: any; })[]; /** List of multi-factor authentication (MFA) providers with which the user is enrolled. This array is updated when the user enrolls in MFA and when an administrator resets a user's MFA enrollments. */ multifactor?: string[]; /** Contains info retrieved from the identity provider with which the user originally authenticates. Users may also link their profile to multiple identity providers; those identities will then also appear in this array. The contents of an individual identity provider object varies by provider. */ identities: ({ /** Name of the Auth0 connection used to authenticate the user. */ connection?: string; /** Indicates whether the connection is a social one. */ isSocial?: boolean; /** User information associated with the connection. When profiles are linked, it is populated with the associated user info for secondary accounts. */ profileData?: { [additionalProperties: string]: string; }; /** Name of the entity that is authenticating the user, such as Facebook, Google, SAML, or your own provider. */ provider?: string; /** User's unique identifier for this connection/provider. */ user_id?: string; } & { [additionalProperties: string]: any; })[]; } & { [additionalProperties: string]: any; }; /** [Early Access] An object containing shared data across custom Actions for the duration of a transaction. */ metadata?: { [additionalProperties: string]: string | number | boolean; }; /** The scopes requested (if any) provided in the token exchange request. */ requested_scopes: string[]; /** The type of token to be generated by Auth0. For example: urn:ietf:params:oauth:token-type:access_token. */ requested_token_type: string | null; /** The subject token provided in the token exchange request. */ subject_token: string; /** The subject_token_type provided in the token exchange request. */ subject_token_type: string; }; }; /** Options to control the behavior of the setUserByConnection command. */ type CustomTokenExchangeSetUserByConnectionOptions = { /** Behavior to apply if no user with the specified user_id exists in the connection. */ creationBehavior: 'create_if_not_exists' | 'none'; /** Behavior to apply if a user with specified user_id already exists in the connection. */ updateBehavior: 'replace' | 'none'; }; /** An object containing the user profile attributes to set. */ type CustomTokenExchangeSetUserByConnectionUserAttributes = { /** The user's email. */ email?: string; /** Whether this email address is verified (true) or unverified (false). */ email_verified?: boolean; /** The user's family name(s). */ family_name?: string; /** The user's given name(s). */ given_name?: string; /** The user's full name. */ name?: string; /** The user's nickname. */ nickname?: string; /** The user's phone number (following the E.164 recommendation). */ phone_number?: string; /** Whether this phone number has been verified (true) or not (false). */ phone_verified?: boolean; /** A URI pointing to the user's picture. */ picture?: string; /** The user's unique identifier within the connection. */ user_id: string; /** The user's username. */ username?: string; /** Whether the user will receive a verification email after creation (true) or no email (false). */ verify_email?: boolean; } & { [additionalProperties: string]: any; }; type TxMetadataValue = string | boolean | number; /** Recursively defines nested actor levels, terminating when the depth tuple is exhausted. */ type NestedActor = D extends [unknown, ...infer Rest] ? { sub: string; act?: NestedActor; } & Record : never; /** Nested actor representing a delegation chain. Max 5 levels (root + 4 nested). */ type ActorParams = { sub: string; act?: NestedActor; } & Record; interface Configuration {} interface Secrets { [secretName: string]: string; } interface Event extends CustomTokenExchangeV1Event { /** * @private Configuration values associated with this Action. */ configuration: Configuration; /** * Secret values securely associated with this Action. */ secrets: Secrets; } interface AccessAPI { /** * 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. * * @param code The error code justifying the rejection of the token exchange. Can be invalid_request, server_error, or any custom code * @param reason A human-readable explanation for rejecting the token exchange request. * * @example * ```js * 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 * * }; * ``` */ deny(code: string, reason: string): void; /** * 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. * * @param reason A human-readable explanation for rejecting the token exchange request. * * @example * ```js * 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; * } * } * * }; * ``` */ rejectInvalidSubjectToken(reason: string): void; } interface AuthenticationAPI { /** * 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. * * @param user_id The ID of the user; must be an existing user. * * @example * ```js * 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; * }; * ``` */ setUserById(user_id: string): void; /** * 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. * * @param connection_name Name of the connection the user should be stored in. * * @param user_attributes * 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. * * @param options * 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. * * @example * Set user by connection with full profile attributes: * ```js * 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; * }; * ``` * * @example * Create a user without verifying email: * ```js * 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; * }; * ``` */ setUserByConnection( connection_name: string, user_attributes: CustomTokenExchangeSetUserByConnectionUserAttributes, options: CustomTokenExchangeSetUserByConnectionOptions ): void; /** * Set the organization for the user associated with the token exchange. * * @param organization_id_or_name The ID or name of the organization to set for the user. * * @example * ```js * 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; * }; * ``` */ setOrganization(organization_id_or_name: string): void; /** * 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. * * @param actor 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. * * @example * ```js * 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; * }; * ``` */ setActor(actor: ActorParams): void; } interface TransactionAPI { /** * [Early Access] Store or update the value in the transaction metadata for a specified key. * * Metadata modified using this method is updated in real-time in the * `event.transaction.metadata` object. * * @param key The key of the property to be set. * @param value The value of the property. This may be set to `null` to remove the * metadata property. * * @example * ```js * exports.onExecuteCustomTokenExchange = async (event, api) => { * // Store data to share across Actions for the duration of the transaction. * api.transaction.setMetadata('subject_verified', true); * api.transaction.setMetadata('risk_score', 42); * * // Read it back from the event in real-time. * console.log(event.transaction.metadata.risk_score); // 42 * * // Remove a previously set property by passing `null`. * api.transaction.setMetadata('risk_score', null); * }; * ``` */ setMetadata(key: string, value: TxMetadataValue | null): void; } interface UserAPI { /** * Set application-specific metadata for the user corresponding to the subject token. * * @param key The metadata property to be set. * @param value The value of the metadata property. This may be set to `null` to remove the * metadata property. * * @example * ```js * 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; * }; * ``` */ setAppMetadata(key: string, value: unknown): void; /** * Set general metadata for the user corresponding to the subject token. * * @param key The metadata property to be set. * @param value The value of the metadata property. This may be set to `null` to remove the * metadata property. * * @example * ```js * 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; * }; * ``` */ setUserMetadata(key: string, value: unknown): void; } /** * Methods and utilities to help change the behaviour of the custom token exchange flow. */ interface CustomTokenExchangeAPI { /** * Modify the access of the token exchange request, such as rejecting the request. */ readonly access: AccessAPI; /** * Indicate the result of the authentication of the subject token, to specify the user whom tokens will be issued for. */ readonly authentication: AuthenticationAPI; /** * Request changes to the user corresponding to the subject token. */ readonly user: UserAPI; /** * Store and retrieve data that persists across executions. */ readonly cache: CacheAPI; /** * [Early Access] Make changes to the transaction. */ readonly transaction: TransactionAPI; } interface CustomTokenExchangeAction { (event: Event, api: CustomTokenExchangeAPI): Promise; } type CustomTokenExchangeModule = { onExecuteCustomTokenExchange: CustomTokenExchangeAction; }; export type { Configuration, CustomTokenExchangeAPI, CustomTokenExchangeAction, CustomTokenExchangeModule, Event, Secrets, };