diff --git a/package.json b/package.json index 1721af4..5c4ce35 100644 --- a/package.json +++ b/package.json @@ -36,26 +36,50 @@ "types": "./src/credentials-exchange/v2/index.d.ts", "default": null }, + "./credentials-exchange/v2/test": { + "types": "./src/credentials-exchange/v2/test/index.d.ts", + "default": "./src/credentials-exchange/v2/test/index.js" + }, "./custom-email-provider/v1": { "types": "./src/custom-email-provider/v1/index.d.ts", "default": null }, + "./custom-email-provider/v1/test": { + "types": "./src/custom-email-provider/v1/test/index.d.ts", + "default": "./src/custom-email-provider/v1/test/index.js" + }, "./custom-phone-provider/v1": { "types": "./src/custom-phone-provider/v1/index.d.ts", "default": null }, + "./custom-phone-provider/v1/test": { + "types": "./src/custom-phone-provider/v1/test/index.d.ts", + "default": "./src/custom-phone-provider/v1/test/index.js" + }, "./custom-token-exchange/v1": { "types": "./src/custom-token-exchange/v1/index.d.ts", "default": null }, + "./custom-token-exchange/v1/test": { + "types": "./src/custom-token-exchange/v1/test/index.d.ts", + "default": "./src/custom-token-exchange/v1/test/index.js" + }, "./event-stream/v1": { "types": "./src/event-stream/v1/index.d.ts", "default": null }, + "./event-stream/v1/test": { + "types": "./src/event-stream/v1/test/index.d.ts", + "default": "./src/event-stream/v1/test/index.js" + }, "./password-reset-post-challenge/v1": { "types": "./src/password-reset-post-challenge/v1/index.d.ts", "default": null }, + "./password-reset-post-challenge/v1/test": { + "types": "./src/password-reset-post-challenge/v1/test/index.d.ts", + "default": "./src/password-reset-post-challenge/v1/test/index.js" + }, "./post-change-password/v1": { "types": "./src/post-change-password/v1/index.d.ts", "default": null @@ -64,6 +88,10 @@ "types": "./src/post-change-password/v2/index.d.ts", "default": null }, + "./post-change-password/v2/test": { + "types": "./src/post-change-password/v2/test/index.d.ts", + "default": "./src/post-change-password/v2/test/index.js" + }, "./post-login/v1": { "types": "./src/post-login/v1/index.d.ts", "default": null @@ -76,6 +104,10 @@ "types": "./src/post-login/v3/index.d.ts", "default": null }, + "./post-login/v3/test": { + "types": "./src/post-login/v3/test/index.d.ts", + "default": "./src/post-login/v3/test/index.js" + }, "./post-user-registration/v1": { "types": "./src/post-user-registration/v1/index.d.ts", "default": null @@ -84,6 +116,10 @@ "types": "./src/post-user-registration/v2/index.d.ts", "default": null }, + "./post-user-registration/v2/test": { + "types": "./src/post-user-registration/v2/test/index.d.ts", + "default": "./src/post-user-registration/v2/test/index.js" + }, "./pre-user-registration/v1": { "types": "./src/pre-user-registration/v1/index.d.ts", "default": null @@ -92,6 +128,10 @@ "types": "./src/pre-user-registration/v2/index.d.ts", "default": null }, + "./pre-user-registration/v2/test": { + "types": "./src/pre-user-registration/v2/test/index.d.ts", + "default": "./src/pre-user-registration/v2/test/index.js" + }, "./send-phone-message/v1": { "types": "./src/send-phone-message/v1/index.d.ts", "default": null @@ -99,6 +139,10 @@ "./send-phone-message/v2": { "types": "./src/send-phone-message/v2/index.d.ts", "default": null + }, + "./send-phone-message/v2/test": { + "types": "./src/send-phone-message/v2/test/index.d.ts", + "default": "./src/send-phone-message/v2/test/index.js" } } } diff --git a/src/_shared/Bi7NRjgy.js b/src/_shared/Bi7NRjgy.js new file mode 100644 index 0000000..6cde24e --- /dev/null +++ b/src/_shared/Bi7NRjgy.js @@ -0,0 +1,93 @@ +'use strict'; + +var index = require('./DvTaCl9e.js'); + +const { assertValidScope, MAX_SCOPE_COUNT_LIMIT } = index.helpers.accessToken; +/** + * Implements the {@link TargetScopesAPI} for managing target scopes: validates + * arguments, tracks the scope set, and delegates to a + * {@link TargetScopesWriterAPI} to record the resulting command. + * + * Note: commands are recorded before the local scope set is updated, so a + * writer failure leaves the scope set unchanged. + */ +class TargetScopesAPIImpl { + #scopesWriter; + #targetScopes; + constructor(scopesWriter, initialTargetScopes) { + this.#scopesWriter = scopesWriter; + this.#targetScopes = new Set(initialTargetScopes); + } + addTargetScope(scope) { + if (typeof scope !== 'string') { + throw new TypeError('The value for the scope must be a string.'); + } + const trimmedScope = scope.trim(); + assertValidScope(trimmedScope); + if (!this.#targetScopes.has(trimmedScope) && this.#targetScopes.size >= MAX_SCOPE_COUNT_LIMIT) { + throw new Error(`The number of scopes exceeds the allowed maximum of ${MAX_SCOPE_COUNT_LIMIT}.`); + } + this.#scopesWriter.addTargetScope(trimmedScope); + this.#targetScopes.add(trimmedScope); + } + removeTargetScope(scope) { + if (typeof scope !== 'string') { + throw new TypeError('The value for the scope must be a string.'); + } + const trimmedScope = scope.trim(); + assertValidScope(trimmedScope); + this.#scopesWriter.removeTargetScope(trimmedScope); + this.#targetScopes.delete(trimmedScope); + } + setTargetScopes(scopes) { + if (!Array.isArray(scopes)) { + throw new TypeError('The value for scopes must be an array.'); + } + // Empty array is a valid input and should clear all target scopes + if (scopes.length === 0) { + return this.clearTargetScopes(); + } + for (const scope of scopes) { + if (typeof scope !== 'string') { + throw new TypeError('The value for the scope must be a string.'); + } + assertValidScope(scope.trim()); + } + const uniqueScopes = [...new Set(scopes.map((s) => s.trim()))]; + if (uniqueScopes.length > MAX_SCOPE_COUNT_LIMIT) { + throw new Error(`The number of scopes exceeds the allowed maximum of ${MAX_SCOPE_COUNT_LIMIT}.`); + } + this.#scopesWriter.setTargetScopes(uniqueScopes); + this.#targetScopes.clear(); + for (const s of uniqueScopes) { + this.#targetScopes.add(s); + } + } + clearTargetScopes() { + this.#scopesWriter.clearTargetScopes(); + this.#targetScopes.clear(); + } + getTargetScopes() { + // Return a copy to prevent external mutation of the scope set + return [...this.#targetScopes]; + } +} +/** + * A no-op {@link TargetScopesWriterAPI}. Backs stub trigger APIs so the target + * scope methods still validate and reads still reflect prior writes. + */ +class NoopTargetScopesWriterAPI { + addTargetScope(_scope) { } + removeTargetScope(_scope) { } + setTargetScopes(_scopes) { } + clearTargetScopes() { } +} +/** + * Creates a {@link TargetScopesAPI} backed by {@link NoopTargetScopesWriterAPI} + * for use in stub trigger API implementations. + */ +function createNoopTargetScopesAPI(initialTargetScopes = []) { + return new TargetScopesAPIImpl(new NoopTargetScopesWriterAPI(), initialTargetScopes); +} + +exports.createNoopTargetScopesAPI = createNoopTargetScopesAPI; diff --git a/src/_shared/BquXyhu2.d.ts b/src/_shared/BquXyhu2.d.ts new file mode 100644 index 0000000..c7feb68 --- /dev/null +++ b/src/_shared/BquXyhu2.d.ts @@ -0,0 +1,118 @@ +interface Handler { + (...args: any[]): Promise; +} +interface Execution { + /** Returns all stdout/stderr output written by the action via console. */ + getLogs(): string; +} +/** An actions: module to register up front, so the action can require('actions:'). */ +interface ModuleRegistration { + name: string; + filename: string; + secrets?: Record; +} +interface LoadedAction> { + /** Invokes the loaded action's export named `entrypoint`, e.g. `action.execute('onExecutePostLogin', event, api)`. */ + execute( + entrypoint: K, + ...args: Parameters + ): Promise; +} +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; +} +export type { CacheAPI as C, LoadedAction as L, ModuleRegistration as M }; diff --git a/src/_shared/CUlF8oaW.d.ts b/src/_shared/CUlF8oaW.d.ts new file mode 100644 index 0000000..9e6ac37 --- /dev/null +++ b/src/_shared/CUlF8oaW.d.ts @@ -0,0 +1,1867 @@ +/** 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; + }; +}; +/** EventStreamV1Event */ +type EventStreamV1Event = { + /** The CloudEvent message containing all event properties. */ + message: { + /** Identifies the event. */ + id: string; + /** Describes the type of event related to the originating occurrence. */ + type: string; + /** The event payload. */ + data?: { + [additionalProperties: string]: any; + } | null; + /** Identifies the context in which an event happened. */ + source: string; + /** The version of the CloudEvents specification which the event uses. */ + specversion: string; + /** Timestamp of when the occurrence happened. Must adhere to RFC 3339. */ + time?: string | null; + /** The Auth0 tenant identifier to which the event is associated. */ + a0tenant: string; + /** + * The Auth0 event stream ID of the stream the event was delivered on. + * Present when the event is delivered via an event stream; omitted when + * events are retrieved via the Events API (GET /api/v2/events). + */ + a0stream?: string; + /** The purpose of this event. Set only in special cases such as a test event; omitted for normal events. */ + a0purpose?: 'test' & string; + }; +}; +/** + * PasswordResetPostChallengeV1Event + * + * Event Object for the Password Reset Post Challenge + */ +type PasswordResetPostChallengeV1Event = { + /** Details about authentication obtained during the password reset flow. */ + authentication: { + /** Contains the authentication methods a user has completed during their session. */ + methods: ( + | { + /** + * The name of the first factor that was completed. Values include the following: + * - `federated` A social or enterprise connection was used to authenticate the user as the first factor. + * - `pwd` A password was used to authenticate a database connection user as the first factor. + * - `passkey` A passkey was used to authenticate a database connection user as the first factor. + * - `sms` A Passwordless SMS connection was used to authenticate the user as the first factor. + * - `email` A Passwordless Email connection was used to authenticate the user as the first factor or verify email for password reset. + * - `phone_number` A phone number was used for password reset. + * - `mock` Used for internal testing. + * - May also be a URL denoting a custom authentication method (as second or later factor). + * @summary First Factor + */ + name: string; + timestamp: string; + } + | { + /** + * The user completed multi-factor authentication (second or later factors). + * @summary Multi-factor Authentication + */ + name: 'mfa'; + timestamp: string; + } + )[]; + /** Supplemental risk assessment. This is available only if the Akamai Integration is enabled and Akamai forwards the headers for the transaction. */ + riskAssessment?: { + /** Supplemental signals sent from third party providers to assist in risk assessments. */ + supplemental?: { + /** [Early Access] Supplemental risk assessment. This is available only if Akamai Account Protector is enabled and Akamai forwards the headers for the transaction. */ + akamai?: { + /** The bot detection results as forwarded by Akamai Bot Manager. */ + akamaiBot?: { + /** The type of the Akamai bot manager results. */ + type?: string; + /** The action of the Akamai bot manager results. */ + action?: string; + /** The bot category of the Akamai bot manager results. */ + botCategory?: string[]; + /** The bot score of the Akamai bot manager results. */ + botScore?: number; + /** The bot score response segment of the Akamai bot manager results. */ + botScoreResponseSegment?: string; + /** The botnet ID of the Akamai bot manager results. */ + botnetId?: string; + }; + /** The user risk detection results as forwarded by Akamai Account Protector. */ + akamaiUserRisk?: { + /** The action of the Akamai user risk assessment. */ + action?: string; + /** The allowed status of the Akamai user risk assessment. */ + allow?: number; + /** The email domain of the user. */ + emailDomain?: string; + /** The general risk of the Akamai user risk assessment. */ + general?: { + [additionalProperties: string]: any; + }; + /** The OUID of the user. */ + ouid?: string; + /** The request ID of the user. */ + requestid?: string; + /** The risk of the Akamai user risk assessment. */ + risk?: { + [additionalProperties: string]: any; + }; + /** The score of the Akamai user risk assessment. */ + score?: number; + /** The status of the Akamai user risk assessment. */ + status?: number; + /** The trust of the Akamai user risk assessment. */ + trust?: { + [additionalProperties: string]: any; + }; + /** The username of the user. */ + username?: string; + /** The UUID of the Akamai user risk assessment. */ + uuid?: string; + }; + }; + }; + }; + } & { + [additionalProperties: string]: any; + }; + /** An object containing information describing the authorization granted to the user who is logging in. */ + authorization: { + /** An array containing the names of a user's assigned roles. */ + roles: string[]; + }; + /** Information about the Client with which this password reset 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 Connection that was used to authenticate the user. */ + connection: { + /** The connection's unique identifier. */ + id: string; + /** Metadata associated with the connection. */ + metadata?: { + [additionalProperties: string]: string; + }; + /** The name of the connection used to authenticate the user (such as `twitter` or `some-g-suite-domain`). */ + name: string; + /** The type of connection. For social connections, `event.connection.strategy === event.connection.name`. For enterprise connections, the strategy is `waad` (Windows Azure AD), `ad` (Active Directory/LDAP), `auth0` (database connections), and so on. */ + strategy: string; + }; + /** Details about the custom domain associated with the current transaction. */ + custom_domain?: { + /** The custom domain name. */ + domain: string; + /** Custom domain metadata as key-value pairs. */ + domain_metadata: { + [additionalProperties: string]: 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; + }; + /** Collected data from rendered custom prompts. */ + prompt?: { + /** The prompt ID. */ + id: string; + /** Fields and hidden fields data. */ + fields?: { + [additionalProperties: string]: any; + }; + /** Shared variables data. */ + vars?: { + [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 and Client Credential Exchange flows and Post Login 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 query string parameters sent to the authorization request. */ + query: { + [additionalProperties: string]: any; + }; + /** The value of the `User-Agent` header received when initiating the transaction. */ + user_agent?: string; + }; + /** Login statistics for the current user. */ + stats: { + /** The number of times this user has logged in. */ + logins_count: number; + }; + /** Details about the Tenant associated with the current transaction. */ + tenant: { + /** The name of the tenant. */ + id: string; + }; + /** Details about the current transaction. */ + transaction: { + /** Correlation ID can be provided in the initial authentication request when the application redirects to Universal Login. You can use value to correlate logs and requests from your Action code with the user flow. */ + correlation_id?: string; + /** The locale to be used for this transaction as determined by comparing the browser's requested languages to the tenant's language settings. */ + locale: string; + /** Hint to the Authorization Server about the login identifier the End-User might use to log in (if necessary). */ + login_hint?: string; + /** An opaque arbitrary alphanumeric string your app adds to the initial request that Auth0 includes when redirecting back to your application. */ + state?: string; + /** The ui_locales provided in the original authentication request. */ + ui_locales: string[]; + } & { + [additionalProperties: string]: any; + }; + /** An object describing the user on whose behalf the current transaction was initiated. */ + 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. Empty array means the user has no enrolled factors. If enrolledFactors is undefined, the system was unable fetch the information, the user may or may not have enrolled factors. */ + 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; + })[]; + /** Contains info retrieved from the identity provider with which the user originally authenticated. 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; + }; +}; +/** PostChangePasswordV2Event */ +type PostChangePasswordV2Event = { + /** Details about supplemental authentication signals obtained during the password change flow. */ + authentication?: { + /** Details about risk assessments information for different flows. */ + riskAssessment?: { + /** Supplemental signals sent from third party providers to assist in risk assessments. */ + supplemental?: { + /** [Early Access] Supplemental risk assessment. This is available only if Akamai Account Protector is enabled and Akamai forwards the headers for the transaction. */ + akamai?: { + /** The bot detection results as forwarded by Akamai Bot Manager. */ + akamaiBot?: { + /** The type of the Akamai bot manager results. */ + type?: string; + /** The action of the Akamai bot manager results. */ + action?: string; + /** The bot category of the Akamai bot manager results. */ + botCategory?: string[]; + /** The bot score of the Akamai bot manager results. */ + botScore?: number; + /** The bot score response segment of the Akamai bot manager results. */ + botScoreResponseSegment?: string; + /** The botnet ID of the Akamai bot manager results. */ + botnetId?: string; + }; + /** The user risk detection results as forwarded by Akamai Account Protector. */ + akamaiUserRisk?: { + /** The action of the Akamai user risk assessment. */ + action?: string; + /** The allowed status of the Akamai user risk assessment. */ + allow?: number; + /** The email domain of the user. */ + emailDomain?: string; + /** The general risk of the Akamai user risk assessment. */ + general?: { + [additionalProperties: string]: any; + }; + /** The OUID of the user. */ + ouid?: string; + /** The request ID of the user. */ + requestid?: string; + /** The risk of the Akamai user risk assessment. */ + risk?: { + [additionalProperties: string]: any; + }; + /** The score of the Akamai user risk assessment. */ + score?: number; + /** The status of the Akamai user risk assessment. */ + status?: number; + /** The trust of the Akamai user risk assessment. */ + trust?: { + [additionalProperties: string]: any; + }; + /** The username of the user. */ + username?: string; + /** The UUID of the Akamai user risk assessment. */ + uuid?: string; + }; + }; + }; + }; + }; + /** Details about the Connection that was used for the current transaction. */ + connection: { + /** The connection's unique identifier. */ + id: string; + /** Metadata associated with the connection. */ + metadata?: { + [additionalProperties: string]: string; + }; + /** The name of the connection used to authenticate the user (such as `twitter` or `some-g-suite-domain`). */ + name: string; + /** The type of connection. For social connections, `event.connection.strategy === event.connection.name`. For enterprise connections, the strategy is `waad` (Windows Azure AD), `ad` (Active Directory/LDAP), `auth0` (database connections), and so on. */ + strategy: string; + }; + /** Details about the custom domain associated with the current transaction. */ + custom_domain?: { + /** The custom domain name. */ + domain: string; + /** Custom domain metadata as key-value pairs. */ + domain_metadata: { + [additionalProperties: string]: string; + }; + }; + /** Details about the request that initiated the transaction. */ + request: { + 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 Tenant associated with the current transaction. */ + tenant: { + /** The name of the tenant. */ + id: string; + }; + /** Details about the current transaction. */ + transaction?: { + /** Correlation ID can be provided in the initial authentication request when the application redirects to Universal Login. You can use value to correlate logs and requests from your Action code with the user flow. */ + correlation_id?: string; + }; + /** An object describing the user on whose behalf the current transaction was initiated. */ + user: { + /** (unique) User's email address. */ + email?: string; + /** Indicates whether the user has verified their email address. */ + email_verified?: boolean; + /** 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; + /** (unique) User's phone number. */ + phone_number?: string; + /** Indicates whether the user has verified their phone number. */ + phone_verified?: boolean; + /** (unique) User's unique identifier. */ + user_id?: string; + /** (unique) User's username. */ + username?: string; + }; +} & { + [additionalProperties: string]: any; +}; +/** PostLoginV3Event */ +type PostLoginV3Event = { + /** [Early Access] Information about the agent acting in this flow. Set when the authenticating client is linked to an agent and the tenant has agents as principals enabled; `undefined` otherwise. */ + agent?: { + /** [Early Access] The stable identifier for the agent, prefixed with `agt_` (for example, `agt_2hVk6JxPxbRgNZDKfJQqmn`). */ + agent_id: string; + /** [Early Access] Free-form key-value metadata associated with the agent. Always defined when `agent` is present; an empty object when the agent has no metadata. */ + agent_metadata: { + [additionalProperties: string]: any; + }; + /** [Early Access] The human-readable name of the agent. */ + name: string; + }; + /** Details about authentication signals obtained during the login flow. */ + authentication?: { + /** Contains the authentication methods a user has completed during their session. */ + methods: ( + | { + /** + * The name of the first factor that was completed. Values include the following: + * - `federated` A social or enterprise connection was used to authenticate the user as the first factor. + * - `pwd` A password was used to authenticate a database connection user as the first factor. + * - `passkey` A passkey was used to authenticate a database connection user as the first factor. + * - `sms` A Passwordless SMS connection was used to authenticate the user as the first factor. + * - `email` A Passwordless Email connection was used to authenticate the user as the first factor or verify email for password reset. + * - `phone_number` A phone number was used for password reset. + * - `mock` Used for internal testing. + * - May also be a URL denoting a custom authentication method (as second or later factor). + * @summary First Factor + */ + name: string; + timestamp: string; + } + | { + /** + * The user completed multi-factor authentication (second or later factors). + * @summary Multi-factor Authentication + */ + name: 'mfa'; + timestamp: string; + } + )[]; + /** Details about risk assessments obtained during the login or password reset flow. */ + riskAssessment?: { + assessments: { + /** Determines if the user is logging in as a known agent. */ + AgentDetection?: { + code: 'unknown' | 'verified_agent'; + confidence: 'low' | 'medium' | 'high' | 'neutral'; + details?: { + provider?: string; + }; + }; + /** Determines if the user is logging in from a location signaling impossible travel. */ + ImpossibleTravel?: { + code: + | 'minimal_travel_from_last_login' + | 'travel_from_last_login' + | 'substantial_travel_from_last_login' + | 'impossible_travel_from_last_login' + | 'invalid_travel' + | 'missing_geoip' + | 'anonymous_proxy' + | 'unknown_location' + | 'initial_login' + | 'location_history_not_found' + | 'assessment_not_available'; + confidence: 'low' | 'medium' | 'high' | 'neutral'; + }; + /** Determines if the user is logging in from a known device. */ + NewDevice?: { + code: + | 'match' + | 'partial_match' + | 'no_match' + | 'initial_login' + | 'unknown_device' + | 'no_device_history' + | 'assessment_not_available'; + confidence: 'low' | 'medium' | 'high' | 'neutral'; + details?: { + device?: 'known' | 'unknown'; + useragent?: 'known' | 'unknown'; + }; + }; + /** Shows if the IP was found in Auth0's repository of low reputation IPs. */ + UntrustedIP?: { + code: + | 'not_found_on_deny_list' + | 'found_on_deny_list' + | 'invalid_ip_address' + | 'assessment_not_available'; + confidence: 'low' | 'medium' | 'high' | 'neutral'; + details?: { + category?: string; + /** The originating IP address of the request. */ + ip?: string; + matches?: string; + source?: string; + }; + }; + }; + /** Overall risk score */ + confidence: 'low' | 'medium' | 'high' | 'neutral'; + /** [Early Access] Supplemental risk assessment. */ + supplemental?: { + akamai?: { + /** The bot detection results as forwarded by Akamai Bot Manager. */ + akamaiBot?: { + /** The type of the Akamai bot manager results. */ + type?: string; + /** The action of the Akamai bot manager results. */ + action?: string; + /** The bot category of the Akamai bot manager results. */ + botCategory?: string[]; + /** The bot score of the Akamai bot manager results. */ + botScore?: number; + /** The bot score response segment of the Akamai bot manager results. */ + botScoreResponseSegment?: string; + /** The botnet ID of the Akamai bot manager results. */ + botnetId?: string; + }; + /** The user risk detection results as forwarded by Akamai Account Protector. */ + akamaiUserRisk?: { + /** The action of the Akamai user risk assessment. */ + action?: string; + /** The allowed status of the Akamai user risk assessment. */ + allow?: number; + /** The email domain of the user. */ + emailDomain?: string; + /** The general risk of the Akamai user risk assessment. */ + general?: { + [additionalProperties: string]: any; + }; + /** The OUID of the user. */ + ouid?: string; + /** The request ID of the user. */ + requestid?: string; + /** The risk of the Akamai user risk assessment. */ + risk?: { + [additionalProperties: string]: any; + }; + /** The score of the Akamai user risk assessment. */ + score?: number; + /** The status of the Akamai user risk assessment. */ + status?: number; + /** The trust of the Akamai user risk assessment. */ + trust?: { + [additionalProperties: string]: any; + }; + /** The username of the user. */ + username?: string; + /** The UUID of the Akamai user risk assessment. */ + uuid?: string; + }; + } & { + [additionalProperties: string]: any; + }; + }; + version: string; + }; + }; + /** An object containing information describing the authorization granted to the user who is logging in. */ + authorization?: { + /** An array containing the names of a user's assigned roles. */ + roles: string[]; + }; + /** Information about the Client with which this login transaction was initiated. */ + client: { + /** The client id of the application to which the user is logging in. */ + 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; + /** [Early Access] An object for holding refresh token configuration properties. */ + refresh_token?: { + /** [Early Access] A collection of policies governing multi-resource refresh token exchange (MRRT), defining how refresh tokens can be used across different resource servers */ + policies?: { + /** [Early Access] The specific resource server (audience) to which this MRRT policy applies. */ + audience?: string; + /** The scopes of access that are authorized for the resource server (audience). */ + scope?: string[]; + }[]; + }; + }; + /** Details about the Connection that was used to authenticate the user. */ + connection: { + /** The connection's unique identifier. */ + id: string; + /** Metadata associated with the connection. */ + metadata?: { + [additionalProperties: string]: string; + }; + /** The name of the connection used to authenticate the user (such as `twitter` or `some-g-suite-domain`). */ + name: string; + /** The type of connection. For social connections, `event.connection.strategy === event.connection.name`. For enterprise connections, the strategy is `waad` (Windows Azure AD), `ad` (Active Directory/LDAP), `auth0` (database connections), and so on. */ + strategy: string; + }; + /** Details about the custom domain associated with the current transaction. */ + custom_domain?: { + /** The custom domain name. */ + domain: string; + /** Custom domain metadata as key-value pairs. */ + domain_metadata: { + [additionalProperties: string]: 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; + }; + /** Collected data from rendered custom prompts. */ + prompt?: { + /** The prompt ID. */ + id: string; + /** Fields and hidden fields data. */ + fields?: { + [additionalProperties: string]: any; + }; + /** Shared variables data. */ + vars?: { + [additionalProperties: string]: any; + }; + }; + /** [Enterprise Customers] The current refresh token. */ + refresh_token?: { + /** [Enterprise Customers] The ID of the refresh token. */ + id: string; + /** [Enterprise Customers] The ID of the client associated with the refresh token. */ + client_id?: string; + /** [Enterprise Customers] Timestamp of when the refresh token was created. */ + created_at: string; + device?: { + /** [Enterprise Customers] First autonomous system number associated with this refresh token. */ + initial_asn?: string; + /** [Enterprise Customers] First IP address associated with this refresh token. */ + initial_ip?: string; + /** [Enterprise Customers] First user agent of the device associated with this refresh token. */ + initial_user_agent?: string; + /** [Enterprise Customers] Last autonomous system number from which this refresh token was last exchanged. */ + last_asn?: string; + /** [Enterprise Customers] Last IP address from which this refresh token was last exchanged. */ + last_ip?: string; + /** [Enterprise Customers] Last user agent of the device from which this refresh token was last exchanged. */ + last_user_agent?: string; + }; + /** [Enterprise Customers] Timestamp of when the refresh token will absolutely expire. */ + expires_at?: string; + /** [Enterprise Customers] Timestamp of when the refresh token will idle expire. */ + idle_expires_at?: string; + /** [Enterprise Customers] Timestamp of when the refresh token was last successfully exchanged. */ + last_exchanged_at?: string; + /** Refresh Token Metadata */ + metadata?: { + [additionalProperties: string]: any; + }; + resource_servers?: { + /** [Enterprise Customers] The audience of the refresh token. */ + audience: string; + /** [Enterprise Customers] Scopes of the refresh token. */ + scopes: string; + }[]; + /** [Enterprise Customers] If the refresh token is a rotating refresh token. */ + rotating?: boolean; + /** [Enterprise Customers] The ID of the session bound to the refresh token. */ + session_id?: string; + /** [Enterprise Customers] This object is defined when the session is created from a session transfer token (Native to Web SSO), undefined otherwise. */ + session_transfer?: { + /** [Enterprise Customers] This object is defined when the refresh token is created from a session initiated as a result of session transfer (Native to Web SSO), undefined otherwise. */ + parent_refresh_token?: { + /** [Enterprise Customers] The ID of the parent refresh token from which this session/refresh token was created as a result of a session transfer (Native to Web SSO). */ + id?: string; + }; + }; + /** [Enterprise Customers] The ID of the user bound to the refresh token. */ + user_id?: string; + }; + /** Details about the request that initiated the transaction. */ + request: { + /** The ASN (autonomous system number) of the user-agent making the request. */ + asn?: string; + /** The body of the POST request. This data will only be available during refresh token and Client Credential Exchange flows and Post Login 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 query string parameters sent to the authorization request. */ + query: { + [additionalProperties: string]: any; + }; + /** 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; + }; + /** An object containing fingerprint signatures. This will be available only if the client is using cloudflare. The JA3/JA4 fingerprint can be null or empty in some cases. The most common case is for HTTP requests because JA3 and JA4 are calculated in TLS. It can also be empty due to the Worker sending requests within the same zone or to a zone that is not proxied (or a third party). */ + security_context?: { + /** JA3 fingerprint signature. This will be available only if the client is using a TLS connection. */ + ja3?: string; + /** JA4 fingerprint signature. This will be available only if the client is using a TLS connection. */ + ja4?: string; + }; + /** The current login session. */ + session?: { + /** The ID of the current session. */ + id: string; + /** The actor for sessions established using Session transfer tokens from Custom Token Exchange. Contains a required 'sub' property and up to 5 additional properties set via the Custom Token Exchange action. The actor will be defined for delegated sessions only. */ + actor?: { + /** The subject identifier of the actor. A unique identifier for the entity acting in this role. */ + sub: string; + } & { + [additionalProperties: string]: string | number | boolean | null; + }; + /** [Enterprise Customers] The date and time when the session was last authenticated. */ + authenticated_at?: string; + /** [Enterprise Customers] List of client details for the session. */ + clients?: { + /** [Enterprise Customers] ID of client for the session. */ + client_id: string; + }[]; + /** [Enterprise Customers] Cookie configuration for the session, which determines how the session cookie is handled by the User Agent. */ + cookie?: { + /** [Enterprise Customers] The persistence mode of the session cookie. When set to 'non-persistent' (ephemeral), the cookie will be deleted when the browser is closed. When set to 'persistent', the cookie will be stored until it expires or is deleted by the user. */ + mode: 'persistent' | 'non-persistent'; + }; + /** [Enterprise Customers] The date and time when the session was created. */ + created_at?: string; + /** [Enterprise Customers] Metadata related to the device used in the session. */ + device?: { + /** [Enterprise Customers] First autonomous system number associated with this session. */ + initial_asn?: string; + /** [Enterprise Customers] First IP address associated with this session. */ + initial_ip?: string; + /** [Enterprise Customers] First user agent of the device associated with this session. */ + initial_user_agent?: string; + /** [Enterprise Customers] Last autonomous system number from which this user logged in. */ + last_asn?: string; + /** [Enterprise Customers] Last IP address from which this user logged in. */ + last_ip?: string; + /** [Enterprise Customers] Last user agent of the device from which this user logged in. */ + last_user_agent?: string; + }; + /** [Enterprise Customers] The date and time when the session will expire. */ + expires_at?: string; + /** [Enterprise Customers] The date and time when the session will expire if idle. */ + idle_expires_at?: string; + /** [Enterprise Customers] The date and time when the session was last successfully interacted with. */ + last_interacted_at?: string; + /** [Enterprise Customers] [Early Access] Session Metadata */ + metadata?: { + [additionalProperties: string]: any; + }; + /** [Enterprise Customers] [Early Access] This object is defined when the session is created from a session transfer token (Native to Web SSO), undefined otherwise. */ + session_transfer?: { + /** [Enterprise Customers] This object is defined when the refresh token is created from a session initiated as a result of session transfer (Native to Web SSO), undefined otherwise. */ + parent_refresh_token?: { + /** [Enterprise Customers] The ID of the parent refresh token from which this session/refresh token was created as a result of a session transfer (Native to Web SSO). */ + id?: string; + /** [Enterprise Customers] The metadata of the parent refresh token from which this session/refresh token was created as a result of a session transfer (Native to Web SSO). */ + metadata?: { + [additionalProperties: string]: any; + }; + }; + }; + /** [Enterprise Customers] The date and time when the session was last updated. */ + updated_at?: string; + /** [Enterprise Customers] ID of the user which can be used when interacting with other APIs. */ + user_id?: string; + }; + /** [Early Access] Details of the current session transfer token being used to establish Single Sign-On (SSO) from a native application to a web application. */ + session_transfer_token?: { + /** [Early Access] The client identifier of the application that issued the token. */ + client_id: string; + /** [Early Access] Details about the request that issued the token. */ + request: { + /** [Early Access] The Autonomous System Number (ASN) associated with the request that issued the token. */ + asn?: string; + geoip?: { + cityName?: string; + continentCode?: string; + countryCode?: string; + countryCode3?: string; + countryName?: string; + latitude?: number; + longitude?: number; + subdivisionCode?: string; + subdivisionName?: string; + timeZone?: string; + } & { + [additionalProperties: string]: any; + }; + /** [Early Access] The IP address associated with the request that issued the token. */ + ip: string; + /** [Early Access] The User-Agent string of the device that issued the token. */ + user_agent?: string; + }; + /** [Early Access] The scopes requested when the token was issued. */ + scope: string[]; + }; + /** Login statistics for the current user. */ + stats: { + /** The number of times this user has logged in. */ + logins_count: number; + }; + /** Details about the Tenant associated with the current transaction. */ + tenant: { + /** The name of the tenant. */ + id: string; + }; + /** Details about the current transaction. */ + transaction?: { + /** Unique identifier for the transaction. Populated for all browser-based login flows. */ + id?: string; + /** Any acr_values provided in the original authentication request. */ + acr_values?: string[]; + /** Dynamic Linking ID that allows developers to reference this transaction. */ + linking_id?: string; + /** The locale to be used for this transaction as determined by comparing the browser's requested languages to the tenant's language settings. */ + locale?: string; + /** Hint to the Authorization Server about the login identifier the End-User might use to log in (if necessary). */ + login_hint?: string; + /** List of instructions indicating whether the user may be prompted for re-authentication and consent. */ + prompt?: string[]; + protocol?: ( + | 'oidc-basic-profile' + | 'oidc-ciba' + | 'oidc-ciba-web-link' + | 'oidc-implicit-profile' + | 'oauth2-device-code' + | 'oauth2-resource-owner' + | 'oauth2-resource-owner-jwt-bearer' + | 'oauth2-password' + | 'oauth2-webauthn' + | 'oauth2-access-token' + | 'oauth2-refresh-token' + | 'oauth2-token-exchange' + | 'oidc-hybrid-profile' + | 'samlp' + | 'wsfed' + | 'wstrust-usernamemixed' + ) & + string; + /** The URL to which Auth0 will redirect the browser after the transaction is completed. */ + redirect_uri?: string; + /** The details of a rich authorization request per Section 2 of the Rich Authorization Requests spec at https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar#section-2. */ + requested_authorization_details?: ({ + /** The type of authorization details as a string. The value of the type field determines the allowable contents of the object which contains it. */ + type: string; + } & { + [additionalProperties: string]: any; + })[]; + /** The scopes requested (if any) when starting this authentication flow. */ + requested_scopes?: string[]; + /** Informs the Authorization Server of the mechanism to be used for returning parameters from the Authorization Endpoint. */ + response_mode?: 'query' | 'fragment' | 'form_post' | 'web_message'; + /** Denotes the kind of credential that Auth0 will return. */ + response_type?: ('code' | 'token' | 'id_token')[]; + /** An opaque arbitrary alphanumeric string your app adds to the initial request that Auth0 includes when redirecting back to your application. */ + state?: string; + /** The ui_locales provided in the original authentication request. */ + ui_locales?: string[]; + /** The actor in a token exchange request. */ + actor?: { + /** The next actor in the delegation chain, representing that this actor is making the request on behalf of another principal. */ + act?: { + /** The next actor in the delegation chain, representing that this actor is making the request on behalf of another principal. */ + act?: { + /** The next actor in the delegation chain, representing that this actor is making the request on behalf of another principal. */ + act?: { + /** The next actor in the delegation chain, representing that this actor is making the request on behalf of another principal. */ + act?: { + act?: never; + /** The subject identifier of the actor. A unique identifier for the entity acting in this role. */ + sub: string; + } & { + [additionalProperties: string]: any; + }; + /** The subject identifier of the actor. A unique identifier for the entity acting in this role. */ + sub: string; + } & { + [additionalProperties: string]: any; + }; + /** The subject identifier of the actor. A unique identifier for the entity acting in this role. */ + sub: string; + } & { + [additionalProperties: string]: any; + }; + /** The subject identifier of the actor. A unique identifier for the entity acting in this role. */ + sub: string; + } & { + [additionalProperties: string]: any; + }; + /** The subject identifier of the actor. A unique identifier for the entity acting in this role. */ + sub: string; + } & { + [additionalProperties: string]: any; + }; + /** The type of the actor token in a token exchange request. */ + actor_token_type?: string; + /** Correlation ID can be provided in the initial authentication request when the application redirects to Universal Login. You can use value to correlate logs and requests from your Action code with the user flow. */ + correlation_id?: string; + /** An object containing shared data across custom Actions for the duration of a transaction. */ + metadata: { + [additionalProperties: string]: string | number | boolean; + }; + /** The type of the subject token in a token exchange request. */ + subject_token_type?: string; + } & { + [additionalProperties: string]: any; + }; + /** An object describing the user on whose behalf the current transaction was initiated. */ + 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 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; + }; +}; +/** PostUserRegistrationV2Event */ +type PostUserRegistrationV2Event = { + /** Details about supplemental authentication signals obtained during the registration flow. */ + authentication?: { + /** Details about risk assessments information for different flows. */ + riskAssessment?: { + /** Supplemental signals sent from third party providers to assist in risk assessments. */ + supplemental?: { + /** [Early Access] Supplemental risk assessment. This is available only if Akamai Account Protector is enabled and Akamai forwards the headers for the transaction. */ + akamai?: { + /** The bot detection results as forwarded by Akamai Bot Manager. */ + akamaiBot?: { + /** The type of the Akamai bot manager results. */ + type?: string; + /** The action of the Akamai bot manager results. */ + action?: string; + /** The bot category of the Akamai bot manager results. */ + botCategory?: string[]; + /** The bot score of the Akamai bot manager results. */ + botScore?: number; + /** The bot score response segment of the Akamai bot manager results. */ + botScoreResponseSegment?: string; + /** The botnet ID of the Akamai bot manager results. */ + botnetId?: string; + }; + /** The user risk detection results as forwarded by Akamai Account Protector. */ + akamaiUserRisk?: { + /** The action of the Akamai user risk assessment. */ + action?: string; + /** The allowed status of the Akamai user risk assessment. */ + allow?: number; + /** The email domain of the user. */ + emailDomain?: string; + /** The general risk of the Akamai user risk assessment. */ + general?: { + [additionalProperties: string]: any; + }; + /** The OUID of the user. */ + ouid?: string; + /** The request ID of the user. */ + requestid?: string; + /** The risk of the Akamai user risk assessment. */ + risk?: { + [additionalProperties: string]: any; + }; + /** The score of the Akamai user risk assessment. */ + score?: number; + /** The status of the Akamai user risk assessment. */ + status?: number; + /** The trust of the Akamai user risk assessment. */ + trust?: { + [additionalProperties: string]: any; + }; + /** The username of the user. */ + username?: string; + /** The UUID of the Akamai user risk assessment. */ + uuid?: string; + }; + }; + }; + }; + }; + /** Details about the Connection that was used to register the user. */ + connection: { + /** The connection's unique identifier. */ + id: string; + /** Metadata associated with the connection. */ + metadata?: { + [additionalProperties: string]: string; + }; + /** The name of the connection used to authenticate the user (such as `twitter` or `some-g-suite-domain`). */ + name: string; + /** The type of connection. For social connections, `event.connection.strategy === event.connection.name`. For enterprise connections, the strategy is `waad` (Windows Azure AD), `ad` (Active Directory/LDAP), `auth0` (database connections), and so on. */ + strategy: string; + }; + /** Details about the custom domain associated with the current transaction. */ + custom_domain?: { + /** The custom domain name. */ + domain: string; + /** Custom domain metadata as key-value pairs. */ + domain_metadata: { + [additionalProperties: string]: string; + }; + }; + /** Details about the request that initiated the transaction. */ + request?: { + 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; + }; + /** An object containing fingerprint signatures. This will be available only if the client is using cloudflare. The JA3/JA4 fingerprint can be null or empty in some cases. The most common case is for HTTP requests because JA3 and JA4 are calculated in TLS. It can also be empty due to the Worker sending requests within the same zone or to a zone that is not proxied (or a third party). */ + security_context?: { + /** JA3 fingerprint signature. This will be available only if the client is using a TLS connection. */ + ja3?: string; + /** JA4 fingerprint signature. This will be available only if the client is using a TLS connection. */ + ja4?: string; + }; + /** Details about the Tenant associated with the current transaction. */ + tenant: { + /** The name of the tenant. */ + id: string; + }; + /** Details about the current transaction. */ + transaction?: { + /** Any acr_values provided in the original authentication request. */ + acr_values: string[]; + /** The locale to be used for this transaction as determined by comparing the browser's requested languages to the tenant's language settings. */ + locale: string; + /** Hint to the Authorization Server about the login identifier the End-User might use to log in (if necessary). */ + login_hint?: string; + /** List of instructions indicating whether the user may be prompted for re-authentication and consent. */ + prompt?: string[]; + protocol?: ( + | 'oidc-basic-profile' + | 'oidc-ciba' + | 'oidc-ciba-web-link' + | 'oidc-implicit-profile' + | 'oauth2-device-code' + | 'oauth2-resource-owner' + | 'oauth2-resource-owner-jwt-bearer' + | 'oauth2-password' + | 'oauth2-webauthn' + | 'oauth2-access-token' + | 'oauth2-refresh-token' + | 'oauth2-token-exchange' + | 'oidc-hybrid-profile' + | 'samlp' + | 'wsfed' + | 'wstrust-usernamemixed' + ) & + string; + /** The URL to which Auth0 will redirect the browser after the transaction is completed. */ + redirect_uri?: string; + /** The scopes requested (if any) when starting this authentication flow. */ + requested_scopes: string[]; + /** Informs the Authorization Server of the mechanism to be used for returning parameters from the Authorization Endpoint. */ + response_mode?: 'query' | 'fragment' | 'form_post' | 'web_message'; + /** Denotes the kind of credential that Auth0 will return. */ + response_type?: ('code' | 'token' | 'id_token')[]; + /** An opaque arbitrary alphanumeric string your app adds to the initial request that Auth0 includes when redirecting back to your application. */ + state?: string; + /** The ui_locales provided in the original authentication request. */ + ui_locales: string[]; + } & { + [additionalProperties: string]: any; + }; + /** An object describing the user on whose behalf the current transaction was initiated. */ + 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; + } & { + [additionalProperties: string]: any; + }; +} & { + [additionalProperties: string]: any; +}; +/** PreUserRegistrationV2Event */ +type PreUserRegistrationV2Event = { + /** Details about authentication obtained during the pre user registration flow. */ + authentication?: { + /** Details about risk assessments information for different flows. */ + riskAssessment?: { + /** Supplemental signals sent from third party providers to assist in risk assessments. */ + supplemental?: { + /** [Early Access] Supplemental risk assessment. This is available only if Akamai Account Protector is enabled and Akamai forwards the headers for the transaction. */ + akamai?: { + /** The bot detection results as forwarded by Akamai Bot Manager. */ + akamaiBot?: { + /** The type of the Akamai bot manager results. */ + type?: string; + /** The action of the Akamai bot manager results. */ + action?: string; + /** The bot category of the Akamai bot manager results. */ + botCategory?: string[]; + /** The bot score of the Akamai bot manager results. */ + botScore?: number; + /** The bot score response segment of the Akamai bot manager results. */ + botScoreResponseSegment?: string; + /** The botnet ID of the Akamai bot manager results. */ + botnetId?: string; + }; + /** The user risk detection results as forwarded by Akamai Account Protector. */ + akamaiUserRisk?: { + /** The action of the Akamai user risk assessment. */ + action?: string; + /** The allowed status of the Akamai user risk assessment. */ + allow?: number; + /** The email domain of the user. */ + emailDomain?: string; + /** The general risk of the Akamai user risk assessment. */ + general?: { + [additionalProperties: string]: any; + }; + /** The OUID of the user. */ + ouid?: string; + /** The request ID of the user. */ + requestid?: string; + /** The risk of the Akamai user risk assessment. */ + risk?: { + [additionalProperties: string]: any; + }; + /** The score of the Akamai user risk assessment. */ + score?: number; + /** The status of the Akamai user risk assessment. */ + status?: number; + /** The trust of the Akamai user risk assessment. */ + trust?: { + [additionalProperties: string]: any; + }; + /** The username of the user. */ + username?: string; + /** The UUID of the Akamai user risk assessment. */ + uuid?: string; + }; + }; + }; + }; + }; + /** 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 Connection that was used to register the user. */ + connection: { + /** The connection's unique identifier. */ + id: string; + /** Metadata associated with the connection. */ + metadata?: { + [additionalProperties: string]: string; + }; + /** The name of the connection used to authenticate the user (such as `twitter` or `some-g-suite-domain`). */ + name: string; + /** The type of connection. For social connections, `event.connection.strategy === event.connection.name`. For enterprise connections, the strategy is `waad` (Windows Azure AD), `ad` (Active Directory/LDAP), `auth0` (database connections), and so on. */ + strategy: string; + }; + /** Details about the custom domain associated with the current transaction. */ + custom_domain?: { + /** The custom domain name. */ + domain: string; + /** Custom domain metadata as key-value pairs. */ + domain_metadata: { + [additionalProperties: string]: string; + }; + }; + /** 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; + }; + /** An object containing fingerprint signatures. This will be available only if the client is using cloudflare. The JA3/JA4 fingerprint can be null or empty in some cases. The most common case is for HTTP requests because JA3 and JA4 are calculated in TLS. It can also be empty due to the Worker sending requests within the same zone or to a zone that is not proxied (or a third party). */ + security_context?: { + /** JA3 fingerprint signature. This will be available only if the client is using a TLS connection. */ + ja3?: string; + /** JA4 fingerprint signature. This will be available only if the client is using a TLS connection. */ + ja4?: string; + }; + /** Details about the Tenant associated with the current transaction. */ + tenant: { + /** The name of the tenant. */ + id: string; + }; + /** Details about the current transaction. */ + transaction?: { + /** Any acr_values provided in the original authentication request. */ + acr_values: string[]; + /** The locale to be used for this transaction as determined by comparing the browser's requested languages to the tenant's language settings. */ + locale: string; + /** Hint to the Authorization Server about the login identifier the End-User might use to log in (if necessary). */ + login_hint?: string; + /** List of instructions indicating whether the user may be prompted for re-authentication and consent. */ + prompt?: string[]; + protocol?: ( + | 'oidc-basic-profile' + | 'oidc-ciba' + | 'oidc-ciba-web-link' + | 'oidc-implicit-profile' + | 'oauth2-device-code' + | 'oauth2-resource-owner' + | 'oauth2-resource-owner-jwt-bearer' + | 'oauth2-password' + | 'oauth2-webauthn' + | 'oauth2-access-token' + | 'oauth2-refresh-token' + | 'oauth2-token-exchange' + | 'oidc-hybrid-profile' + | 'samlp' + | 'wsfed' + | 'wstrust-usernamemixed' + ) & + string; + /** The URL to which Auth0 will redirect the browser after the transaction is completed. */ + redirect_uri?: string; + /** The scopes requested (if any) when starting this authentication flow. */ + requested_scopes: string[]; + /** Informs the Authorization Server of the mechanism to be used for returning parameters from the Authorization Endpoint. */ + response_mode?: 'query' | 'fragment' | 'form_post' | 'web_message'; + /** Denotes the kind of credential that Auth0 will return. */ + response_type?: ('code' | 'token' | 'id_token')[]; + /** An opaque arbitrary alphanumeric string your app adds to the initial request that Auth0 includes when redirecting back to your application. */ + state?: string; + /** The ui_locales provided in the original authentication request. */ + ui_locales: string[]; + /** Correlation ID can be provided in the initial authentication request when the application redirects to Universal Login. You can use value to correlate logs and requests from your Action code with the user flow. */ + correlation_id?: string; + } & { + [additionalProperties: string]: any; + }; + /** An object describing the user who is attempting to register. */ + 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; + }; + /** (unique) User's email address. */ + email?: string; + /** User's family name. */ + family_name?: string; + /** User's given name. */ + given_name?: string; + /** User's full name. */ + name?: string; + /** User's nickname. */ + nickname?: string; + /** User's phone number. */ + phone_number?: string; + /** URL pointing to the [user's profile picture](https://auth0.com/docs/users/change-user-picture). */ + picture?: 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; + }; +} & { + [additionalProperties: string]: any; +}; +/** SendPhoneMessageV2Event */ +type SendPhoneMessageV2Event = { + /** 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 custom domain associated with the current transaction. */ + custom_domain?: { + /** The custom domain name. */ + domain: string; + /** Custom domain metadata as key-value pairs. */ + domain_metadata: { + [additionalProperties: string]: string; + }; + }; + /** Details about the message that is sent to the user. */ + message_options: { + /** The flow that triggered this action. */ + action: ('enrollment' | 'second-factor-authentication') & string; + /** One-time password that the user needs to use to enter in the form. */ + code: string; + /** How the message will be delivered, either by 'sms' or 'voice'. */ + message_type: ('sms' | 'voice') & string; + /** Phone number where the message will be sent. */ + recipient: string; + /** Content of the message to be sent. */ + text: string; + }; + /** Details about the request that initiated the transaction. */ + request: { + 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; + }; + /** An object containing fingerprint signatures. This will be available only if the client is using cloudflare. The JA3/JA4 fingerprint can be null or empty in some cases. The most common case is for HTTP requests because JA3 and JA4 are calculated in TLS. It can also be empty due to the Worker sending requests within the same zone or to a zone that is not proxied (or a third party). */ + security_context?: { + /** JA3 fingerprint signature. This will be available only if the client is using a TLS connection. */ + ja3?: string; + /** JA4 fingerprint signature. This will be available only if the client is using a TLS connection. */ + ja4?: string; + }; + /** Details about the Tenant associated with the current transaction. */ + tenant: { + /** The name of the tenant. */ + id: string; + }; + /** Details about the current transaction. */ + transaction?: { + /** Correlation ID can be provided in the initial authentication request when the application redirects to Universal Login. You can use value to correlate logs and requests from your Action code with the user flow. */ + correlation_id?: string; + }; + /** An object describing the user on whose behalf the current transaction was initiated. */ + 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; + /** 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; + }; +}; +/** The custom prompt ID. */ +type PromptId = string; +type PromptOptions = { + /** Key-value pairs to populate field values (client-side). */ + fields?: { + [patternProperties: string]: any; + }; + /** Key-value pairs to inject variables (server-side). */ + vars?: { + [patternProperties: string]: any; + }; +}; +/** 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; +}; +export type { + CustomTokenExchangeV1Event as C, + EventStreamV1Event as E, + PasswordResetPostChallengeV1Event as P, + SendPhoneMessageV2Event as S, + CustomTokenExchangeSetUserByConnectionUserAttributes as a, + CustomTokenExchangeSetUserByConnectionOptions as b, + PostChangePasswordV2Event as c, + PostLoginV3Event as d, + PromptId as e, + PromptOptions as f, + PostUserRegistrationV2Event as g, + PreUserRegistrationV2Event as h, +}; diff --git a/src/_shared/C_R4QU65.d.ts b/src/_shared/C_R4QU65.d.ts new file mode 100644 index 0000000..00704ae --- /dev/null +++ b/src/_shared/C_R4QU65.d.ts @@ -0,0 +1,5 @@ +import { e as PromptId, f as PromptOptions } from './CUlF8oaW.js'; +type AccessDeniedErrorCode = 'invalid_scope' | 'invalid_request' | 'server_error'; +type RenderPromptId = PromptId; +type RenderPromptOptions = PromptOptions; +export type { AccessDeniedErrorCode as A, RenderPromptId as R, RenderPromptOptions as a }; diff --git a/src/_shared/DvTaCl9e.js b/src/_shared/DvTaCl9e.js new file mode 100644 index 0000000..757c8d4 --- /dev/null +++ b/src/_shared/DvTaCl9e.js @@ -0,0 +1,4530 @@ +'use strict'; + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __esm = (fn, res) => function __init() { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// ../../node_modules/@ggoodman/typed-validator/typed-validator.js +var typed_validator_exports = {}; +__export(typed_validator_exports, { + ValidationError: () => ValidationError, + createCodec: () => createCodec +}); +function valueToShapeString(value) { + return JSON.stringify(value, valueToShapeReplacer); +} +function valueToShapeReplacer(_key, value) { + return typeof value === "object" && value ? value : typeof value; +} +function createCodec(name, uri, validateFn) { + return new CodecImpl(name, uri, validateFn); +} +var ValidationError, CodecImpl; +var init_typed_validator = __esm({ + "../../node_modules/@ggoodman/typed-validator/typed-validator.js"() { + ValidationError = class extends Error { + static isValidationError(err) { + return err instanceof this; + } + constructor(schemaName, value, validatorErrors) { + const errorStrings = validatorErrors.map((err) => { + return ` ${err.message} at ${err.instancePath || "#"}, got ${valueToShapeString(err.data)}`; + }); + super(`Validation for the schema ${JSON.stringify(schemaName)} failed with the following errors: +${errorStrings.join("\n")}`); + this.value = value; + this.validatorErrors = validatorErrors; + } + }; + CodecImpl = class CodecImpl2 { + /** + * Identify function returning the given argument as a value matching the schema. + * + * This can be useful to use in non-TypeScript code to construct a valid object while + * benefitting from suggestions from a TypeScript language service. + */ + identity(obj) { + return obj; + } + /** + * Check if a value matches the schema. + */ + is(obj) { + return this.validateFn(obj); + } + /** + * Validate that a value matches the schema and throws if not. + */ + validate(obj) { + if (!this.validateFn(obj)) { + throw new ValidationError(this.name, obj, this.validateFn.errors || []); + } + return obj; + } + constructor(name, uri, validateFn) { + this.name = name; + this.uri = uri; + this.validateFn = validateFn; + } + }; + } +}); + +// src/index.ts +init_typed_validator(); + +// src/generated/schemas.js +var { createCodec: createCodec2 } = (init_typed_validator(), __toCommonJS(typed_validator_exports)); +var __getOwnPropNames2 = Object.getOwnPropertyNames; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var require_ucs2length = __commonJS({ + "../../node_modules/ajv/dist/runtime/ucs2length.js"(exports) { + Object.defineProperty(exports, "__esModule", { value: true }); + function ucs2length(str) { + const len = str.length; + let length = 0; + let pos = 0; + let value; + while (pos < len) { + length++; + value = str.charCodeAt(pos++); + if (value >= 55296 && value <= 56319 && pos < len) { + value = str.charCodeAt(pos); + if ((value & 64512) === 56320) + pos++; + } + } + return length; + } + exports.default = ucs2length; + ucs2length.code = 'require("ajv/dist/runtime/ucs2length").default'; + } +}); +var func2 = Object.prototype.hasOwnProperty; +var func4 = require_ucs2length().default; +var pattern0 = new RegExp("^[^\\s]{1,280}$", "u"); +function validate60(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.type === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "type" }, + message: "must have required property 'type'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (data.target === void 0) { + const err1 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "target" }, + message: "must have required property 'target'" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + if (data.modifications === void 0) { + const err2 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "modifications" }, + message: "must have required property 'modifications'" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "type" || key0 === "modifications" || key0 === "target")) { + const err3 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + } + if (data.type !== void 0) { + if ("ModifyScope" !== data.type) { + const err4 = { + instancePath: instancePath + "/type", + schemaPath: "#/properties/type/const", + keyword: "const", + params: { allowedValue: "ModifyScope" }, + message: "must be equal to constant" + }; + if (vErrors === null) { + vErrors = [err4]; + } else { + vErrors.push(err4); + } + errors++; + } + } + if (data.modifications !== void 0) { + let data1 = data.modifications; + if (data1 && typeof data1 == "object" && !Array.isArray(data1)) { + if (Object.keys(data1).length > 2e3) { + const err5 = { + instancePath: instancePath + "/modifications", + schemaPath: "#/properties/modifications/maxProperties", + keyword: "maxProperties", + params: { limit: 2e3 }, + message: "must NOT have more than 2000 properties" + }; + if (vErrors === null) { + vErrors = [err5]; + } else { + vErrors.push(err5); + } + errors++; + } + if (Object.keys(data1).length < 1) { + const err6 = { + instancePath: instancePath + "/modifications", + schemaPath: "#/properties/modifications/minProperties", + keyword: "minProperties", + params: { limit: 1 }, + message: "must NOT have fewer than 1 properties" + }; + if (vErrors === null) { + vErrors = [err6]; + } else { + vErrors.push(err6); + } + errors++; + } + for (const key1 in data1) { + if (!pattern0.test(key1)) { + const err7 = { + instancePath: instancePath + "/modifications", + schemaPath: "#/properties/modifications/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key1 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err7]; + } else { + vErrors.push(err7); + } + errors++; + } + } + for (const key2 in data1) { + if (pattern0.test(key2)) { + let data2 = data1[key2]; + if (typeof data2 !== "string") { + const err8 = { + instancePath: instancePath + "/modifications/" + key2.replace(/~/g, "~0").replace(/\//g, "~1"), + schemaPath: "#/properties/modifications/patternProperties/%5E%5B%5E%5Cs%5D%7B1%2C280%7D%24/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err8]; + } else { + vErrors.push(err8); + } + errors++; + } + const _errs8 = errors; + let valid2 = false; + let passing0 = null; + const _errs9 = errors; + if ("add" !== data2) { + const err9 = { + instancePath: instancePath + "/modifications/" + key2.replace(/~/g, "~0").replace(/\//g, "~1"), + schemaPath: "#/properties/modifications/patternProperties/%5E%5B%5E%5Cs%5D%7B1%2C280%7D%24/oneOf/0/const", + keyword: "const", + params: { allowedValue: "add" }, + message: "must be equal to constant" + }; + if (vErrors === null) { + vErrors = [err9]; + } else { + vErrors.push(err9); + } + errors++; + } + var _valid0 = _errs9 === errors; + if (_valid0) { + valid2 = true; + passing0 = 0; + } + const _errs10 = errors; + if ("remove" !== data2) { + const err10 = { + instancePath: instancePath + "/modifications/" + key2.replace(/~/g, "~0").replace(/\//g, "~1"), + schemaPath: "#/properties/modifications/patternProperties/%5E%5B%5E%5Cs%5D%7B1%2C280%7D%24/oneOf/1/const", + keyword: "const", + params: { allowedValue: "remove" }, + message: "must be equal to constant" + }; + if (vErrors === null) { + vErrors = [err10]; + } else { + vErrors.push(err10); + } + errors++; + } + var _valid0 = _errs10 === errors; + if (_valid0 && valid2) { + valid2 = false; + passing0 = [passing0, 1]; + } else { + if (_valid0) { + valid2 = true; + passing0 = 1; + } + } + if (!valid2) { + const err11 = { + instancePath: instancePath + "/modifications/" + key2.replace(/~/g, "~0").replace(/\//g, "~1"), + schemaPath: "#/properties/modifications/patternProperties/%5E%5B%5E%5Cs%5D%7B1%2C280%7D%24/oneOf", + keyword: "oneOf", + params: { passingSchemas: passing0 }, + message: "must match exactly one schema in oneOf" + }; + if (vErrors === null) { + vErrors = [err11]; + } else { + vErrors.push(err11); + } + errors++; + } else { + errors = _errs8; + if (vErrors !== null) { + if (_errs8) { + vErrors.length = _errs8; + } else { + vErrors = null; + } + } + } + } + } + } else { + const err12 = { + instancePath: instancePath + "/modifications", + schemaPath: "#/properties/modifications/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err12]; + } else { + vErrors.push(err12); + } + errors++; + } + } + if (data.target !== void 0) { + if ("accessToken" !== data.target) { + const err13 = { + instancePath: instancePath + "/target", + schemaPath: "#/properties/target/const", + keyword: "const", + params: { allowedValue: "accessToken" }, + message: "must be equal to constant" + }; + if (vErrors === null) { + vErrors = [err13]; + } else { + vErrors.push(err13); + } + errors++; + } + } + } else { + const err14 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err14]; + } else { + vErrors.push(err14); + } + errors++; + } + validate60.errors = vErrors; + return errors === 0; +} +var pattern5 = new RegExp("^[a-zA-Z_][a-zA-Z0-9_]{0,49}$", "u"); +var pattern6 = new RegExp(".*", "u"); +var schema53 = { + type: ["string", "number", "boolean", "null", "array"], + maxLength: 2048, + items: { type: ["string", "number", "boolean"], maxLength: 2048 } +}; +var schema57 = { + $id: "https://auth0.com/triggers/PostLogin/generic/commands/PostLoginSetSAMLConfigurationInput.json", + $schema: "http://json-schema.org/draft-07/schema", + title: "PostLoginSetSAMLConfigurationInput", + description: "Input validation for the SetSAMLConfiguration command.", + type: "object", + additionalProperties: false, + properties: { + RelayState: { type: "string", maxLength: 512 }, + audience: { type: "string", maxLength: 2048 }, + authnContextClassRef: { type: "string", maxLength: 512 }, + cert: { type: "string", maxLength: 4096 }, + createUpnClaim: { type: "boolean" }, + destination: { type: "string", maxLength: 2048 }, + digestAlgorithm: { enum: ["sha256", "sha1"] }, + encryptionAlgorithm: { enum: ["aes256-gcm", "aes256-cbc"] }, + encryptionCert: { type: "string", maxLength: 4096 }, + encryptionPublicKey: { type: "string", maxLength: 4096 }, + includeAttributeNameFormat: { type: "boolean" }, + issuer: { type: "string", maxLength: 512 }, + key: { type: "string", maxLength: 4096 }, + lifetimeInSeconds: { type: "number" }, + mapIdentities: { type: "boolean" }, + mapUnknownClaimsAsIs: { type: "boolean" }, + nameIdentifierFormat: { type: "string", maxLength: 512 }, + nameIdentifierProbes: { + type: "array", + maxItems: 10, + items: { type: "string", maxLength: 512 } + }, + passthroughClaimsWithNoMapping: { type: "boolean" }, + recipient: { type: "string", maxLength: 2048 }, + signResponse: { type: "boolean" }, + signatureAlgorithm: { enum: ["rsa-sha256", "rsa-sha1"] }, + signingCert: { type: "string", maxLength: 4096 }, + typedAttributes: { type: "boolean" } + } +}; +function validate117(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + for (const key0 in data) { + if (!func2.call(schema57.properties, key0)) { + const err0 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } + if (data.RelayState !== void 0) { + let data0 = data.RelayState; + if (typeof data0 === "string") { + if (func4(data0) > 512) { + const err1 = { + instancePath: instancePath + "/RelayState", + schemaPath: "#/properties/RelayState/maxLength", + keyword: "maxLength", + params: { limit: 512 }, + message: "must NOT have more than 512 characters" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } else { + const err2 = { + instancePath: instancePath + "/RelayState", + schemaPath: "#/properties/RelayState/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + } + if (data.audience !== void 0) { + let data1 = data.audience; + if (typeof data1 === "string") { + if (func4(data1) > 2048) { + const err3 = { + instancePath: instancePath + "/audience", + schemaPath: "#/properties/audience/maxLength", + keyword: "maxLength", + params: { limit: 2048 }, + message: "must NOT have more than 2048 characters" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + } else { + const err4 = { + instancePath: instancePath + "/audience", + schemaPath: "#/properties/audience/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err4]; + } else { + vErrors.push(err4); + } + errors++; + } + } + if (data.authnContextClassRef !== void 0) { + let data2 = data.authnContextClassRef; + if (typeof data2 === "string") { + if (func4(data2) > 512) { + const err5 = { + instancePath: instancePath + "/authnContextClassRef", + schemaPath: "#/properties/authnContextClassRef/maxLength", + keyword: "maxLength", + params: { limit: 512 }, + message: "must NOT have more than 512 characters" + }; + if (vErrors === null) { + vErrors = [err5]; + } else { + vErrors.push(err5); + } + errors++; + } + } else { + const err6 = { + instancePath: instancePath + "/authnContextClassRef", + schemaPath: "#/properties/authnContextClassRef/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err6]; + } else { + vErrors.push(err6); + } + errors++; + } + } + if (data.cert !== void 0) { + let data3 = data.cert; + if (typeof data3 === "string") { + if (func4(data3) > 4096) { + const err7 = { + instancePath: instancePath + "/cert", + schemaPath: "#/properties/cert/maxLength", + keyword: "maxLength", + params: { limit: 4096 }, + message: "must NOT have more than 4096 characters" + }; + if (vErrors === null) { + vErrors = [err7]; + } else { + vErrors.push(err7); + } + errors++; + } + } else { + const err8 = { + instancePath: instancePath + "/cert", + schemaPath: "#/properties/cert/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err8]; + } else { + vErrors.push(err8); + } + errors++; + } + } + if (data.createUpnClaim !== void 0) { + if (typeof data.createUpnClaim !== "boolean") { + const err9 = { + instancePath: instancePath + "/createUpnClaim", + schemaPath: "#/properties/createUpnClaim/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err9]; + } else { + vErrors.push(err9); + } + errors++; + } + } + if (data.destination !== void 0) { + let data5 = data.destination; + if (typeof data5 === "string") { + if (func4(data5) > 2048) { + const err10 = { + instancePath: instancePath + "/destination", + schemaPath: "#/properties/destination/maxLength", + keyword: "maxLength", + params: { limit: 2048 }, + message: "must NOT have more than 2048 characters" + }; + if (vErrors === null) { + vErrors = [err10]; + } else { + vErrors.push(err10); + } + errors++; + } + } else { + const err11 = { + instancePath: instancePath + "/destination", + schemaPath: "#/properties/destination/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err11]; + } else { + vErrors.push(err11); + } + errors++; + } + } + if (data.digestAlgorithm !== void 0) { + let data6 = data.digestAlgorithm; + if (!(data6 === "sha256" || data6 === "sha1")) { + const err12 = { + instancePath: instancePath + "/digestAlgorithm", + schemaPath: "#/properties/digestAlgorithm/enum", + keyword: "enum", + params: { allowedValues: schema57.properties.digestAlgorithm.enum }, + message: "must be equal to one of the allowed values" + }; + if (vErrors === null) { + vErrors = [err12]; + } else { + vErrors.push(err12); + } + errors++; + } + } + if (data.encryptionAlgorithm !== void 0) { + let data7 = data.encryptionAlgorithm; + if (!(data7 === "aes256-gcm" || data7 === "aes256-cbc")) { + const err13 = { + instancePath: instancePath + "/encryptionAlgorithm", + schemaPath: "#/properties/encryptionAlgorithm/enum", + keyword: "enum", + params: { + allowedValues: schema57.properties.encryptionAlgorithm.enum + }, + message: "must be equal to one of the allowed values" + }; + if (vErrors === null) { + vErrors = [err13]; + } else { + vErrors.push(err13); + } + errors++; + } + } + if (data.encryptionCert !== void 0) { + let data8 = data.encryptionCert; + if (typeof data8 === "string") { + if (func4(data8) > 4096) { + const err14 = { + instancePath: instancePath + "/encryptionCert", + schemaPath: "#/properties/encryptionCert/maxLength", + keyword: "maxLength", + params: { limit: 4096 }, + message: "must NOT have more than 4096 characters" + }; + if (vErrors === null) { + vErrors = [err14]; + } else { + vErrors.push(err14); + } + errors++; + } + } else { + const err15 = { + instancePath: instancePath + "/encryptionCert", + schemaPath: "#/properties/encryptionCert/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err15]; + } else { + vErrors.push(err15); + } + errors++; + } + } + if (data.encryptionPublicKey !== void 0) { + let data9 = data.encryptionPublicKey; + if (typeof data9 === "string") { + if (func4(data9) > 4096) { + const err16 = { + instancePath: instancePath + "/encryptionPublicKey", + schemaPath: "#/properties/encryptionPublicKey/maxLength", + keyword: "maxLength", + params: { limit: 4096 }, + message: "must NOT have more than 4096 characters" + }; + if (vErrors === null) { + vErrors = [err16]; + } else { + vErrors.push(err16); + } + errors++; + } + } else { + const err17 = { + instancePath: instancePath + "/encryptionPublicKey", + schemaPath: "#/properties/encryptionPublicKey/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err17]; + } else { + vErrors.push(err17); + } + errors++; + } + } + if (data.includeAttributeNameFormat !== void 0) { + if (typeof data.includeAttributeNameFormat !== "boolean") { + const err18 = { + instancePath: instancePath + "/includeAttributeNameFormat", + schemaPath: "#/properties/includeAttributeNameFormat/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err18]; + } else { + vErrors.push(err18); + } + errors++; + } + } + if (data.issuer !== void 0) { + let data11 = data.issuer; + if (typeof data11 === "string") { + if (func4(data11) > 512) { + const err19 = { + instancePath: instancePath + "/issuer", + schemaPath: "#/properties/issuer/maxLength", + keyword: "maxLength", + params: { limit: 512 }, + message: "must NOT have more than 512 characters" + }; + if (vErrors === null) { + vErrors = [err19]; + } else { + vErrors.push(err19); + } + errors++; + } + } else { + const err20 = { + instancePath: instancePath + "/issuer", + schemaPath: "#/properties/issuer/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err20]; + } else { + vErrors.push(err20); + } + errors++; + } + } + if (data.key !== void 0) { + let data12 = data.key; + if (typeof data12 === "string") { + if (func4(data12) > 4096) { + const err21 = { + instancePath: instancePath + "/key", + schemaPath: "#/properties/key/maxLength", + keyword: "maxLength", + params: { limit: 4096 }, + message: "must NOT have more than 4096 characters" + }; + if (vErrors === null) { + vErrors = [err21]; + } else { + vErrors.push(err21); + } + errors++; + } + } else { + const err22 = { + instancePath: instancePath + "/key", + schemaPath: "#/properties/key/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err22]; + } else { + vErrors.push(err22); + } + errors++; + } + } + if (data.lifetimeInSeconds !== void 0) { + let data13 = data.lifetimeInSeconds; + if (!(typeof data13 == "number" && isFinite(data13))) { + const err23 = { + instancePath: instancePath + "/lifetimeInSeconds", + schemaPath: "#/properties/lifetimeInSeconds/type", + keyword: "type", + params: { type: "number" }, + message: "must be number" + }; + if (vErrors === null) { + vErrors = [err23]; + } else { + vErrors.push(err23); + } + errors++; + } + } + if (data.mapIdentities !== void 0) { + if (typeof data.mapIdentities !== "boolean") { + const err24 = { + instancePath: instancePath + "/mapIdentities", + schemaPath: "#/properties/mapIdentities/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err24]; + } else { + vErrors.push(err24); + } + errors++; + } + } + if (data.mapUnknownClaimsAsIs !== void 0) { + if (typeof data.mapUnknownClaimsAsIs !== "boolean") { + const err25 = { + instancePath: instancePath + "/mapUnknownClaimsAsIs", + schemaPath: "#/properties/mapUnknownClaimsAsIs/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err25]; + } else { + vErrors.push(err25); + } + errors++; + } + } + if (data.nameIdentifierFormat !== void 0) { + let data16 = data.nameIdentifierFormat; + if (typeof data16 === "string") { + if (func4(data16) > 512) { + const err26 = { + instancePath: instancePath + "/nameIdentifierFormat", + schemaPath: "#/properties/nameIdentifierFormat/maxLength", + keyword: "maxLength", + params: { limit: 512 }, + message: "must NOT have more than 512 characters" + }; + if (vErrors === null) { + vErrors = [err26]; + } else { + vErrors.push(err26); + } + errors++; + } + } else { + const err27 = { + instancePath: instancePath + "/nameIdentifierFormat", + schemaPath: "#/properties/nameIdentifierFormat/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err27]; + } else { + vErrors.push(err27); + } + errors++; + } + } + if (data.nameIdentifierProbes !== void 0) { + let data17 = data.nameIdentifierProbes; + if (Array.isArray(data17)) { + if (data17.length > 10) { + const err28 = { + instancePath: instancePath + "/nameIdentifierProbes", + schemaPath: "#/properties/nameIdentifierProbes/maxItems", + keyword: "maxItems", + params: { limit: 10 }, + message: "must NOT have more than 10 items" + }; + if (vErrors === null) { + vErrors = [err28]; + } else { + vErrors.push(err28); + } + errors++; + } + const len0 = data17.length; + for (let i0 = 0; i0 < len0; i0++) { + let data18 = data17[i0]; + if (typeof data18 === "string") { + if (func4(data18) > 512) { + const err29 = { + instancePath: instancePath + "/nameIdentifierProbes/" + i0, + schemaPath: "#/properties/nameIdentifierProbes/items/maxLength", + keyword: "maxLength", + params: { limit: 512 }, + message: "must NOT have more than 512 characters" + }; + if (vErrors === null) { + vErrors = [err29]; + } else { + vErrors.push(err29); + } + errors++; + } + } else { + const err30 = { + instancePath: instancePath + "/nameIdentifierProbes/" + i0, + schemaPath: "#/properties/nameIdentifierProbes/items/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err30]; + } else { + vErrors.push(err30); + } + errors++; + } + } + } else { + const err31 = { + instancePath: instancePath + "/nameIdentifierProbes", + schemaPath: "#/properties/nameIdentifierProbes/type", + keyword: "type", + params: { type: "array" }, + message: "must be array" + }; + if (vErrors === null) { + vErrors = [err31]; + } else { + vErrors.push(err31); + } + errors++; + } + } + if (data.passthroughClaimsWithNoMapping !== void 0) { + if (typeof data.passthroughClaimsWithNoMapping !== "boolean") { + const err32 = { + instancePath: instancePath + "/passthroughClaimsWithNoMapping", + schemaPath: "#/properties/passthroughClaimsWithNoMapping/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err32]; + } else { + vErrors.push(err32); + } + errors++; + } + } + if (data.recipient !== void 0) { + let data20 = data.recipient; + if (typeof data20 === "string") { + if (func4(data20) > 2048) { + const err33 = { + instancePath: instancePath + "/recipient", + schemaPath: "#/properties/recipient/maxLength", + keyword: "maxLength", + params: { limit: 2048 }, + message: "must NOT have more than 2048 characters" + }; + if (vErrors === null) { + vErrors = [err33]; + } else { + vErrors.push(err33); + } + errors++; + } + } else { + const err34 = { + instancePath: instancePath + "/recipient", + schemaPath: "#/properties/recipient/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err34]; + } else { + vErrors.push(err34); + } + errors++; + } + } + if (data.signResponse !== void 0) { + if (typeof data.signResponse !== "boolean") { + const err35 = { + instancePath: instancePath + "/signResponse", + schemaPath: "#/properties/signResponse/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err35]; + } else { + vErrors.push(err35); + } + errors++; + } + } + if (data.signatureAlgorithm !== void 0) { + let data22 = data.signatureAlgorithm; + if (!(data22 === "rsa-sha256" || data22 === "rsa-sha1")) { + const err36 = { + instancePath: instancePath + "/signatureAlgorithm", + schemaPath: "#/properties/signatureAlgorithm/enum", + keyword: "enum", + params: { + allowedValues: schema57.properties.signatureAlgorithm.enum + }, + message: "must be equal to one of the allowed values" + }; + if (vErrors === null) { + vErrors = [err36]; + } else { + vErrors.push(err36); + } + errors++; + } + } + if (data.signingCert !== void 0) { + let data23 = data.signingCert; + if (typeof data23 === "string") { + if (func4(data23) > 4096) { + const err37 = { + instancePath: instancePath + "/signingCert", + schemaPath: "#/properties/signingCert/maxLength", + keyword: "maxLength", + params: { limit: 4096 }, + message: "must NOT have more than 4096 characters" + }; + if (vErrors === null) { + vErrors = [err37]; + } else { + vErrors.push(err37); + } + errors++; + } + } else { + const err38 = { + instancePath: instancePath + "/signingCert", + schemaPath: "#/properties/signingCert/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err38]; + } else { + vErrors.push(err38); + } + errors++; + } + } + if (data.typedAttributes !== void 0) { + if (typeof data.typedAttributes !== "boolean") { + const err39 = { + instancePath: instancePath + "/typedAttributes", + schemaPath: "#/properties/typedAttributes/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err39]; + } else { + vErrors.push(err39); + } + errors++; + } + } + } else { + const err40 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err40]; + } else { + vErrors.push(err40); + } + errors++; + } + validate117.errors = vErrors; + return errors === 0; +} +var schema66 = { enum: ["persistent", "non-persistent"] }; +var ModifyScope = validate60; +var pattern7 = new RegExp("^[a-zA-Z0-9@._+-]{1,255}$", "u"); +var CustomTokenExchangeDenyInput = validate319; +function validate320(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 1024) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 1024 }, + message: "must NOT have more than 1024 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (func4(data) < 1) { + const err1 = { + instancePath, + schemaPath: "#/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate320.errors = vErrors; + return errors === 0; +} +function validate322(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 4096) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 4096 }, + message: "must NOT have more than 4096 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (func4(data) < 1) { + const err1 = { + instancePath, + schemaPath: "#/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate322.errors = vErrors; + return errors === 0; +} +function validate319(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.code === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "code" }, + message: "must have required property 'code'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (data.reason === void 0) { + const err1 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "reason" }, + message: "must have required property 'reason'" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "code" || key0 === "reason")) { + const err2 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + } + if (data.code !== void 0) { + if (!validate320(data.code, { + instancePath: instancePath + "/code", + parentData: data, + parentDataProperty: "code", + rootData + })) { + vErrors = vErrors === null ? validate320.errors : vErrors.concat(validate320.errors); + errors = vErrors.length; + } + } + if (data.reason !== void 0) { + if (!validate322(data.reason, { + instancePath: instancePath + "/reason", + parentData: data, + parentDataProperty: "reason", + rootData + })) { + vErrors = vErrors === null ? validate322.errors : vErrors.concat(validate322.errors); + errors = vErrors.length; + } + } + } else { + const err3 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + validate319.errors = vErrors; + return errors === 0; +} +var CustomTokenExchangeRejectInvalidSubjectTokenInput = validate352; +function validate353(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 4096) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 4096 }, + message: "must NOT have more than 4096 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (func4(data) < 1) { + const err1 = { + instancePath, + schemaPath: "#/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate353.errors = vErrors; + return errors === 0; +} +function validate352(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.reason === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "reason" }, + message: "must have required property 'reason'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "reason")) { + const err1 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } + if (data.reason !== void 0) { + if (!validate353(data.reason, { + instancePath: instancePath + "/reason", + parentData: data, + parentDataProperty: "reason", + rootData + })) { + vErrors = vErrors === null ? validate353.errors : vErrors.concat(validate353.errors); + errors = vErrors.length; + } + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate352.errors = vErrors; + return errors === 0; +} +var CustomTokenExchangeSetMetadataInput = validate355; +function validate356(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data !== "string") { + const err0 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + validate356.errors = vErrors; + return errors === 0; +} +function validate355(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.key === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "key" }, + message: "must have required property 'key'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "key" || key0 === "value")) { + const err1 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } + if (data.key !== void 0) { + if (!validate356(data.key, { + instancePath: instancePath + "/key", + parentData: data, + parentDataProperty: "key", + rootData + })) { + vErrors = vErrors === null ? validate356.errors : vErrors.concat(validate356.errors); + errors = vErrors.length; + } + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate355.errors = vErrors; + return errors === 0; +} +var CustomTokenExchangeSetOrganizationInput = validate358; +function validate359(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 50) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 50 }, + message: "must NOT have more than 50 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (func4(data) < 1) { + const err1 = { + instancePath, + schemaPath: "#/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate359.errors = vErrors; + return errors === 0; +} +function validate358(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.organization_id_or_name === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "organization_id_or_name" }, + message: "must have required property 'organization_id_or_name'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "organization_id_or_name")) { + const err1 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } + if (data.organization_id_or_name !== void 0) { + if (!validate359(data.organization_id_or_name, { + instancePath: instancePath + "/organization_id_or_name", + parentData: data, + parentDataProperty: "organization_id_or_name", + rootData + })) { + vErrors = vErrors === null ? validate359.errors : vErrors.concat(validate359.errors); + errors = vErrors.length; + } + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate358.errors = vErrors; + return errors === 0; +} +var CustomTokenExchangeSetUserByConnectionInput = validate361; +function validate362(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 512) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 512 }, + message: "must NOT have more than 512 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (func4(data) < 1) { + const err1 = { + instancePath, + schemaPath: "#/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate362.errors = vErrors; + return errors === 0; +} +var schema180 = { + description: "Options to control the behavior of the setUserByConnection command.", + type: "object", + required: ["creationBehavior", "updateBehavior"], + additionalProperties: false, + properties: { + creationBehavior: { + description: "Behavior to apply if no user with the specified user_id exists in the connection.", + enum: ["create_if_not_exists", "none"] + }, + updateBehavior: { + description: "Behavior to apply if a user with specified user_id already exists in the connection.", + enum: ["replace", "none"] + } + } +}; +function validate364(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.creationBehavior === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "creationBehavior" }, + message: "must have required property 'creationBehavior'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (data.updateBehavior === void 0) { + const err1 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "updateBehavior" }, + message: "must have required property 'updateBehavior'" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "creationBehavior" || key0 === "updateBehavior")) { + const err2 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + } + if (data.creationBehavior !== void 0) { + let data0 = data.creationBehavior; + if (!(data0 === "create_if_not_exists" || data0 === "none")) { + const err3 = { + instancePath: instancePath + "/creationBehavior", + schemaPath: "#/properties/creationBehavior/enum", + keyword: "enum", + params: { allowedValues: schema180.properties.creationBehavior.enum }, + message: "must be equal to one of the allowed values" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + } + if (data.updateBehavior !== void 0) { + let data1 = data.updateBehavior; + if (!(data1 === "replace" || data1 === "none")) { + const err4 = { + instancePath: instancePath + "/updateBehavior", + schemaPath: "#/properties/updateBehavior/enum", + keyword: "enum", + params: { allowedValues: schema180.properties.updateBehavior.enum }, + message: "must be equal to one of the allowed values" + }; + if (vErrors === null) { + vErrors = [err4]; + } else { + vErrors.push(err4); + } + errors++; + } + } + } else { + const err5 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err5]; + } else { + vErrors.push(err5); + } + errors++; + } + validate364.errors = vErrors; + return errors === 0; +} +function validate366(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (Object.keys(data).length > 24) { + const err0 = { + instancePath, + schemaPath: "#/maxProperties", + keyword: "maxProperties", + params: { limit: 24 }, + message: "must NOT have more than 24 properties" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (data.user_id === void 0) { + const err1 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "user_id" }, + message: "must have required property 'user_id'" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + if (data.email !== void 0) { + if (typeof data.email !== "string") { + const err2 = { + instancePath: instancePath + "/email", + schemaPath: "#/properties/email/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + } + if (data.email_verified !== void 0) { + if (typeof data.email_verified !== "boolean") { + const err3 = { + instancePath: instancePath + "/email_verified", + schemaPath: "#/properties/email_verified/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + } + if (data.family_name !== void 0) { + let data2 = data.family_name; + if (typeof data2 === "string") { + if (func4(data2) > 150) { + const err4 = { + instancePath: instancePath + "/family_name", + schemaPath: "#/properties/family_name/maxLength", + keyword: "maxLength", + params: { limit: 150 }, + message: "must NOT have more than 150 characters" + }; + if (vErrors === null) { + vErrors = [err4]; + } else { + vErrors.push(err4); + } + errors++; + } + if (func4(data2) < 1) { + const err5 = { + instancePath: instancePath + "/family_name", + schemaPath: "#/properties/family_name/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err5]; + } else { + vErrors.push(err5); + } + errors++; + } + } else { + const err6 = { + instancePath: instancePath + "/family_name", + schemaPath: "#/properties/family_name/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err6]; + } else { + vErrors.push(err6); + } + errors++; + } + } + if (data.given_name !== void 0) { + let data3 = data.given_name; + if (typeof data3 === "string") { + if (func4(data3) > 150) { + const err7 = { + instancePath: instancePath + "/given_name", + schemaPath: "#/properties/given_name/maxLength", + keyword: "maxLength", + params: { limit: 150 }, + message: "must NOT have more than 150 characters" + }; + if (vErrors === null) { + vErrors = [err7]; + } else { + vErrors.push(err7); + } + errors++; + } + if (func4(data3) < 1) { + const err8 = { + instancePath: instancePath + "/given_name", + schemaPath: "#/properties/given_name/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err8]; + } else { + vErrors.push(err8); + } + errors++; + } + } else { + const err9 = { + instancePath: instancePath + "/given_name", + schemaPath: "#/properties/given_name/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err9]; + } else { + vErrors.push(err9); + } + errors++; + } + } + if (data.name !== void 0) { + let data4 = data.name; + if (typeof data4 === "string") { + if (func4(data4) > 300) { + const err10 = { + instancePath: instancePath + "/name", + schemaPath: "#/properties/name/maxLength", + keyword: "maxLength", + params: { limit: 300 }, + message: "must NOT have more than 300 characters" + }; + if (vErrors === null) { + vErrors = [err10]; + } else { + vErrors.push(err10); + } + errors++; + } + if (func4(data4) < 1) { + const err11 = { + instancePath: instancePath + "/name", + schemaPath: "#/properties/name/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err11]; + } else { + vErrors.push(err11); + } + errors++; + } + } else { + const err12 = { + instancePath: instancePath + "/name", + schemaPath: "#/properties/name/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err12]; + } else { + vErrors.push(err12); + } + errors++; + } + } + if (data.nickname !== void 0) { + let data5 = data.nickname; + if (typeof data5 === "string") { + if (func4(data5) > 300) { + const err13 = { + instancePath: instancePath + "/nickname", + schemaPath: "#/properties/nickname/maxLength", + keyword: "maxLength", + params: { limit: 300 }, + message: "must NOT have more than 300 characters" + }; + if (vErrors === null) { + vErrors = [err13]; + } else { + vErrors.push(err13); + } + errors++; + } + if (func4(data5) < 1) { + const err14 = { + instancePath: instancePath + "/nickname", + schemaPath: "#/properties/nickname/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err14]; + } else { + vErrors.push(err14); + } + errors++; + } + } else { + const err15 = { + instancePath: instancePath + "/nickname", + schemaPath: "#/properties/nickname/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err15]; + } else { + vErrors.push(err15); + } + errors++; + } + } + if (data.phone_number !== void 0) { + if (typeof data.phone_number !== "string") { + const err16 = { + instancePath: instancePath + "/phone_number", + schemaPath: "#/properties/phone_number/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err16]; + } else { + vErrors.push(err16); + } + errors++; + } + } + if (data.phone_verified !== void 0) { + if (typeof data.phone_verified !== "boolean") { + const err17 = { + instancePath: instancePath + "/phone_verified", + schemaPath: "#/properties/phone_verified/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err17]; + } else { + vErrors.push(err17); + } + errors++; + } + } + if (data.picture !== void 0) { + if (!(typeof data.picture === "string")) { + const err18 = { + instancePath: instancePath + "/picture", + schemaPath: "#/properties/picture/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err18]; + } else { + vErrors.push(err18); + } + errors++; + } + } + if (data.user_id !== void 0) { + let data9 = data.user_id; + if (typeof data9 === "string") { + if (func4(data9) > 255) { + const err19 = { + instancePath: instancePath + "/user_id", + schemaPath: "#/properties/user_id/maxLength", + keyword: "maxLength", + params: { limit: 255 }, + message: "must NOT have more than 255 characters" + }; + if (vErrors === null) { + vErrors = [err19]; + } else { + vErrors.push(err19); + } + errors++; + } + if (func4(data9) < 1) { + const err20 = { + instancePath: instancePath + "/user_id", + schemaPath: "#/properties/user_id/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err20]; + } else { + vErrors.push(err20); + } + errors++; + } + } else { + const err21 = { + instancePath: instancePath + "/user_id", + schemaPath: "#/properties/user_id/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err21]; + } else { + vErrors.push(err21); + } + errors++; + } + } + if (data.username !== void 0) { + let data10 = data.username; + if (typeof data10 === "string") { + if (func4(data10) > 128) { + const err22 = { + instancePath: instancePath + "/username", + schemaPath: "#/properties/username/maxLength", + keyword: "maxLength", + params: { limit: 128 }, + message: "must NOT have more than 128 characters" + }; + if (vErrors === null) { + vErrors = [err22]; + } else { + vErrors.push(err22); + } + errors++; + } + if (func4(data10) < 1) { + const err23 = { + instancePath: instancePath + "/username", + schemaPath: "#/properties/username/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err23]; + } else { + vErrors.push(err23); + } + errors++; + } + } else { + const err24 = { + instancePath: instancePath + "/username", + schemaPath: "#/properties/username/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err24]; + } else { + vErrors.push(err24); + } + errors++; + } + } + if (data.verify_email !== void 0) { + if (typeof data.verify_email !== "boolean") { + const err25 = { + instancePath: instancePath + "/verify_email", + schemaPath: "#/properties/verify_email/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err25]; + } else { + vErrors.push(err25); + } + errors++; + } + } + } else { + const err26 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err26]; + } else { + vErrors.push(err26); + } + errors++; + } + validate366.errors = vErrors; + return errors === 0; +} +function validate361(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.connection_name === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "connection_name" }, + message: "must have required property 'connection_name'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (data.user_attributes === void 0) { + const err1 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "user_attributes" }, + message: "must have required property 'user_attributes'" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + if (data.options === void 0) { + const err2 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "options" }, + message: "must have required property 'options'" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "connection_name" || key0 === "options" || key0 === "user_attributes")) { + const err3 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + } + if (data.connection_name !== void 0) { + if (!validate362(data.connection_name, { + instancePath: instancePath + "/connection_name", + parentData: data, + parentDataProperty: "connection_name", + rootData + })) { + vErrors = vErrors === null ? validate362.errors : vErrors.concat(validate362.errors); + errors = vErrors.length; + } + } + if (data.options !== void 0) { + if (!validate364(data.options, { + instancePath: instancePath + "/options", + parentData: data, + parentDataProperty: "options", + rootData + })) { + vErrors = vErrors === null ? validate364.errors : vErrors.concat(validate364.errors); + errors = vErrors.length; + } + } + if (data.user_attributes !== void 0) { + if (!validate366(data.user_attributes, { + instancePath: instancePath + "/user_attributes", + parentData: data, + parentDataProperty: "user_attributes", + rootData + })) { + vErrors = vErrors === null ? validate366.errors : vErrors.concat(validate366.errors); + errors = vErrors.length; + } + } + } else { + const err4 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err4]; + } else { + vErrors.push(err4); + } + errors++; + } + validate361.errors = vErrors; + return errors === 0; +} +var CustomTokenExchangeSetUserByIdInput = validate368; +function validate369(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 512) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 512 }, + message: "must NOT have more than 512 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (func4(data) < 1) { + const err1 = { + instancePath, + schemaPath: "#/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate369.errors = vErrors; + return errors === 0; +} +function validate368(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.user_id === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "user_id" }, + message: "must have required property 'user_id'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "user_id")) { + const err1 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } + if (data.user_id !== void 0) { + if (!validate369(data.user_id, { + instancePath: instancePath + "/user_id", + parentData: data, + parentDataProperty: "user_id", + rootData + })) { + vErrors = vErrors === null ? validate369.errors : vErrors.concat(validate369.errors); + errors = vErrors.length; + } + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate368.errors = vErrors; + return errors === 0; +} +var PasswordResetPostChallengeRenderPromptInput = validate561; +function validate562(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 48) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 48 }, + message: "must NOT have more than 48 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate562.errors = vErrors; + return errors === 0; +} +function validate564(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + for (const key0 in data) { + if (!(key0 === "fields" || key0 === "vars")) { + const err0 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } + if (data.fields !== void 0) { + let data0 = data.fields; + if (data0 && typeof data0 == "object" && !Array.isArray(data0)) { + if (Object.keys(data0).length > 24) { + const err1 = { + instancePath: instancePath + "/fields", + schemaPath: "#/properties/fields/maxProperties", + keyword: "maxProperties", + params: { limit: 24 }, + message: "must NOT have more than 24 properties" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + for (const key1 in data0) { + if (!pattern5.test(key1)) { + const err2 = { + instancePath: instancePath + "/fields", + schemaPath: "#/properties/fields/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key1 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + } + } else { + const err3 = { + instancePath: instancePath + "/fields", + schemaPath: "#/properties/fields/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + } + if (data.vars !== void 0) { + let data1 = data.vars; + if (data1 && typeof data1 == "object" && !Array.isArray(data1)) { + if (Object.keys(data1).length > 24) { + const err4 = { + instancePath: instancePath + "/vars", + schemaPath: "#/properties/vars/maxProperties", + keyword: "maxProperties", + params: { limit: 24 }, + message: "must NOT have more than 24 properties" + }; + if (vErrors === null) { + vErrors = [err4]; + } else { + vErrors.push(err4); + } + errors++; + } + for (const key2 in data1) { + if (!pattern6.test(key2)) { + const err5 = { + instancePath: instancePath + "/vars", + schemaPath: "#/properties/vars/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key2 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err5]; + } else { + vErrors.push(err5); + } + errors++; + } + } + } else { + const err6 = { + instancePath: instancePath + "/vars", + schemaPath: "#/properties/vars/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err6]; + } else { + vErrors.push(err6); + } + errors++; + } + } + } else { + const err7 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err7]; + } else { + vErrors.push(err7); + } + errors++; + } + validate564.errors = vErrors; + return errors === 0; +} +function validate561(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.promptId === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "promptId" }, + message: "must have required property 'promptId'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "promptId" || key0 === "promptOptions")) { + const err1 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } + if (data.promptId !== void 0) { + if (!validate562(data.promptId, { + instancePath: instancePath + "/promptId", + parentData: data, + parentDataProperty: "promptId", + rootData + })) { + vErrors = vErrors === null ? validate562.errors : vErrors.concat(validate562.errors); + errors = vErrors.length; + } + } + if (data.promptOptions !== void 0) { + if (!validate564(data.promptOptions, { + instancePath: instancePath + "/promptOptions", + parentData: data, + parentDataProperty: "promptOptions", + rootData + })) { + vErrors = vErrors === null ? validate564.errors : vErrors.concat(validate564.errors); + errors = vErrors.length; + } + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate561.errors = vErrors; + return errors === 0; +} +var PostLoginRenderPromptInput = validate739; +function validate740(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 48) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 48 }, + message: "must NOT have more than 48 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate740.errors = vErrors; + return errors === 0; +} +function validate742(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + for (const key0 in data) { + if (!(key0 === "fields" || key0 === "vars")) { + const err0 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } + if (data.fields !== void 0) { + let data0 = data.fields; + if (data0 && typeof data0 == "object" && !Array.isArray(data0)) { + if (Object.keys(data0).length > 24) { + const err1 = { + instancePath: instancePath + "/fields", + schemaPath: "#/properties/fields/maxProperties", + keyword: "maxProperties", + params: { limit: 24 }, + message: "must NOT have more than 24 properties" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + for (const key1 in data0) { + if (!pattern5.test(key1)) { + const err2 = { + instancePath: instancePath + "/fields", + schemaPath: "#/properties/fields/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key1 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + } + } else { + const err3 = { + instancePath: instancePath + "/fields", + schemaPath: "#/properties/fields/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + } + if (data.vars !== void 0) { + let data1 = data.vars; + if (data1 && typeof data1 == "object" && !Array.isArray(data1)) { + if (Object.keys(data1).length > 24) { + const err4 = { + instancePath: instancePath + "/vars", + schemaPath: "#/properties/vars/maxProperties", + keyword: "maxProperties", + params: { limit: 24 }, + message: "must NOT have more than 24 properties" + }; + if (vErrors === null) { + vErrors = [err4]; + } else { + vErrors.push(err4); + } + errors++; + } + for (const key2 in data1) { + if (!pattern6.test(key2)) { + const err5 = { + instancePath: instancePath + "/vars", + schemaPath: "#/properties/vars/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key2 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err5]; + } else { + vErrors.push(err5); + } + errors++; + } + } + } else { + const err6 = { + instancePath: instancePath + "/vars", + schemaPath: "#/properties/vars/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err6]; + } else { + vErrors.push(err6); + } + errors++; + } + } + } else { + const err7 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err7]; + } else { + vErrors.push(err7); + } + errors++; + } + validate742.errors = vErrors; + return errors === 0; +} +function validate739(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.promptId === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "promptId" }, + message: "must have required property 'promptId'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "promptId" || key0 === "promptOptions")) { + const err1 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } + if (data.promptId !== void 0) { + if (!validate740(data.promptId, { + instancePath: instancePath + "/promptId", + parentData: data, + parentDataProperty: "promptId", + rootData + })) { + vErrors = vErrors === null ? validate740.errors : vErrors.concat(validate740.errors); + errors = vErrors.length; + } + } + if (data.promptOptions !== void 0) { + if (!validate742(data.promptOptions, { + instancePath: instancePath + "/promptOptions", + parentData: data, + parentDataProperty: "promptOptions", + rootData + })) { + vErrors = vErrors === null ? validate742.errors : vErrors.concat(validate742.errors); + errors = vErrors.length; + } + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate739.errors = vErrors; + return errors === 0; +} +var PostLoginRevokeRefreshTokenInput = validate139; +function validate140(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 1024) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 1024 }, + message: "must NOT have more than 1024 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate140.errors = vErrors; + return errors === 0; +} +function validate139(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + for (const key0 in data) { + if (!(key0 === "message")) { + const err0 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } + if (data.message !== void 0) { + if (!validate140(data.message, { + instancePath: instancePath + "/message", + parentData: data, + parentDataProperty: "message", + rootData + })) { + vErrors = vErrors === null ? validate140.errors : vErrors.concat(validate140.errors); + errors = vErrors.length; + } + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate139.errors = vErrors; + return errors === 0; +} +var PostLoginRevokeSessionInput = validate146; +function validate147(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data !== "string") { + const err0 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + validate147.errors = vErrors; + return errors === 0; +} +function validate149(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + for (const key0 in data) { + if (!(key0 === "preserveRefreshTokens")) { + const err0 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } + if (data.preserveRefreshTokens !== void 0) { + if (typeof data.preserveRefreshTokens !== "boolean") { + const err1 = { + instancePath: instancePath + "/preserveRefreshTokens", + schemaPath: "#/properties/preserveRefreshTokens/type", + keyword: "type", + params: { type: "boolean" }, + message: "must be boolean" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate149.errors = vErrors; + return errors === 0; +} +function validate146(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + for (const key0 in data) { + if (!(key0 === "message" || key0 === "options")) { + const err0 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } + if (data.message !== void 0) { + if (!validate147(data.message, { + instancePath: instancePath + "/message", + parentData: data, + parentDataProperty: "message", + rootData + })) { + vErrors = vErrors === null ? validate147.errors : vErrors.concat(validate147.errors); + errors = vErrors.length; + } + } + if (data.options !== void 0) { + if (!validate149(data.options, { + instancePath: instancePath + "/options", + parentData: data, + parentDataProperty: "options", + rootData + })) { + vErrors = vErrors === null ? validate149.errors : vErrors.concat(validate149.errors); + errors = vErrors.length; + } + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate146.errors = vErrors; + return errors === 0; +} +var PostLoginSetCookieModeInput = validate132; +function validate133(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (!(data === "persistent" || data === "non-persistent")) { + const err0 = { + instancePath, + schemaPath: "#/enum", + keyword: "enum", + params: { allowedValues: schema66.enum }, + message: "must be equal to one of the allowed values" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + validate133.errors = vErrors; + return errors === 0; +} +function validate132(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.mode === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "mode" }, + message: "must have required property 'mode'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "mode")) { + const err1 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } + if (data.mode !== void 0) { + if (!validate133(data.mode, { + instancePath: instancePath + "/mode", + parentData: data, + parentDataProperty: "mode", + rootData + })) { + vErrors = vErrors === null ? validate133.errors : vErrors.concat(validate133.errors); + errors = vErrors.length; + } + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate132.errors = vErrors; + return errors === 0; +} +var PostLoginSetRefreshTokenExpirationInput = validate95; +function validate96(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (!(typeof data == "number" && isFinite(data))) { + const err0 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "number" }, + message: "must be number" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + validate96.errors = vErrors; + return errors === 0; +} +function validate98(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (!(typeof data == "number" && isFinite(data))) { + const err0 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "number" }, + message: "must be number" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + validate98.errors = vErrors; + return errors === 0; +} +function validate95(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + for (const key0 in data) { + if (!(key0 === "absolute" || key0 === "inactivity")) { + const err0 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } + if (data.absolute !== void 0) { + if (!validate96(data.absolute, { + instancePath: instancePath + "/absolute", + parentData: data, + parentDataProperty: "absolute", + rootData + })) { + vErrors = vErrors === null ? validate96.errors : vErrors.concat(validate96.errors); + errors = vErrors.length; + } + } + if (data.inactivity !== void 0) { + if (!validate98(data.inactivity, { + instancePath: instancePath + "/inactivity", + parentData: data, + parentDataProperty: "inactivity", + rootData + })) { + vErrors = vErrors === null ? validate98.errors : vErrors.concat(validate98.errors); + errors = vErrors.length; + } + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate95.errors = vErrors; + return errors === 0; +} +var PostLoginSetSAMLAttributeInput = validate106; +function validate107(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 1024) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 1024 }, + message: "must NOT have more than 1024 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate107.errors = vErrors; + return errors === 0; +} +function validate109(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data !== "string" && !(typeof data == "number" && isFinite(data)) && typeof data !== "boolean" && data !== null && !Array.isArray(data)) { + const err0 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: schema53.type }, + message: "must be string,number,boolean,null,array" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (typeof data === "string") { + if (func4(data) > 2048) { + const err1 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 2048 }, + message: "must NOT have more than 2048 characters" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } + if (Array.isArray(data)) { + const len0 = data.length; + for (let i0 = 0; i0 < len0; i0++) { + let data0 = data[i0]; + if (typeof data0 !== "string" && !(typeof data0 == "number" && isFinite(data0)) && typeof data0 !== "boolean") { + const err2 = { + instancePath: instancePath + "/" + i0, + schemaPath: "#/items/type", + keyword: "type", + params: { type: schema53.items.type }, + message: "must be string,number,boolean" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + if (typeof data0 === "string") { + if (func4(data0) > 2048) { + const err3 = { + instancePath: instancePath + "/" + i0, + schemaPath: "#/items/maxLength", + keyword: "maxLength", + params: { limit: 2048 }, + message: "must NOT have more than 2048 characters" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + } + } + } + validate109.errors = vErrors; + return errors === 0; +} +function validate106(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.attribute === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "attribute" }, + message: "must have required property 'attribute'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (data.value === void 0) { + const err1 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "value" }, + message: "must have required property 'value'" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "attribute" || key0 === "value")) { + const err2 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + } + if (data.attribute !== void 0) { + if (!validate107(data.attribute, { + instancePath: instancePath + "/attribute", + parentData: data, + parentDataProperty: "attribute", + rootData + })) { + vErrors = vErrors === null ? validate107.errors : vErrors.concat(validate107.errors); + errors = vErrors.length; + } + } + if (data.value !== void 0) { + if (!validate109(data.value, { + instancePath: instancePath + "/value", + parentData: data, + parentDataProperty: "value", + rootData + })) { + vErrors = vErrors === null ? validate109.errors : vErrors.concat(validate109.errors); + errors = vErrors.length; + } + } + } else { + const err3 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + validate106.errors = vErrors; + return errors === 0; +} +var PostLoginSetSAMLConfigurationInput = validate117; +var PostLoginSetSessionExpirationInput = validate121; +function validate122(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (!(typeof data == "number" && isFinite(data))) { + const err0 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "number" }, + message: "must be number" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + validate122.errors = vErrors; + return errors === 0; +} +function validate124(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (!(typeof data == "number" && isFinite(data))) { + const err0 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "number" }, + message: "must be number" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + validate124.errors = vErrors; + return errors === 0; +} +function validate121(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + for (const key0 in data) { + if (!(key0 === "absolute" || key0 === "inactivity")) { + const err0 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } + if (data.absolute !== void 0) { + if (!validate122(data.absolute, { + instancePath: instancePath + "/absolute", + parentData: data, + parentDataProperty: "absolute", + rootData + })) { + vErrors = vErrors === null ? validate122.errors : vErrors.concat(validate122.errors); + errors = vErrors.length; + } + } + if (data.inactivity !== void 0) { + if (!validate124(data.inactivity, { + instancePath: instancePath + "/inactivity", + parentData: data, + parentDataProperty: "inactivity", + rootData + })) { + vErrors = vErrors === null ? validate124.errors : vErrors.concat(validate124.errors); + errors = vErrors.length; + } + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate121.errors = vErrors; + return errors === 0; +} +var PostLoginValidationErrorInput = validate744; +function validate745(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 100) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 100 }, + message: "must NOT have more than 100 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate745.errors = vErrors; + return errors === 0; +} +function validate747(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 100) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 100 }, + message: "must NOT have more than 100 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate747.errors = vErrors; + return errors === 0; +} +function validate744(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.errorCode === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "errorCode" }, + message: "must have required property 'errorCode'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (data.errorMessage === void 0) { + const err1 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "errorMessage" }, + message: "must have required property 'errorMessage'" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "errorCode" || key0 === "errorMessage")) { + const err2 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + } + if (data.errorCode !== void 0) { + if (!validate745(data.errorCode, { + instancePath: instancePath + "/errorCode", + parentData: data, + parentDataProperty: "errorCode", + rootData + })) { + vErrors = vErrors === null ? validate745.errors : vErrors.concat(validate745.errors); + errors = vErrors.length; + } + } + if (data.errorMessage !== void 0) { + if (!validate747(data.errorMessage, { + instancePath: instancePath + "/errorMessage", + parentData: data, + parentDataProperty: "errorMessage", + rootData + })) { + vErrors = vErrors === null ? validate747.errors : vErrors.concat(validate747.errors); + errors = vErrors.length; + } + } + } else { + const err3 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + validate744.errors = vErrors; + return errors === 0; +} +var PreUserRegistrationSetUserIdInput = validate913; +function validate914(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 255) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 255 }, + message: "must NOT have more than 255 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (func4(data) < 1) { + const err1 = { + instancePath, + schemaPath: "#/minLength", + keyword: "minLength", + params: { limit: 1 }, + message: "must NOT have fewer than 1 characters" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + if (!pattern7.test(data)) { + const err2 = { + instancePath, + schemaPath: "#/pattern", + keyword: "pattern", + params: { pattern: "^[a-zA-Z0-9@._+-]{1,255}$" }, + message: 'must match pattern "^[a-zA-Z0-9@._+-]{1,255}$"' + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + } else { + const err3 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + validate914.errors = vErrors; + return errors === 0; +} +function validate913(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.user_id === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "user_id" }, + message: "must have required property 'user_id'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "user_id")) { + const err1 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + } + if (data.user_id !== void 0) { + if (!validate914(data.user_id, { + instancePath: instancePath + "/user_id", + parentData: data, + parentDataProperty: "user_id", + rootData + })) { + vErrors = vErrors === null ? validate914.errors : vErrors.concat(validate914.errors); + errors = vErrors.length; + } + } + } else { + const err2 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + validate913.errors = vErrors; + return errors === 0; +} +var PreUserRegistrationValidationErrorInput = validate916; +function validate917(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 100) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 100 }, + message: "must NOT have more than 100 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate917.errors = vErrors; + return errors === 0; +} +function validate919(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (typeof data === "string") { + if (func4(data) > 100) { + const err0 = { + instancePath, + schemaPath: "#/maxLength", + keyword: "maxLength", + params: { limit: 100 }, + message: "must NOT have more than 100 characters" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + } else { + const err1 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "string" }, + message: "must be string" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + validate919.errors = vErrors; + return errors === 0; +} +function validate916(data, { instancePath = "", parentData, parentDataProperty, rootData = data } = {}) { + let vErrors = null; + let errors = 0; + if (data && typeof data == "object" && !Array.isArray(data)) { + if (data.errorCode === void 0) { + const err0 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "errorCode" }, + message: "must have required property 'errorCode'" + }; + if (vErrors === null) { + vErrors = [err0]; + } else { + vErrors.push(err0); + } + errors++; + } + if (data.errorMessage === void 0) { + const err1 = { + instancePath, + schemaPath: "#/required", + keyword: "required", + params: { missingProperty: "errorMessage" }, + message: "must have required property 'errorMessage'" + }; + if (vErrors === null) { + vErrors = [err1]; + } else { + vErrors.push(err1); + } + errors++; + } + for (const key0 in data) { + if (!(key0 === "errorCode" || key0 === "errorMessage")) { + const err2 = { + instancePath, + schemaPath: "#/additionalProperties", + keyword: "additionalProperties", + params: { additionalProperty: key0 }, + message: "must NOT have additional properties" + }; + if (vErrors === null) { + vErrors = [err2]; + } else { + vErrors.push(err2); + } + errors++; + } + } + if (data.errorCode !== void 0) { + if (!validate917(data.errorCode, { + instancePath: instancePath + "/errorCode", + parentData: data, + parentDataProperty: "errorCode", + rootData + })) { + vErrors = vErrors === null ? validate917.errors : vErrors.concat(validate917.errors); + errors = vErrors.length; + } + } + if (data.errorMessage !== void 0) { + if (!validate919(data.errorMessage, { + instancePath: instancePath + "/errorMessage", + parentData: data, + parentDataProperty: "errorMessage", + rootData + })) { + vErrors = vErrors === null ? validate919.errors : vErrors.concat(validate919.errors); + errors = vErrors.length; + } + } + } else { + const err3 = { + instancePath, + schemaPath: "#/type", + keyword: "type", + params: { type: "object" }, + message: "must be object" + }; + if (vErrors === null) { + vErrors = [err3]; + } else { + vErrors.push(err3); + } + errors++; + } + validate916.errors = vErrors; + return errors === 0; +} +var CustomTokenExchangeDenyInputCodec = /* @__PURE__ */ createCodec2( + "CustomTokenExchangeDenyInput", + "CustomTokenExchangeDenyInput", + CustomTokenExchangeDenyInput +); +var CustomTokenExchangeRejectInvalidSubjectTokenInputCodec = /* @__PURE__ */ createCodec2( + "CustomTokenExchangeRejectInvalidSubjectTokenInput", + "CustomTokenExchangeRejectInvalidSubjectTokenInput", + CustomTokenExchangeRejectInvalidSubjectTokenInput +); +var CustomTokenExchangeSetMetadataInputCodec = /* @__PURE__ */ createCodec2( + "CustomTokenExchangeSetMetadataInput", + "CustomTokenExchangeSetMetadataInput", + CustomTokenExchangeSetMetadataInput +); +var CustomTokenExchangeSetOrganizationInputCodec = /* @__PURE__ */ createCodec2( + "CustomTokenExchangeSetOrganizationInput", + "CustomTokenExchangeSetOrganizationInput", + CustomTokenExchangeSetOrganizationInput +); +var CustomTokenExchangeSetUserByConnectionInputCodec = /* @__PURE__ */ createCodec2( + "CustomTokenExchangeSetUserByConnectionInput", + "CustomTokenExchangeSetUserByConnectionInput", + CustomTokenExchangeSetUserByConnectionInput +); +var CustomTokenExchangeSetUserByIdInputCodec = /* @__PURE__ */ createCodec2( + "CustomTokenExchangeSetUserByIdInput", + "CustomTokenExchangeSetUserByIdInput", + CustomTokenExchangeSetUserByIdInput +); +var ModifyScopeCodec = /* @__PURE__ */ createCodec2( + "ModifyScope", + "ModifyScope", + ModifyScope +); +var PasswordResetPostChallengeRenderPromptInputCodec = /* @__PURE__ */ createCodec2( + "PasswordResetPostChallengeRenderPromptInput", + "PasswordResetPostChallengeRenderPromptInput", + PasswordResetPostChallengeRenderPromptInput +); +var PostLoginRenderPromptInputCodec = /* @__PURE__ */ createCodec2( + "PostLoginRenderPromptInput", + "PostLoginRenderPromptInput", + PostLoginRenderPromptInput +); +var PostLoginRevokeRefreshTokenInputCodec = /* @__PURE__ */ createCodec2( + "PostLoginRevokeRefreshTokenInput", + "PostLoginRevokeRefreshTokenInput", + PostLoginRevokeRefreshTokenInput +); +var PostLoginRevokeSessionInputCodec = /* @__PURE__ */ createCodec2( + "PostLoginRevokeSessionInput", + "PostLoginRevokeSessionInput", + PostLoginRevokeSessionInput +); +var PostLoginSetCookieModeInputCodec = /* @__PURE__ */ createCodec2( + "PostLoginSetCookieModeInput", + "PostLoginSetCookieModeInput", + PostLoginSetCookieModeInput +); +var PostLoginSetRefreshTokenExpirationInputCodec = /* @__PURE__ */ createCodec2( + "PostLoginSetRefreshTokenExpirationInput", + "PostLoginSetRefreshTokenExpirationInput", + PostLoginSetRefreshTokenExpirationInput +); +var PostLoginSetSAMLAttributeInputCodec = /* @__PURE__ */ createCodec2( + "PostLoginSetSAMLAttributeInput", + "PostLoginSetSAMLAttributeInput", + PostLoginSetSAMLAttributeInput +); +var PostLoginSetSAMLConfigurationInputCodec = /* @__PURE__ */ createCodec2( + "PostLoginSetSAMLConfigurationInput", + "PostLoginSetSAMLConfigurationInput", + PostLoginSetSAMLConfigurationInput +); +var PostLoginSetSessionExpirationInputCodec = /* @__PURE__ */ createCodec2( + "PostLoginSetSessionExpirationInput", + "PostLoginSetSessionExpirationInput", + PostLoginSetSessionExpirationInput +); +var PostLoginValidationErrorInputCodec = /* @__PURE__ */ createCodec2( + "PostLoginValidationErrorInput", + "PostLoginValidationErrorInput", + PostLoginValidationErrorInput +); +var PreUserRegistrationSetUserIdInputCodec = /* @__PURE__ */ createCodec2( + "PreUserRegistrationSetUserIdInput", + "PreUserRegistrationSetUserIdInput", + PreUserRegistrationSetUserIdInput +); +var PreUserRegistrationValidationErrorInputCodec = /* @__PURE__ */ createCodec2( + "PreUserRegistrationValidationErrorInput", + "PreUserRegistrationValidationErrorInput", + PreUserRegistrationValidationErrorInput +); + +// src/helpers/index.ts +init_typed_validator(); + +// src/helpers/accessToken.ts +var accessToken_exports = {}; +__export(accessToken_exports, { + MAX_SCOPE_COUNT_LIMIT: () => MAX_SCOPE_COUNT_LIMIT, + MAX_SCOPE_COUNT_LOWER_LIMIT: () => MAX_SCOPE_COUNT_LOWER_LIMIT, + MAX_SCOPE_LENGTH: () => MAX_SCOPE_LENGTH, + addScope: () => addScope, + assertValidModifyScopeRecord: () => assertValidModifyScopeRecord, + assertValidScope: () => assertValidScope, + modifyScope: () => modifyScope, + removeScope: () => removeScope +}); +var MAX_SCOPE_COUNT_LIMIT = 1e3; +var MAX_SCOPE_COUNT_LOWER_LIMIT = MAX_SCOPE_COUNT_LIMIT; +var MAX_SCOPE_LENGTH = 280; +function createModification(op, scope) { + const modifications = /* @__PURE__ */ Object.create(null); + modifications[scope] = op; + return modifications; +} +var whitespaceRegex = new RegExp(/\s+/g); +function hasWhitespace(str) { + return whitespaceRegex.test(str); +} +function assertValidScope(scope) { + const length = scope.length; + if (length === 0) { + throw new Error("The value for the scope is required."); + } + if (length > MAX_SCOPE_LENGTH) { + throw new Error( + `The value for the scope exceeds the allowed maximum of ${MAX_SCOPE_LENGTH} characters.` + ); + } + if (hasWhitespace(scope)) { + throw new Error("The value for the scope must not have whitespace."); + } +} +function assertValidModifyScopeRecord(command) { + try { + ModifyScopeCodec.validate(command); + } catch (_) { + throw new Error("The value for the scope is of the wrong type or exceeds the size limit."); + } +} +function modifyScope(newModifications, existingModifications) { + const modifications = Object.assign(/* @__PURE__ */ Object.create(null), existingModifications, newModifications); + const addCount = Object.values(modifications).filter((op) => op === "add").length; + if (addCount > MAX_SCOPE_COUNT_LIMIT) { + throw new Error( + `The number of scopes exceeds the allowed maximum of ${MAX_SCOPE_COUNT_LIMIT}.` + ); + } + const command = { + type: "ModifyScope", + target: "accessToken", + modifications + }; + assertValidModifyScopeRecord(command); + return command.modifications; +} +function addScope(scope) { + if (typeof scope !== "string") { + throw new TypeError("The value for the scope must be a string."); + } + const trimmedScope = scope.trim(); + assertValidScope(trimmedScope); + const command = { + type: "ModifyScope", + target: "accessToken", + modifications: createModification("add", trimmedScope) + }; + assertValidModifyScopeRecord(command); + return command; +} +function removeScope(scope) { + if (typeof scope !== "string") { + throw new TypeError("The value for the scope must be a string."); + } + const trimmedScope = scope.trim(); + assertValidScope(trimmedScope); + const command = { + type: "ModifyScope", + target: "accessToken", + modifications: createModification("remove", trimmedScope) + }; + assertValidModifyScopeRecord(command); + return command; +} + +// src/helpers/customClaim.ts +var customClaim_exports = {}; +__export(customClaim_exports, { + RESERVED_CUSTOM_CLAIMS: () => RESERVED_CUSTOM_CLAIMS, + assertSettableCustomClaim: () => assertSettableCustomClaim +}); +var RESERVED_CUSTOM_CLAIMS = ["scope"]; +function assertSettableCustomClaim(name) { + if (RESERVED_CUSTOM_CLAIMS.includes(name)) { + throw new Error(`The ${JSON.stringify(name)} claim cannot be set.`); + } +} + +// src/helpers/renderPrompt/constants.ts +var constants_exports = {}; +__export(constants_exports, { + MAX_RENDER_PROMPT_OPTIONS_BYTES: () => MAX_RENDER_PROMPT_OPTIONS_BYTES +}); +var MAX_RENDER_PROMPT_OPTIONS_BYTES = 24 * 1024; + +// src/helpers/samlResponse/constants.ts +var constants_exports2 = {}; +__export(constants_exports2, { + MAX_SAML_ATTRIBUTE_BYTES: () => MAX_SAML_ATTRIBUTE_BYTES, + MAX_SAML_ATTRIBUTE_CHANGES: () => MAX_SAML_ATTRIBUTE_CHANGES, + MAX_SAML_BYTES: () => MAX_SAML_BYTES, + MAX_SAML_VALUE_BYTES: () => MAX_SAML_VALUE_BYTES +}); +var MAX_SAML_ATTRIBUTE_CHANGES = 100; +var MAX_SAML_BYTES = 102400; +var MAX_SAML_ATTRIBUTE_BYTES = 1024; +var MAX_SAML_VALUE_BYTES = 2048; + +// src/helpers/setPrimaryUser/constants.ts +var constants_exports3 = {}; +__export(constants_exports3, { + MAX_USER_ID_LENGTH: () => MAX_USER_ID_LENGTH +}); +var MAX_USER_ID_LENGTH = 128; + +// src/helpers/index.ts +function formatValidationError(error) { + const messages = {}; + error.validatorErrors.forEach((e) => { + if (e.message) { + if (messages[e.instancePath]) { + messages[e.instancePath] += `, ${e.message}`; + } else { + messages[e.instancePath] = `${e.instancePath.split("/").pop()} ${e.message}`; + } + } + }); + return Object.values(messages).join("; "); +} +function validate(codec, data, errorPrefix) { + try { + const res = codec.validate(data); + return res; + } catch (e) { + if (e instanceof ValidationError) { + const prefix = errorPrefix ? `${errorPrefix}: ` : ""; + throw new Error(`${prefix}${formatValidationError(e)}`); + } + throw e; + } +} +var helpers = { + accessToken: accessToken_exports, + customClaim: customClaim_exports, + samlResponse: { + ...constants_exports2, + /** @deprecated use `validate` at the top level to handle Codecs error formatting */ + formatValidationError + }, + setPrimaryUser: constants_exports3, + renderPrompt: constants_exports +}; + +exports.CustomTokenExchangeDenyInputCodec = CustomTokenExchangeDenyInputCodec; +exports.CustomTokenExchangeRejectInvalidSubjectTokenInputCodec = CustomTokenExchangeRejectInvalidSubjectTokenInputCodec; +exports.CustomTokenExchangeSetMetadataInputCodec = CustomTokenExchangeSetMetadataInputCodec; +exports.CustomTokenExchangeSetOrganizationInputCodec = CustomTokenExchangeSetOrganizationInputCodec; +exports.CustomTokenExchangeSetUserByConnectionInputCodec = CustomTokenExchangeSetUserByConnectionInputCodec; +exports.CustomTokenExchangeSetUserByIdInputCodec = CustomTokenExchangeSetUserByIdInputCodec; +exports.MAX_RENDER_PROMPT_OPTIONS_BYTES = MAX_RENDER_PROMPT_OPTIONS_BYTES; +exports.MAX_SAML_BYTES = MAX_SAML_BYTES; +exports.MAX_USER_ID_LENGTH = MAX_USER_ID_LENGTH; +exports.PasswordResetPostChallengeRenderPromptInputCodec = PasswordResetPostChallengeRenderPromptInputCodec; +exports.PostLoginRenderPromptInputCodec = PostLoginRenderPromptInputCodec; +exports.PostLoginRevokeRefreshTokenInputCodec = PostLoginRevokeRefreshTokenInputCodec; +exports.PostLoginRevokeSessionInputCodec = PostLoginRevokeSessionInputCodec; +exports.PostLoginSetCookieModeInputCodec = PostLoginSetCookieModeInputCodec; +exports.PostLoginSetRefreshTokenExpirationInputCodec = PostLoginSetRefreshTokenExpirationInputCodec; +exports.PostLoginSetSAMLAttributeInputCodec = PostLoginSetSAMLAttributeInputCodec; +exports.PostLoginSetSAMLConfigurationInputCodec = PostLoginSetSAMLConfigurationInputCodec; +exports.PostLoginSetSessionExpirationInputCodec = PostLoginSetSessionExpirationInputCodec; +exports.PostLoginValidationErrorInputCodec = PostLoginValidationErrorInputCodec; +exports.PreUserRegistrationSetUserIdInputCodec = PreUserRegistrationSetUserIdInputCodec; +exports.PreUserRegistrationValidationErrorInputCodec = PreUserRegistrationValidationErrorInputCodec; +exports.helpers = helpers; +exports.validate = validate; diff --git a/src/_shared/Dygdkb3A.js b/src/_shared/Dygdkb3A.js new file mode 100644 index 0000000..b6544ba --- /dev/null +++ b/src/_shared/Dygdkb3A.js @@ -0,0 +1,29 @@ +'use strict'; + +/** + * Determines whether the given value is a non-null object and not an array. + * + * This function is useful for validating that a value is a plain object + * (e.g., `{}` or `{ key: value }`) and not `null`, an array, or any other type. + * + * @param value - The value to check. + * @returns `true` if the value is a non-null object and not an array, otherwise `false`. + */ +function isObject(value) { + return value != null && typeof value === "object" && Array.isArray(value) === false; +} + +/** + * Creates a no-op {@link TransactionMetadataAPI} for use in mock API implementations. + */ +function createNoopTransactionMetadataAPI() { + return { + setMetadata() { }, + getMetadata() { + return {}; + }, + }; +} + +exports.createNoopTransactionMetadataAPI = createNoopTransactionMetadataAPI; +exports.isObject = isObject; diff --git a/src/_shared/_XK5Fidc.js b/src/_shared/_XK5Fidc.js new file mode 100644 index 0000000..7b020b5 --- /dev/null +++ b/src/_shared/_XK5Fidc.js @@ -0,0 +1,660 @@ +'use strict'; + +var node_vm = require('node:vm'); +var async_hooks = require('async_hooks'); +var console = require('console'); +var stream = require('stream'); +var promises = require('node:fs/promises'); +var node_module = require('node:module'); + +/** + * @param obj + * @returns + * + * @license + * + * MIT License + * + * Copyright (c) 2020 Vadim @streamich Dalecky + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +const isArray = Array.isArray; +/** + * Create a deep copy of a plain-old JavaScript object. + * + * This function *SHOULD NOT* be used on anything more complex, such as `Map`, + * `Set` or any other class _instance_. + * + * @param obj object to clone + * @returns a copy of `obj` + */ +function deepClone(obj) { + if (!obj) + return obj; + if (isArray(obj)) { + const arr = []; + const length = obj.length; + for (let i = 0; i < length; i++) + arr.push(deepClone(obj[i])); + return arr; + } + else if (typeof obj === 'object') { + const keys = Object.keys(obj); + const length = keys.length; + const newObject = {}; + for (var i = 0; i < length; i++) { + const key = keys[i]; + newObject[key] = deepClone(obj[key]); + } + return newObject; + } + return obj; +} + +// The 256kb is also implemented in Actions +const LOG_LIMIT = 256 * 1024; +class LogBuffer { + chunks = []; + byteLength = 0; + maxLength; + destroyed = false; + constructor(maxLength) { + this.maxLength = maxLength; + } + destroy() { + this.destroyed = true; + this.chunks.length = 0; + this.byteLength = 0; + } + pushChunk(chunk) { + if (this.destroyed) { + return; + } + if (this.byteLength >= this.maxLength) { + return; + } + // Trim the chunk if necessary + if (this.byteLength + chunk.length > this.maxLength) { + const chunkSlice = Math.max(0, this.maxLength - this.byteLength); + chunk = chunk.subarray(0, chunkSlice); + } + this.byteLength += chunk.length; + this.chunks.push(chunk); + } + /** + * Get the accumulated logs for this async continuation and mark the instance + * as destroyed. + * + * @returns the accumulated logs as a string + */ + getLogs() { + const logs = Buffer.concat(this.chunks, this.byteLength).toString('utf-8'); + this.destroy(); + return logs; + } +} +class HostAPI { + static #als = new async_hooks.AsyncLocalStorage(); + runWithExecutionContext(store, callback, ...args) { + return HostAPI.#als.run(store, callback, ...args); + } + getExecutionContext() { + return HostAPI.#als.getStore(); + } +} +function isExecutionContext(store) { + return typeof store === 'object' && store !== null && 'logBuffer' in store; +} +function createTransform(getExecutionContext) { + return function transform(chunk, _encoding, callback) { + const store = getExecutionContext(); + if (isExecutionContext(store)) { + if (store.logBuffer) { + store.logBuffer.pushChunk(chunk); + } + } + callback(undefined, chunk); + }; +} +function createContextAwareConsole(stdout, stderr, getExecutionContext) { + const transform = createTransform(getExecutionContext); + const sinkStdout = new stream.Stream.Transform({ + transform, + }); + sinkStdout.on('data', forwardData(stdout)); + const sinkStdErr = new stream.Stream.Transform({ + transform, + }); + sinkStdErr.on('data', forwardData(stderr)); + return new console.Console(sinkStdout, sinkStdErr); +} +function forwardData(sink) { + return function write(chunk) { + if (!sink.destroyed && sink.writable) { + sink.write(chunk); + } + }; +} + +async function compileActionModule(compiler, script) { + return new Promise((resolve, reject) => { + // Wrap the Action in minimal boilerplate so that initialization can be deferred until + // we have a custom Console instance to inject. The wrapper allows us to capture the Action's + // handler function (e.g. onExecutePostLogin) from `module` and/or `exports`. + const wrappedScript = `module.exports = function(module, exports, console){\n${script}\n};`; + compiler(wrappedScript, (err, actionInitializerFn) => { + if (err) { + reject(err); + return; + } + resolve(actionInitializerFn); + }); + }); +} +class ActionsRunner { + #hostAPI; + #mod = { exports: {} }; + #modError = undefined; + #initialized = false; + #initializerFn; + #console; + constructor(hostAPI, initializerFn) { + this.#hostAPI = hostAPI; + this.#initializerFn = initializerFn; + // Create a Console that routes stdout/stderr into the active execution's LogBuffer. + // It uses AsyncLocalStorage to correctly attribute log output even when multiple + // Action executions are interleaved across async boundaries. + // + // The Console is created at the instance level so the same reference is reused across + // executions, keeping the Action cacheable without requiring an injected console argument. + this.#console = createContextAwareConsole(process.stdout, process.stderr, this.#hostAPI.getExecutionContext.bind(this.#hostAPI)); + } + getConsole() { + return this.#console; + } + async execute(fn, options) { + return await this.#hostAPI.runWithExecutionContext({ logBuffer: options.logBuffer }, async () => { + // Lazy load the Action on first execution so top-level console output is captured. + if (!this.#initialized) { + this.#initialized = true; + try { + if (!this.#initializerFn) { + throw new Error('Compiler did not return an initializer function'); + } + // Invoke the deferred initializer, supplying the captured module scope and the + // context-aware Console so the Action's exports and logs are both intercepted. + this.#initializerFn(this.#mod, this.#mod.exports, this.#console); + } + catch (err) { + // Synchronous error thrown during Action module initialization. + this.#modError = toError(err); + } + } + // When the script failed to initialize correctly, re-throw the same error on every + // subsequent call. Without this, a module that throws at the top level would fail + // on the first execution but succeed on all later ones. + if (this.#modError) { + throw this.#modError; + } + return await fn(this.#mod.exports); + }); + } +} +function toError(obj) { + if (!obj) { + return new Error('Unknown error'); + } + if (!(obj instanceof Error)) { + return new Error(String(obj)); + } + return obj; +} + +function nodeJsModuleCompiler(code, path, requireFn, options = {}) { + const { pseudoGlobals = {}, customConsole, parsingContext } = options; + const mod = { exports: {} }; + const namedArgs = { + module: mod, + exports: mod.exports, + require: requireFn, + ...pseudoGlobals, + }; + if (customConsole !== undefined) { + namedArgs.console = customConsole; + } + const fn = node_vm.compileFunction(code, Object.keys(namedArgs), { filename: path, parsingContext }); + fn(...Object.values(namedArgs)); + return mod.exports; +} +function actionsModuleCompiler(code, path, secrets, hostRequire, hostAPI, parsingContext) { + let wasCompiled = false; + let hadModError = false; + let mod; + let modErr; + return function loadActionsModule() { + if (wasCompiled) { + if (hadModError) { + throw modErr; + } + return mod; + } + wasCompiled = true; + const contextConsole = createContextAwareConsole(process.stdout, process.stderr, hostAPI.getExecutionContext.bind(hostAPI)); + try { + mod = nodeJsModuleCompiler(code, path, hostRequire, { + pseudoGlobals: { actions: { secrets } }, + customConsole: contextConsole, + parsingContext, + }); + } + catch (err) { + hadModError = true; + modErr = err; + throw err; + } + return mod; + }; +} + +const ACTIONS_MODULE_PREFIX = 'actions:'; +async function moduleLoader(filename, parsingContext) { + const code = await promises.readFile(filename, 'utf8'); + const actionsBaseRequire = node_module.createRequire(filename); + const hostAPI = new HostAPI(); + // Lazy loaders for registered `actions:` modules. The loader function itself is built + // eagerly at registration time, but each call only compiles the module and runs its + // top-level code the first time, so that init-time logs are captured under the active + // AsyncLocalStorage execution context rather than at registration time. Each loader + // caches its result (or thrown error), so repeated requires of the same module return + // the same exports object. + const modLoaderCache = new Map(); + // Mirrors actionsBaseRequire's resolve/cache/extensions/main so that customer code calling + // require.resolve(...) etc. on this merged require sees the same behavior it would from the + // plain node require, per the webtask-sandbox `makeRequireFunction` reference. + const actionRequireFn = (request) => { + // if we have already cached an action module loader for this request, invoke it + // and return its exports + const load = modLoaderCache.get(request); + if (load !== undefined) { + return load(); + } + // otherwise use the default node require + return actionsBaseRequire(request); + }; + actionRequireFn.resolve = actionsBaseRequire.resolve; + actionRequireFn.cache = actionsBaseRequire.cache; + actionRequireFn.extensions = actionsBaseRequire.extensions; + actionRequireFn.main = actionsBaseRequire.main; + const actionInitializerFn = await compileActionModule((script, cb) => { + try { + cb(null, nodeJsModuleCompiler(script, filename, actionRequireFn, { + parsingContext, + })); + } + catch (err) { + cb(err instanceof Error ? err : new Error(String(err)), undefined); + } + }, code); + return { + runner: new ActionsRunner(hostAPI, actionInitializerFn), + registerActionsModule: async (name, moduleFilename, secrets = {}) => { + const modKey = `${ACTIONS_MODULE_PREFIX}${name}`; + if (modLoaderCache.has(modKey)) { + throw new Error(`Module '${modKey}' is already registered`); + } + const modCode = await promises.readFile(moduleFilename, 'utf8'); + const modRequire = node_module.createRequire(moduleFilename); + const loadFn = actionsModuleCompiler(modCode, moduleFilename, secrets, modRequire, hostAPI, parsingContext); + modLoaderCache.set(modKey, loadFn); + }, + }; +} + +// When `createContext(globalThis)` is called by code already running inside a vm context, the +// object passed in is returned. In this case, `parsingContext === globalThis` would evaluate to +// true. In V8 10.1–10.8 / Node 18.0–19.9, a global that was mutated after the context was created +// (e.g. `jest.spyOn(global, 'fetch')`) is invisible to the newly compiled function, triggering a +// `ReferenceError`, even though getOwnPropertyDescriptor would confirm fetch was present. This only +// surfaces in nested-vm environments like Jest. To work around it, use a Proxy rather than a +// shallow copy of `target`: a copy would go stale once the globals change after this call, and +// would also drop non-enumerable properties that the Proxy forwards transparently. +function liveGlobal(target) { + const dynamicTarget = target; + return new Proxy(Object.create(null), { + get(_, key) { + return dynamicTarget[key]; + }, + set(_, key, value) { + dynamicTarget[key] = value; + return true; + }, + has(_, key) { + return key in dynamicTarget; + }, + deleteProperty(_, key) { + return delete dynamicTarget[key]; + }, + ownKeys() { + return Reflect.ownKeys(dynamicTarget); + }, + getOwnPropertyDescriptor(_, key) { + const desc = Object.getOwnPropertyDescriptor(dynamicTarget, key); + return desc && { ...desc, configurable: true }; + }, + defineProperty(_, key, desc) { + Object.defineProperty(dynamicTarget, key, desc); + return true; + }, + getPrototypeOf() { + return null; + }, + }); +} +const AFFECTED_NODE_MAJORS = new Set([18, 19]); +function buildParsingTarget(target) { + const nodeMajor = Number(process.versions.node.split('.')[0]); + return AFFECTED_NODE_MAJORS.has(nodeMajor) ? liveGlobal(target) : target; +} +function createLoader(getHandler) { + return async function loadAction(filename, modules = []) { + // vm.compileFunction() (called from nodeJsModuleCompiler) defaults to compiling/running + // against the process's root realm, not whichever realm this module itself happens to be + // running in. In the production runtime there's only one realm, but under Jest compiled + // actions run with the node process's original globals instead of the ones the test file + // is mocking. "Contextifying" the result of buildParsingTarget(globalThis) ensures code runs + // under the modified global values (e.g. jest.spyOn), whether that's globalThis itself or the + // live-forwarding Proxy standing in for it on affected Node versions (see buildParsingTarget above). + // + // When node 18 is no longer a supported runtime, buildParsingTarget can be removed. + const parsingContext = node_vm.createContext(buildParsingTarget(globalThis)); + const { runner, registerActionsModule } = await moduleLoader(filename, parsingContext); + for (const { name, filename: moduleFilename, secrets } of modules) { + await registerActionsModule(name, moduleFilename, secrets); + } + return { + async execute(entrypoint, ...args) { + const logBuffer = new LogBuffer(LOG_LIMIT); + await runner.execute((modExports) => { + const handler = getHandler(modExports, entrypoint); + return handler(...args); + }, { + logBuffer, + }); + // store logs right after execute() resolves, mirroring production. A promise that was + // not awaited could write to logBuffer after, racing with a lazy getLogs() call. + const logs = logBuffer.getLogs(); + return { getLogs: () => logs }; + }, + }; + }; +} + +// The default ttl of a cache record is 15 minutes. This should be sent by Actions in the future. +const DEFAULT_CACHE_TTL = 15 * 60 * 1000; +const MAX_KEY_BYTES = 64; +const MAX_VALUE_BYTES = 4096; +// Adding a little bit of padding for the serialization overhead and at least 2 keys of 64 bytes + 4kb of data +const MAX_TOTAL_CACHE_BYTES = (16 * 1024) + 256; + +// This validator currently uses hard-coded values. We will want to have Actions send these values over in the future. +function canSetNewCacheRecord(cache, key, value) { + const keySize = Buffer.byteLength(key, 'utf-8'); + const valueSize = Buffer.byteLength(value, 'utf-8'); + // Cache keys are limited to 64 bytes + if (keySize > MAX_KEY_BYTES) { + return { + type: 'error', + code: 'CacheKeySizeExceeded', + }; + } + // Cache values are limited to 4096 bytes + if (valueSize > MAX_VALUE_BYTES) { + return { + type: 'error', + code: 'CacheValueSizeExceeded', + }; + } + // Total cache size must not exceed 8,192 bytes + const cacheSize = getCacheSize(cache); + if (cacheSize + keySize + valueSize > MAX_TOTAL_CACHE_BYTES) { + return { + type: 'error', + code: 'CacheSizeExceeded', + }; + } + return { + type: 'success', + }; +} +function getCacheSize(cache) { + let allCharacters = ''; + for (const key of cache.keys()) { + allCharacters += key; + } + for (const value of cache.values()) { + allCharacters += value.value; + } + return Buffer.byteLength(allCharacters, 'utf-8'); +} + +/** + * A {@link CacheWriterAPI} that does not persisting mutations beyond the lifetime + * of a single tested action. Backs stub trigger APIs so `api.cache` behaves + * like a real, working cache (reads reflect prior writes). + */ +class NoopCacheWriterAPI { + setCacheValue(key, value, expiresAt) { + return { type: 'success', record: { value, expires_at: expiresAt } }; + } + deleteCacheValue(_key) { + return { type: 'success' }; + } +} +/** + * Creates a {@link CacheAPI} backed by {@link NoopCacheWriterAPI} for use in + * stub trigger API implementations. + */ +function createNoopCacheAPI() { + return new CacheAPIImpl(new NoopCacheWriterAPI(), new Map()); +} +class CacheAPIImpl { + #cacheWriter; + #cacheData; + constructor(cacheWriter, cacheData) { + this.#cacheWriter = cacheWriter; + this.#cacheData = cacheData; + } + delete(key) { + const cache = this.#cacheData; + const cacheEntry = this.#cacheData.get(key); + // We want to make sure we are able to both delete the value and persist the result + if (!cacheEntry) { + return { + type: 'error', + code: 'CacheKeyDoesNotExist', + }; + } + // The key exists so it's safe to persist the result + const writeResult = this.#cacheWriter.deleteCacheValue(key); + if (writeResult.type === 'error') { + return writeResult; + } + // Now that the command was registered we can delete it locally + const deleted = cache.delete(key); + if (!deleted) { + return { + type: 'error', + code: 'FailedToDeleteCacheRecord', + }; + } + return { + type: 'success', + }; + } + get(key) { + const record = this.#cacheData.get(key); + if (!record) { + return; + } + if (record.expires_at < Date.now()) { + return; + } + return record; + } + set(key, value, options) { + const cache = this.#cacheData; + // These validate that the total cache size limit is within limits + const cacheSizeValidation = canSetNewCacheRecord(cache, key, value); + if (cacheSizeValidation.type === 'error') { + return cacheSizeValidation; + } + const now = Date.now(); + let expiresAt = undefined; + const suppliedTTL = options?.ttl; + if (typeof suppliedTTL !== 'undefined') { + if (isFiniteNumber(suppliedTTL)) { + expiresAt = now + Math.floor(suppliedTTL); + } + else { + return { + type: 'error', + code: 'InvalidExpiry', + }; + } + } + const suppliedExpiresAt = options?.expires_at; + if (typeof suppliedExpiresAt !== 'undefined') { + if (isFiniteNumber(suppliedExpiresAt)) { + // Let's further check if an explicit `expires_at` was supplied. If so + // it must be earlier than the expiry implied by a `ttl` option. + if (isFiniteNumber(expiresAt)) { + expiresAt = Math.min(expiresAt, Math.floor(suppliedExpiresAt)); + } + else { + expiresAt = Math.floor(suppliedExpiresAt); + } + } + else { + return { + type: 'error', + code: 'InvalidExpiry', + }; + } + } + if (!isFiniteNumber(expiresAt)) { + // No expiry hints provided so we use the default + expiresAt = now + DEFAULT_CACHE_TTL; + } + if (expiresAt <= now) { + return { + type: 'error', + code: 'ItemAlreadyExpired', + }; + } + if (!Number.isSafeInteger(expiresAt)) { + return { + type: 'error', + code: 'InvalidExpiry', + }; + } + // Add the command first to make sure the cache is not modified if we fail to persist the command + const writeResult = this.#cacheWriter.setCacheValue(key, value, expiresAt); + // Only check error case since the local cache still need updating + if (writeResult.type === 'error') { + return writeResult; + } + cache.set(key, { value, expires_at: expiresAt }); + const record = cache.get(key); + if (!record) { + return { + type: 'error', + code: 'FailedToSetCacheRecord', + }; + } + return { + type: 'success', + record, + }; + } +} +function isFiniteNumber(n) { + return typeof n === 'number' && Number.isFinite(n); +} + +/** + * isEntrypoint checks if the entrypoint is a string and is one of the valid entrypoints. + * @param entrypoints List of valid entrypoints + * @param entrypoint The entrypoint to check, this is an unknown value. + * @returns {boolean} + */ +function isEntrypoint(entrypoints, entrypoint) { + return typeof entrypoint === 'string' && entrypoints.includes(entrypoint); +} +/** + * isExport checks if the exports object is a valid object. + * @param exports + * @returns {boolean} + */ +function isExport(exports) { + return typeof exports === 'object' && exports !== null; +} +/** + * isAction validates that the function is an async function. + * We can't assume that there will be 2 arguments, because customers can omit those + * and it would still be a valid function. + * + * @param fn + * @returns + */ +function isAction(fn) { + return (!!fn && typeof fn === 'function' && fn.constructor && fn.constructor.name === 'AsyncFunction'); +} +/** + * Create an event handler for a particular set of entrypoints. + * @param entrypoints + * @returns + */ +function getHandlerFactory(entrypoints) { + if (entrypoints.length === 0) { + throw new Error(`The compiler must support at least one entrypoint.`); + } + return function (exports, entrypoint) { + if (!isExport(exports)) { + throw new Error('The Action must export an object.'); + } + let exportName = entrypoints[0]; + if (entrypoint) { + if (!isEntrypoint(entrypoints, entrypoint)) { + throw new Error(`The Action can only be called with valid methods.`); + } + exportName = entrypoint; + } + const fn = exports[exportName]; + if (!isAction(fn)) { + throw new Error(`Invalid function signature for the ${JSON.stringify(exportName)} handler. Use "exports.${exportName} = async function(event, api) { }" as the signature.`); + } + return fn; + }; +} + +exports.createLoader = createLoader; +exports.createNoopCacheAPI = createNoopCacheAPI; +exports.deepClone = deepClone; +exports.getHandlerFactory = getHandlerFactory; diff --git a/src/_shared/jH7s8Jy4.d.ts b/src/_shared/jH7s8Jy4.d.ts new file mode 100644 index 0000000..4987120 --- /dev/null +++ b/src/_shared/jH7s8Jy4.d.ts @@ -0,0 +1,2 @@ +type TxMetadataValue = string | boolean | number; +export type { TxMetadataValue as T }; diff --git a/src/credentials-exchange/v1/index.d.ts b/src/credentials-exchange/v1/index.d.ts index f6d99bf..0b1a3f2 100644 --- a/src/credentials-exchange/v1/index.d.ts +++ b/src/credentials-exchange/v1/index.d.ts @@ -1,9 +1,8 @@ -/** CredentialsExchangeV1Event */ -type CredentialsExchangeV1Event = { +interface Event { actor: { /** The body of the POST request. */ body?: { - [additionalProperties: string]: any; + [key: string]: any; }; geoIp?: { city_name?: string; @@ -16,8 +15,7 @@ type CredentialsExchangeV1Event = { subdivision_code?: string; subdivision_name?: string; time_zone?: string; - } & { - [additionalProperties: string]: any; + [key: string]: any; }; /** The hostname that is being used for the authentication flow. */ hostname?: string; @@ -35,7 +33,7 @@ type CredentialsExchangeV1Event = { id: string; /** An object for holding other application properties. */ metadata: { - [additionalProperties: string]: string; + [key: string]: string; }; /** The name of the application (as defined in the Dashboard). */ name: string; @@ -46,30 +44,26 @@ type CredentialsExchangeV1Event = { reason: 'invalid_scope' | 'invalid_request' | 'server_error'; }; customClaims: { - [additionalProperties: string]: any; + [key: string]: any; }; scope: string[]; tenant: { /** The name of the tenant. */ id: string; }; -} & { - [additionalProperties: string]: any; -}; -/** CredentialsExchangeV1Result */ -type CredentialsExchangeV1Result = { + [key: string]: any; +} +interface Result { command?: { type: 'deny'; message: string; reason: 'invalid_scope' | 'invalid_request' | 'server_error'; }; customClaims?: { - [additionalProperties: string]: any; + [key: string]: any; }; scope?: string[]; -}; -interface Event extends CredentialsExchangeV1Event {} -interface Result extends CredentialsExchangeV1Result {} +} interface Secrets { [secretName: string]: string; } diff --git a/src/credentials-exchange/v2/index.d.ts b/src/credentials-exchange/v2/index.d.ts index aabd217..30232d3 100644 --- a/src/credentials-exchange/v2/index.d.ts +++ b/src/credentials-exchange/v2/index.d.ts @@ -95,12 +95,16 @@ interface CacheAPI { */ set(key: string, value: string, options?: CacheSetOptions): CacheWriteResult; } -/** CredentialsExchangeV2Event */ -type CredentialsExchangeV2Event = { +type AccessDeniedErrorCode = 'invalid_scope' | 'invalid_request' | 'server_error'; +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event { /** Information about the access token to be issued. */ accessToken: { customClaims: { - [additionalProperties: string]: any; + [key: string]: any; }; scope: string[]; }; @@ -110,7 +114,7 @@ type CredentialsExchangeV2Event = { agent_id: string; /** [Early Access] Free-form key-value metadata associated with the agent. Always defined when `agent` is present; an empty object when the agent has no metadata. */ agent_metadata: { - [additionalProperties: string]: any; + [key: string]: any; }; /** [Early Access] The human-readable name of the agent. */ name: string; @@ -121,7 +125,7 @@ type CredentialsExchangeV2Event = { client_id: string; /** An object for holding other application properties. */ metadata: { - [additionalProperties: string]: string; + [key: string]: string; }; /** The name of the application (as defined in the Dashboard). */ name: string; @@ -132,7 +136,7 @@ type CredentialsExchangeV2Event = { domain: string; /** Custom domain metadata as key-value pairs. */ domain_metadata: { - [additionalProperties: string]: string; + [key: string]: string; }; }; /** Details about the Organization associated with the current transaction. */ @@ -143,18 +147,17 @@ type CredentialsExchangeV2Event = { display_name: string; /** Metadata associated with the Organization. */ metadata: { - [additionalProperties: string]: string; + [key: string]: string; }; /** The name of the Organization. */ name: string; - } & { - [additionalProperties: string]: any; + [key: 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; + [key: string]: any; }; geoip: { cityName?: string; @@ -167,8 +170,7 @@ type CredentialsExchangeV2Event = { subdivisionCode?: string; subdivisionName?: string; timeZone?: string; - } & { - [additionalProperties: string]: any; + [key: string]: any; }; /** The hostname that is being used for the authentication flow. */ hostname?: string; @@ -200,13 +202,6 @@ type CredentialsExchangeV2Event = { /** [Early Access] The live target scope set for the access token. Initialized from the client grants and immediately updated by api.transaction target scope methods across current and subsequent Actions. After all Actions complete, these scopes are intersected with the client grant. Scopes not present in the grant are silently dropped from the final access token. */ target_scopes?: string[]; }; -}; -type AccessDeniedErrorCode = 'invalid_scope' | 'invalid_request' | 'server_error'; -interface Configuration {} -interface Secrets { - [secretName: string]: string; -} -interface Event extends CredentialsExchangeV2Event { /** * @private Configuration values associated with this Action. */ diff --git a/src/credentials-exchange/v2/test/index.d.ts b/src/credentials-exchange/v2/test/index.d.ts new file mode 100644 index 0000000..e6df9d5 --- /dev/null +++ b/src/credentials-exchange/v2/test/index.d.ts @@ -0,0 +1,239 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +import { A as AccessDeniedErrorCode } from '../../../_shared/C_R4QU65.js'; +import '../../../_shared/CUlF8oaW.js'; +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event { + /** Information about the access token to be issued. */ + accessToken: { + customClaims: { + [key: string]: any; + }; + scope: string[]; + }; + /** [Early Access] Information about the agent acting in this flow. Set when the client requesting the token is linked to an agent and the tenant has agents as principals enabled; `undefined` otherwise. */ + agent?: { + /** [Early Access] The stable identifier for the agent, prefixed with `agt_` (for example, `agt_2hVk6JxPxbRgNZDKfJQqmn`). */ + agent_id: string; + /** [Early Access] Free-form key-value metadata associated with the agent. Always defined when `agent` is present; an empty object when the agent has no metadata. */ + agent_metadata: { + [key: string]: any; + }; + /** [Early Access] The human-readable name of the agent. */ + name: string; + }; + /** Information about the Client used during this token exchange. */ + client: { + /** The client id of the application the user is logging in to. */ + client_id: string; + /** An object for holding other application properties. */ + metadata: { + [key: string]: string; + }; + /** The name of the application (as defined in the Dashboard). */ + name: string; + }; + /** Details about the custom domain associated with the current transaction. */ + custom_domain?: { + /** The custom domain name. */ + domain: string; + /** Custom domain metadata as key-value pairs. */ + domain_metadata: { + [key: string]: 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: { + [key: string]: string; + }; + /** The name of the Organization. */ + name: string; + [key: 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: { + [key: string]: any; + }; + geoip: { + cityName?: string; + continentCode?: string; + countryCode?: string; + countryCode3?: string; + countryName?: string; + latitude?: number; + longitude?: number; + subdivisionCode?: string; + subdivisionName?: string; + timeZone?: string; + [key: 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; + }; + /** Information about the Resource Server that is issuing the access token. */ + resource_server: { + /** The identifier of the resource server. For example: `https://your-api.example.com`. */ + identifier: string; + }; + /** Information about the Tenant used during this token exchange. */ + tenant: { + /** The name of the tenant. */ + id: string; + }; + /** Information about the Credentials Exchange transaction. */ + transaction: { + /** Correlation ID can be provided in the initial authentication request when the application redirects to Universal Login. You can use value to correlate logs and requests from your Action code with the user flow. */ + correlation_id?: string; + /** The scopes specified (if any) when requesting the access token. */ + requested_scopes: string[]; + /** [Early Access] The live target scope set for the access token. Initialized from the client grants and immediately updated by api.transaction target scope methods across current and subsequent Actions. After all Actions complete, these scopes are intersected with the client grant. Scopes not present in the grant are silently dropped from the final access token. */ + target_scopes?: string[]; + }; + /** + * @private Configuration values associated with this Action. + */ + configuration: Configuration; + /** + * Secret values securely associated with this Action. + */ + secrets: Secrets; +} +interface TransactionAPI { + /** + * [Early Access] Add a scope to the target scope set. Added scopes are intersected with the + * client grant after all Actions complete. Scopes not present in the grant + * are silently dropped from the final access token. + * + * @param scope The scope to add. + * @throws Will throw an error if the scope is invalid. + * + * @example + * ```js + * exports.onExecuteCredentialsExchange = async (event, api) => { + * api.transaction.addTargetScope('read:reports'); + * }; + * ``` + */ + addTargetScope(scope: string): void; + /** + * [Early Access] Remove a scope from the target scope set. + * + * @param scope The scope to remove. + * @throws Will throw an error if the scope is invalid. + * + * @example + * ```js + * exports.onExecuteCredentialsExchange = async (event, api) => { + * api.transaction.removeTargetScope('admin:full'); + * }; + * ``` + */ + removeTargetScope(scope: string): void; + /** + * [Early Access] Replace the entire target scope set. The new scopes are intersected with + * the client grant after all Actions complete. Scopes not present in the + * grant are silently dropped from the final access token. + * + * @param scopes The new target scope set. + * @throws Will throw an error if any scope is invalid. + * + * @example + * ```js + * exports.onExecuteCredentialsExchange = async (event, api) => { + * api.transaction.setTargetScopes(['read:users', 'write:users']); + * }; + * ``` + */ + setTargetScopes(scopes: string[]): void; + /** + * [Early Access] Remove all scopes from the target scope set. + * + * @example + * ```js + * exports.onExecuteCredentialsExchange = async (event, api) => { + * api.transaction.clearTargetScopes(); + * }; + * ``` + */ + clearTargetScopes(): void; +} +interface AccessAPI { + /** + * Mark the current token exchange as denied. + * + * @param code The protocol-specific error code justifying the rejection of the login. + * @param reason A human-readable explanation for rejecting the access token grant. + */ + deny(code: AccessDeniedErrorCode, reason?: string): CredentialsExchangeAPI; +} +interface AccessTokenAPI { + /** + * Set a custom claim on the Access Token that will be issued. + * + * @param key Name of the claim (note that this may need to be a fully-qualified url). + * @param value The value of the claim. + */ + setCustomClaim(key: string, value: unknown): CredentialsExchangeAPI; +} +/** + * Methods and utilities to help change the behavior of the Client Credentials Exchange grant. + */ +interface CredentialsExchangeAPI { + /** + * Control availability to the access token. + */ + readonly access: AccessAPI; + /** + * Request changes to the access token being issued. + */ + readonly accessToken: AccessTokenAPI; + /** + * Make changes to the cache. + */ + readonly cache: CacheAPI; + /** + * [Early Access] Make changes to the transaction. + */ + readonly transaction: TransactionAPI; +} +interface CredentialsExchangeAction { + (event: Event, api: CredentialsExchangeAPI): Promise; +} +type CredentialsExchangeModule = { + onExecuteCredentialsExchange: CredentialsExchangeAction; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link CredentialsExchangeTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters< + CredentialsExchangeModule[keyof CredentialsExchangeModule] +>; +/** Loads a CredentialsExchange v2 action file for use in tests, e.g. `action.execute('onExecuteCredentialsExchange', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/credentials-exchange/v2/test/index.js b/src/credentials-exchange/v2/test/index.js new file mode 100644 index 0000000..868c5cc --- /dev/null +++ b/src/credentials-exchange/v2/test/index.js @@ -0,0 +1,198 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +var targetScopes = require('../../../_shared/Bi7NRjgy.js'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); +require('../../../_shared/DvTaCl9e.js'); + +/** + * No-op {@link CredentialsExchangeTriggerAPI} for use in mock API implementations. + */ +class CredentialsExchangeTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + #targetScopesAPI; + constructor(initialTargetScopes = []) { + this.#targetScopesAPI = targetScopes.createNoopTargetScopesAPI(initialTargetScopes); + } + getCacheAPI() { + return this.#cacheAPI; + } + getTargetScopesAPI() { + return this.#targetScopesAPI; + } + denyAccess(_code, _reason) { } + setCustomClaim(_key, _value) { } +} + +const event = { + accessToken: { + scope: ['read:entity'], + customClaims: {}, + }, + agent: { + agent_id: 'agt_abc123', + name: 'My Agent', + agent_metadata: { + env: 'production', + }, + }, + transaction: { + requested_scopes: ['read:entity'], + }, + resource_server: { + identifier: '{{TENANT}}.auth0.com/api/v2', + }, + tenant: { + id: '{{TENANT}}', + }, + configuration: {}, + secrets: {}, + client: { + authentication: { + type: 'self_signed_tls_client_auth', + certificate: { + raw: '-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----', + subject: 'CN=client.example.com', + subjectAltName: 'DNS:client.example.com, IP:127.0.0.1', + thumbprint256: 'qrvM3e7_ABEiM0RVZneImaq7zN3u_wARIjNEVWZ3iJk', + }, + }, + client_id: 'client-id', + name: 'A Client Application', + metadata: {}, + }, + custom_domain: { + domain: 'login.{{YOUR_DOMAIN}}.com', + domain_metadata: { + environment: 'production', + region: 'us-west-2', + }, + }, + request: { + ip: '10.12.13.1', + method: 'POST', + body: { + client_id: 'client-id', + client_secret: 'client-secret', + audience: '{{TENANT}}.auth0.com/api/v2', + grant_type: 'client_credentials', + }, + geoip: { + cityName: 'Bellevue', + continentCode: 'NA', + countryCode3: 'USA', + countryCode: 'US', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + hostname: '{{TENANT}}.auth0.com', + language: 'en', + user_agent: 'curl/7.64.1', + }, + organization: { + display_name: 'My Organization', + id: 'org_juG7cAQ0CymOcVpV', + metadata: { + key: 'value', + }, + name: 'my-organization', + }, +}; + +class TransactionAPIImpl { + #targetScopesAPI; + #eventTransaction; + constructor(targetScopesAPI, eventTransaction) { + this.#targetScopesAPI = targetScopesAPI; + this.#eventTransaction = eventTransaction; + } + addTargetScope(scope) { + this.#targetScopesAPI.addTargetScope(scope); + this.#syncEventTargetScopes(); + } + removeTargetScope(scope) { + this.#targetScopesAPI.removeTargetScope(scope); + this.#syncEventTargetScopes(); + } + setTargetScopes(scopes) { + this.#targetScopesAPI.setTargetScopes(scopes); + this.#syncEventTargetScopes(); + } + clearTargetScopes() { + this.#targetScopesAPI.clearTargetScopes(); + this.#syncEventTargetScopes(); + } + #syncEventTargetScopes() { + this.#eventTransaction.target_scopes = this.#targetScopesAPI.getTargetScopes(); + } +} + +class AccessAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + deny(code, reason) { + this.#triggerAPI.denyAccess(code, reason); + return this.#api; + } +} +class AccessTokenAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + setCustomClaim(key, value) { + this.#triggerAPI.setCustomClaim(key, value); + return this.#api; + } +} +class CredentialsExchangeAPIImpl { + access; + accessToken; + cache; + transaction; + constructor(triggerAPI, eventTransaction) { + this.access = new AccessAPIImpl(triggerAPI, this); + this.accessToken = new AccessTokenAPIImpl(triggerAPI, this); + this.cache = triggerAPI.getCacheAPI(); + this.transaction = new TransactionAPIImpl(triggerAPI.getTargetScopesAPI(), eventTransaction); + } +} + +function contextToArguments({ event, triggerAPI, }) { + return [event, new CredentialsExchangeAPIImpl(triggerAPI, event.transaction)]; +} +const getHandler = handler.getHandlerFactory([ + 'onExecuteCredentialsExchange', +]); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link CredentialsExchangeTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +function getDefaultArguments() { + const event$1 = handler.deepClone(event); + return contextToArguments({ + event: event$1, + triggerAPI: new CredentialsExchangeTriggerAPIStubImpl([...(event$1.accessToken?.scope ?? [])]), + }); +} +/** Loads a CredentialsExchange v2 action file for use in tests, e.g. `action.execute('onExecuteCredentialsExchange', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction; diff --git a/src/custom-email-provider/v1/index.d.ts b/src/custom-email-provider/v1/index.d.ts index f4e9210..24f77fc 100644 --- a/src/custom-email-provider/v1/index.d.ts +++ b/src/custom-email-provider/v1/index.d.ts @@ -126,15 +126,18 @@ interface CustomEmailProviderAPI { */ readonly notification: NotificationsAPI; } -/** CustomEmailProviderV1Event */ -type CustomEmailProviderV1Event = { +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event { /** Information about the Client with which this login 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; + [key: string]: string; }; /** The name of the application (as defined in the Dashboard). */ name: string; @@ -145,7 +148,7 @@ type CustomEmailProviderV1Event = { id: string; /** Metadata associated with the connection. */ metadata?: { - [additionalProperties: string]: string; + [key: string]: string; }; /** The name of the connection used to authenticate the user (such as `twitter` or `some-g-suite-domain`). */ name: string; @@ -158,7 +161,7 @@ type CustomEmailProviderV1Event = { domain: string; /** Custom domain metadata as key-value pairs. */ domain_metadata: { - [additionalProperties: string]: string; + [key: string]: string; }; }; notification: { @@ -197,12 +200,12 @@ type CustomEmailProviderV1Event = { display_name: string; /** Metadata associated with the Organization. */ metadata: { - [additionalProperties: string]: string; + [key: string]: string; }; /** The name of the Organization. */ name: string; } & { - [additionalProperties: string]: any; + [key: string]: any; }; request: { geoip?: { @@ -217,7 +220,7 @@ type CustomEmailProviderV1Event = { subdivisionName?: string; timeZone?: string; } & { - [additionalProperties: string]: any; + [key: string]: any; }; /** The hostname that is being used for the authentication flow. */ hostname?: string; @@ -225,7 +228,7 @@ type CustomEmailProviderV1Event = { ip?: string; /** The query string parameters sent to the authorization request. */ query?: { - [additionalProperties: string]: string; + [key: string]: string; }; /** The value of the `User-Agent` header received when initiating the transaction. */ user_agent?: string; @@ -253,7 +256,7 @@ type CustomEmailProviderV1Event = { 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; + [key: string]: any; }; /** (unique) User's email address. */ email?: string; @@ -273,19 +276,13 @@ type CustomEmailProviderV1Event = { 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; + [key: string]: any; }; /** (unique) User's username. */ username?: string; } & { - [additionalProperties: string]: any; + [key: string]: any; }; -}; -interface Configuration {} -interface Secrets { - [secretName: string]: string; -} -interface Event extends CustomEmailProviderV1Event { /** * @private Configuration values associated with this Action. */ diff --git a/src/custom-email-provider/v1/test/index.d.ts b/src/custom-email-provider/v1/test/index.d.ts new file mode 100644 index 0000000..b758aeb --- /dev/null +++ b/src/custom-email-provider/v1/test/index.d.ts @@ -0,0 +1,221 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +interface NotificationsAPI { + /** + * When called, the notification event is considered failed without recovery: + * We will log an error for this event, but won't be sending it again to the action in the future. + * If you need this notification event to be retried, consider calling retry instead. + * @param reason this reason will be part of the log entry, this will help you analyze the error further. Please note that this field is limited to 1024 characters and will be truncated if larger. + */ + drop(reason: string): void; + /** + * When called, the notification event is considered failed, but recoverable: + * We will log an error for this event, but we will retry it up to 5 times in the next minutes. + * If you consider that this notification event should not be retried, consider calling drop instead. + * @param reason this reason will be part of the log entry, this will help you analyze the error further. Please note that this field is limited to 1024 characters and will be truncated if larger. + */ + retry(reason: string): void; +} +/** + * Methods and utilities to inform whether or not the event message should be treated as an error or not. + */ +interface CustomEmailProviderAPI { + /** + * Make changes to the cache. + */ + readonly cache: CacheAPI; + /** + * Informs if we should consider the notification event as to be retried or to be dropped. + * See each of these methods for further details on the actual behaviour. + * If several calls are made, only the last one is considered. + */ + readonly notification: NotificationsAPI; +} +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event { + /** Information about the Client with which this login 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: { + [key: string]: string; + }; + /** The name of the application (as defined in the Dashboard). */ + name: string; + }; + /** Details about the Connection that was used to authenticate the user. */ + connection?: { + /** The connection's unique identifier. */ + id: string; + /** Metadata associated with the connection. */ + metadata?: { + [key: string]: string; + }; + /** The name of the connection used to authenticate the user (such as `twitter` or `some-g-suite-domain`). */ + name: string; + /** The type of connection. For social connections, `event.connection.strategy === event.connection.name`. For enterprise connections, the strategy is `waad` (Windows Azure AD), `ad` (Active Directory/LDAP), `auth0` (database connections), and so on. */ + strategy: string; + }; + /** Details about the custom domain associated with the current transaction. */ + custom_domain?: { + /** The custom domain name. */ + domain: string; + /** Custom domain metadata as key-value pairs. */ + domain_metadata: { + [key: string]: string; + }; + }; + notification: { + /** Email address of the sender for the email. */ + from: string; + /** Rendered HTML template. */ + html: string; + /** The locale we rendered the message in, example `en_US`, as defined in the BCP-47 specification. */ + locale: string; + /** The type of message that is being send, like `verify_email` or `welcome_email`. */ + message_type: + | 'verify_email' + | 'verify_email_by_code' + | 'reset_email' + | 'reset_email_by_code' + | 'welcome_email' + | 'verification_code' + | 'mfa_oob_code' + | 'enrollment_email' + | 'blocked_account' + | 'stolen_credentials' + | 'try_provider_configuration_email' + | 'organization_invitation'; + /** Subject to be attached to the email. */ + subject: string; + /** Rendered text template. */ + text: string; + /** Email address of the recipient. */ + to: 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: { + [key: string]: string; + }; + /** The name of the Organization. */ + name: string; + } & { + [key: string]: any; + }; + request: { + geoip?: { + cityName?: string; + continentCode?: string; + countryCode?: string; + countryCode3?: string; + countryName?: string; + latitude?: number; + longitude?: number; + subdivisionCode?: string; + subdivisionName?: string; + timeZone?: string; + } & { + [key: string]: any; + }; + /** The hostname that is being used for the authentication flow. */ + hostname?: string; + /** The originating IP address of the request. */ + ip?: string; + /** The query string parameters sent to the authorization request. */ + query?: { + [key: string]: string; + }; + /** The value of the `User-Agent` header received when initiating the transaction. */ + user_agent?: string; + }; + tenant: { + /** The name of the tenant. */ + id: string; + /** The friendly name for the tenant, usually a more human-readable version of the ID. */ + friendly_name?: string; + /** The home URL for the tenant, if defined and as found in its settings. */ + home_url?: string; + /** The logo URL for the tenant, if defined and as found in its settings. */ + logo_url?: string; + /** The email to the tenant's support service, if defined and as found in its settings. */ + support_email?: string; + /** The url to the tenant's support service, if defined and as found in its settings. */ + support_url?: string; + }; + /** Details about the current transaction for tracing purposes. */ + transaction?: { + /** A unique identifier to correlate this request across multiple services for distributed tracing. */ + correlation_id?: string; + }; + /** An object describing the user on whose behalf the current transaction was initiated. */ + 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: { + [key: string]: any; + }; + /** (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; + /** User's full name. */ + name?: string; + /** User's nickname. */ + nickname?: string; + /** URL pointing to the [user's profile picture](https://auth0.com/docs/users/change-user-picture). */ + picture?: 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: { + [key: string]: any; + }; + /** (unique) User's username. */ + username?: string; + } & { + [key: string]: any; + }; + /** + * @private Configuration values associated with this Action. + */ + configuration: Configuration; + /** + * Secret values securely associated with this Action. + */ + secrets: Secrets; +} +interface CustomEmailProviderAction { + (event: Event, api: CustomEmailProviderAPI): Promise; +} +type CustomEmailProviderModule = { + onExecuteCustomEmailProvider: CustomEmailProviderAction; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link CustomEmailProviderTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters< + CustomEmailProviderModule[keyof CustomEmailProviderModule] +>; +/** Loads a CustomEmailProvider v1 action file for use in tests, e.g. `action.execute('onExecuteCustomEmailProvider', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/custom-email-provider/v1/test/index.js b/src/custom-email-provider/v1/test/index.js new file mode 100644 index 0000000..24f3557 --- /dev/null +++ b/src/custom-email-provider/v1/test/index.js @@ -0,0 +1,150 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); + +/** + * No-op {@link CustomEmailProviderTriggerAPI} for use in mock API implementations. + */ +class CustomEmailProviderTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + getCacheAPI() { + return this.#cacheAPI; + } + drop(_reason) { } + retry(_reason) { } +} + +const event = { + client: { + name: 'All Applications', + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + external_client_id: 'https://example.com/oauth-client.json', + metadata: {}, + external_metadata_type: 'cimd', + }, + notification: { + message_type: 'verify_email', + to: 'email@email.com', + locale: 'en-US', + html: '

This is the HTML you should send

', + text: 'This is the text you should send', + subject: 'Email sent from Custom Email Provider', + from: 'example@example.com', + }, + request: { + geoip: { + cityName: 'Bellevue', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + continentCode: 'NA', + countryCode: 'US', + countryCode3: 'USA', + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + ip: '1.2.3.4', + user_agent: 'browser', + query: { + 'ext-test': 'test', + }, + hostname: '{{TENANT}}.auth0.com', + }, + custom_domain: { + domain: 'login.{{YOUR_DOMAIN}}.com', + domain_metadata: { + environment: 'production', + region: 'us-west-2', + }, + }, + tenant: { + id: '{{TENANT}}', + friendly_name: 'Friendly {{TENANT}}', + home_url: 'https://example.com/home', + logo_url: 'https://www.gravatar.com/avatar/?d=monsterid', + support_email: 'email@example.com', + support_url: 'https://example.com/support', + }, + transaction: { + correlation_id: 'abcefg123', + }, + user: { + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + username: 'John Smith', + name: 'Johny Smith', + given_name: 'John', + family_name: 'Smith', + nickname: 'Johnny', + email: 'j+smith@example.com', + email_verified: true, + picture: 'https://www.gravatar.com/avatar/?d=identicon', + user_metadata: {}, + app_metadata: {}, + profile_id: 'profile_id', + }, + organization: { + display_name: 'My Org', + id: 'my-org', + metadata: {}, + name: 'MyOrg', + }, + connection: { + id: 'con_example', + name: 'auth0', + strategy: 'auth0', + metadata: {}, + }, + configuration: {}, + secrets: {}, +}; + +class NotificationsAPIImpl { + #triggerAPI; + constructor(triggerAPI) { + this.#triggerAPI = triggerAPI; + } + drop(reason) { + this.#triggerAPI.drop(reason); + } + retry(reason) { + this.#triggerAPI.retry(reason); + } +} +class CustomEmailProviderAPIImpl { + cache; + notification; + constructor(triggerAPI) { + this.cache = triggerAPI.getCacheAPI(); + this.notification = new NotificationsAPIImpl(triggerAPI); + } +} + +function contextToArguments(ctx) { + return [ctx.event, new CustomEmailProviderAPIImpl(ctx.triggerAPI)]; +} +const getHandler = handler.getHandlerFactory([ + 'onExecuteCustomEmailProvider', +]); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link CustomEmailProviderTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +function getDefaultArguments() { + return contextToArguments({ + event: handler.deepClone(event), + triggerAPI: new CustomEmailProviderTriggerAPIStubImpl(), + }); +} +/** Loads a CustomEmailProvider v1 action file for use in tests, e.g. `action.execute('onExecuteCustomEmailProvider', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction; diff --git a/src/custom-phone-provider/v1/index.d.ts b/src/custom-phone-provider/v1/index.d.ts index 261ee37..abbd0c1 100644 --- a/src/custom-phone-provider/v1/index.d.ts +++ b/src/custom-phone-provider/v1/index.d.ts @@ -126,15 +126,18 @@ interface CustomPhoneProviderAPI { */ readonly notification: NotificationsAPI; } -/** CustomPhoneProviderV1Event */ -type CustomPhoneProviderV1Event = { +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event { /** Information about the Client with which this login 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; + [key: string]: string; }; /** The name of the application (as defined in the Dashboard). */ name: string; @@ -145,7 +148,7 @@ type CustomPhoneProviderV1Event = { id: string; /** Metadata associated with the connection. */ metadata?: { - [additionalProperties: string]: string; + [key: string]: string; }; /** The name of the connection used to authenticate the user (such as `twitter` or `some-g-suite-domain`). */ name: string; @@ -158,7 +161,7 @@ type CustomPhoneProviderV1Event = { domain: string; /** Custom domain metadata as key-value pairs. */ domain_metadata: { - [additionalProperties: string]: string; + [key: string]: string; }; }; notification: { @@ -192,12 +195,12 @@ type CustomPhoneProviderV1Event = { display_name: string; /** Metadata associated with the Organization. */ metadata: { - [additionalProperties: string]: string; + [key: string]: string; }; /** The name of the Organization. */ name: string; } & { - [additionalProperties: string]: any; + [key: string]: any; }; /** Details about the request that initiated the transaction. */ request: { @@ -213,7 +216,7 @@ type CustomPhoneProviderV1Event = { subdivisionName?: string; timeZone?: string; } & { - [additionalProperties: string]: any; + [key: string]: any; }; /** The hostname that is being used for the authentication flow. */ hostname?: string; @@ -249,7 +252,7 @@ type CustomPhoneProviderV1Event = { 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; + [key: string]: any; }; /** Timestamp indicating when the user profile was first created. */ created_at: string; @@ -261,6 +264,23 @@ type CustomPhoneProviderV1Event = { family_name?: string; /** User's given name. */ given_name?: 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?: { + [key: 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; + } & { + [key: string]: any; + })[]; /** 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. */ @@ -279,36 +299,13 @@ type CustomPhoneProviderV1Event = { 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; + [key: string]: any; }; /** (unique) User's username. */ username?: 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; + [key: string]: any; }; -}; -interface Configuration {} -interface Secrets { - [secretName: string]: string; -} -interface Event extends CustomPhoneProviderV1Event { /** * @private Configuration values associated with this Action. */ diff --git a/src/custom-phone-provider/v1/test/index.d.ts b/src/custom-phone-provider/v1/test/index.d.ts new file mode 100644 index 0000000..3a8c05f --- /dev/null +++ b/src/custom-phone-provider/v1/test/index.d.ts @@ -0,0 +1,244 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +interface NotificationsAPI { + /** + * When called, the notification event is considered failed without recovery: + * We will log an error for this event, but won't be sending it again to the action in the future. + * If you need this notification event to be retried, consider calling retry instead. + * @param reason this reason will be part of the log entry, this will help you analyze the error further. Please note that this field is limited to 1024 characters and will be truncated if larger. + */ + drop(reason: string): void; + /** + * When called, the notification event is considered failed, but recoverable: + * We will log an error for this event, but we will retry it up to 5 times in the next minutes. + * If you consider that this notification event should not be retried, consider calling drop instead. + * @param reason this reason will be part of the log entry, this will help you analyze the error further. Please note that this field is limited to 1024 characters and will be truncated if larger. + */ + retry(reason: string): void; +} +/** + * Methods and utilities to inform whether or not the event message should be treated as an error or not. + */ +interface CustomPhoneProviderAPI { + /** + * Make changes to the cache. + */ + readonly cache: CacheAPI; + /** + * Informs if we should consider the notification event as to be retried or to be dropped. + * See each of these methods for further details on the actual behaviour. + * If several calls are made, only the last one is considered. + */ + readonly notification: NotificationsAPI; +} +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event { + /** Information about the Client with which this login 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: { + [key: string]: string; + }; + /** The name of the application (as defined in the Dashboard). */ + name: string; + }; + /** Details about the Connection that was used to authenticate the user. */ + connection?: { + /** The connection's unique identifier. */ + id: string; + /** Metadata associated with the connection. */ + metadata?: { + [key: string]: string; + }; + /** The name of the connection used to authenticate the user (such as `twitter` or `some-g-suite-domain`). */ + name: string; + /** The type of connection. For social connections, `event.connection.strategy === event.connection.name`. For enterprise connections, the strategy is `waad` (Windows Azure AD), `ad` (Active Directory/LDAP), `auth0` (database connections), and so on. */ + strategy: string; + }; + /** Details about the custom domain associated with the current transaction. */ + custom_domain?: { + /** The custom domain name. */ + domain: string; + /** Custom domain metadata as key-value pairs. */ + domain_metadata: { + [key: string]: string; + }; + }; + notification: { + /** The text, as we rendered it, ready to be delivered as a text message. */ + as_text: string; + /** The text, as we rendered it, ready to be delivered as a voicetext message. */ + as_voice: string; + /** The One Time Password that we drawn for this message for some types (e.g. `otp_verify`, `otp_enroll`). If provided, it is important to have it conveyed to the end-user. */ + code?: string; + /** The way the message should be delivered. Could be `text` or `voice`. */ + delivery_method: 'text' | 'voice'; + /** The E.164 compliant phone number for the sender. */ + from?: string; + /** The locale we rendered the message in, example `en_US`, as defined in the BCP-47 specification. */ + locale?: string; + /** The type of message that is being send, like `otp_verify` or `blocked_account`. */ + message_type: + | 'otp_verify' + | 'otp_enroll' + | 'blocked_account' + | 'change_password' + | 'password_breach'; + /** The E.164 compliant phone number for the recipient. */ + recipient: 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: { + [key: string]: string; + }; + /** The name of the Organization. */ + name: string; + } & { + [key: string]: any; + }; + /** Details about the request that initiated the transaction. */ + request: { + geoip: { + cityName?: string; + continentCode?: string; + countryCode?: string; + countryCode3?: string; + countryName?: string; + latitude?: number; + longitude?: number; + subdivisionCode?: string; + subdivisionName?: string; + timeZone?: string; + } & { + [key: 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; + }; + tenant: { + /** The name of the tenant. */ + id: string; + /** The friendly name for the tenant, usually a more human-readable version of the ID. */ + friendly_name?: string; + /** The home URL for the tenant, if defined and as found in its settings. */ + home_url?: string; + /** The logo URL for the tenant, if defined and as found in its settings. */ + logo_url?: string; + /** The email to the tenant's support service, if defined and as found in its settings. */ + support_email?: string; + /** The url to the tenant's support service, if defined and as found in its settings. */ + support_url?: string; + }; + /** Details about the current transaction for tracing purposes. */ + transaction?: { + /** A unique identifier to correlate this request across multiple services for distributed tracing. */ + correlation_id?: string; + }; + /** An object describing the user on whose behalf the current transaction was initiated. */ + 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: { + [key: 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; + /** 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?: { + [key: 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; + } & { + [key: string]: any; + })[]; + /** 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: { + [key: string]: any; + }; + /** (unique) User's username. */ + username?: string; + } & { + [key: string]: any; + }; + /** + * @private Configuration values associated with this Action. + */ + configuration: Configuration; + /** + * Secret values securely associated with this Action. + */ + secrets: Secrets; +} +interface CustomPhoneProviderAction { + (event: Event, api: CustomPhoneProviderAPI): Promise; +} +type CustomPhoneProviderModule = { + onExecuteCustomPhoneProvider: CustomPhoneProviderAction; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link CustomPhoneProviderTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters< + CustomPhoneProviderModule[keyof CustomPhoneProviderModule] +>; +/** Loads a CustomPhoneProvider v1 action file for use in tests, e.g. `action.execute('onExecuteCustomPhoneProvider', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/custom-phone-provider/v1/test/index.js b/src/custom-phone-provider/v1/test/index.js new file mode 100644 index 0000000..b7d3f07 --- /dev/null +++ b/src/custom-phone-provider/v1/test/index.js @@ -0,0 +1,165 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); + +/** + * No-op {@link CustomPhoneProviderTriggerAPI} for use in mock API implementations. + */ +class CustomPhoneProviderTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + getCacheAPI() { + return this.#cacheAPI; + } + drop(_reason) { } + retry(_reason) { } +} + +const event = { + client: { + name: 'All Applications', + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + external_client_id: 'https://example.com/oauth-client.json', + metadata: {}, + external_metadata_type: 'cimd', + }, + notification: { + as_text: 'This is the text you should send, with otp: 123456', + as_voice: 'Hello. This is the text you should send, with otp: 1. 2. 3. 4. 5. 6. I repeat, 1. 2. 3. 4. 5. 6. ', + delivery_method: 'text', + message_type: 'otp_verify', + recipient: '+1-808-555-5555', + code: '123456', + locale: 'en-US', + from: '+1-808-555-2222', + }, + request: { + geoip: { + cityName: 'Bellevue', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + continentCode: 'NA', + countryCode: 'US', + countryCode3: 'USA', + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + ip: '13.33.86.47', + method: 'POST', + hostname: '{{TENANT}}.auth0.com', + language: 'en', + user_agent: 'curl/7.64.1', + }, + custom_domain: { + domain: 'login.{{YOUR_DOMAIN}}.com', + domain_metadata: { + environment: 'production', + region: 'us-west-2', + }, + }, + tenant: { + id: '{{TENANT}}', + friendly_name: 'Friendly {{TENANT}}', + home_url: 'https://example.com/home', + logo_url: 'https://www.gravatar.com/avatar/?d=monsterid', + support_email: 'email@example.com', + support_url: 'https://example.com/support', + }, + transaction: { + correlation_id: 'abcefg123', + }, + user: { + app_metadata: {}, + created_at: '{{DATE}}', + email_verified: true, + updated_at: '{{DATE}}', + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + user_metadata: {}, + email: 'j+smith@example.com', + family_name: 'Smith', + given_name: 'John', + last_password_reset: '{{DATE}}', + name: 'Johny Smith', + nickname: 'Johnny', + phone_number: '+15555555555', + phone_verified: true, + picture: 'https://www.gravatar.com/avatar/?d=identicon', + username: 'John Smith', + identities: [ + { + connection: 'Username-Password-Authentication', + isSocial: false, + provider: 'auth0', + userId: '5f7c8ec7c33c6c004bbafe82', + accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gU21pdGgiLCJpYXQiOjE1MTYyMzkwMjJ9.Q_w2AVguPRU2KskCXwR7ZHl09TQXEntfEA8Jj2_Jyew', + profileData: {}, + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + }, + ], + }, + organization: { + display_name: 'My Org', + id: 'my-org', + metadata: {}, + name: 'MyOrg', + }, + connection: { + id: 'con_example', + name: 'auth0', + strategy: 'auth0', + metadata: {}, + }, + configuration: {}, + secrets: {}, +}; + +class NotificationsAPIImpl { + #triggerAPI; + constructor(triggerAPI) { + this.#triggerAPI = triggerAPI; + } + drop(reason) { + this.#triggerAPI.drop(reason); + } + retry(reason) { + this.#triggerAPI.retry(reason); + } +} +class CustomPhoneProviderAPIImpl { + cache; + notification; + constructor(triggerAPI) { + this.cache = triggerAPI.getCacheAPI(); + this.notification = new NotificationsAPIImpl(triggerAPI); + } +} + +function contextToArguments(ctx) { + return [ctx.event, new CustomPhoneProviderAPIImpl(ctx.triggerAPI)]; +} +const getHandler = handler.getHandlerFactory([ + 'onExecuteCustomPhoneProvider', +]); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link CustomPhoneProviderTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +function getDefaultArguments() { + return contextToArguments({ + event: handler.deepClone(event), + triggerAPI: new CustomPhoneProviderTriggerAPIStubImpl(), + }); +} +/** Loads a CustomPhoneProvider v1 action file for use in tests, e.g. `action.execute('onExecuteCustomPhoneProvider', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction; diff --git a/src/custom-token-exchange/v1/test/index.d.ts b/src/custom-token-exchange/v1/test/index.d.ts new file mode 100644 index 0000000..2438e5c --- /dev/null +++ b/src/custom-token-exchange/v1/test/index.d.ts @@ -0,0 +1,415 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +import { + C as CustomTokenExchangeV1Event, + a as CustomTokenExchangeSetUserByConnectionUserAttributes, + b as CustomTokenExchangeSetUserByConnectionOptions, +} from '../../../_shared/CUlF8oaW.js'; +import { T as TxMetadataValue } from '../../../_shared/jH7s8Jy4.js'; +/** 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; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link CustomTokenExchangeTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters< + CustomTokenExchangeModule[keyof CustomTokenExchangeModule] +>; +/** Loads a CustomTokenExchange v1 action file for use in tests, e.g. `action.execute('onExecuteCustomTokenExchange', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/custom-token-exchange/v1/test/index.js b/src/custom-token-exchange/v1/test/index.js new file mode 100644 index 0000000..799a8e3 --- /dev/null +++ b/src/custom-token-exchange/v1/test/index.js @@ -0,0 +1,312 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +var metadata = require('../../../_shared/Dygdkb3A.js'); +var index = require('../../../_shared/DvTaCl9e.js'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); + +/** + * No-op {@link CustomTokenExchangeTriggerAPI} for use in mock API implementations. + */ +class CustomTokenExchangeTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + #transactionMetadataAPI = metadata.createNoopTransactionMetadataAPI(); + getCacheAPI() { + return this.#cacheAPI; + } + getTransactionMetadataAPI() { + return this.#transactionMetadataAPI; + } + deny(_code, _reason) { } + rejectInvalidSubjectToken(_reason) { } + setUserById(_user_id) { } + setUserByConnection(_connection_name, _user_attributes, _options) { } + setOrganization(_organization_id_or_name) { } + setActor(_actor) { } + setAppMetadata(_key, _value) { } + setUserMetadata(_key, _value) { } +} + +const event = { + client: { + authentication: { + type: 'self_signed_tls_client_auth', + certificate: { + subject: 'CN=client.example.com', + thumbprint256: 'qrvM3e7_ABEiM0RVZneImaq7zN3u_wARIjNEVWZ3iJk', + raw: '-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----', + subjectAltName: 'DNS:client.example.com, IP:127.0.0.1', + }, + }, + name: 'All Applications', + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + external_client_id: 'https://example.com/oauth-client.json', + metadata: {}, + external_metadata_type: 'cimd', + }, + tenant: { + id: '{{TENANT}}', + }, + request: { + body: {}, + hostname: '{{TENANT}}.auth0.com', + ip: '13.33.86.47', + method: 'GET', + geoip: { + cityName: 'Bellevue', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + continentCode: 'NA', + countryCode: 'US', + countryCode3: 'USA', + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + language: 'en', + user_agent: 'curl/7.64.1', + }, + transaction: { + subject_token_type: 'http://auth0.com/oauth/token-type/third-party-artifact', + subject_token: 'auth0|5f7c8ec7c33c6c004bbafe82', + requested_scopes: [], + requested_token_type: 'urn:ietf:params:oauth:token-type:access_token', + actor_token: 'bbafe8c3c00425f7c8ec7afe8c3c004b...', + actor_token_type: 'urn:ietf:params:oauth:token-type:id_token', + actor_token_user: { + app_metadata: { + plan: 'premium', + }, + created_at: '{{DATE}}', + email_verified: true, + email: 'j+smith@example.com', + family_name: 'Smith', + given_name: 'John', + identities: [ + { + accessToken: 'abcdefg1234567', + connection: 'Username-Password-Authentication', + isSocial: false, + profileData: {}, + provider: 'auth0', + userId: '5f7c8ec7c33c6c004bbafe82', + user_id: '5f7c8ec7c33c6c004bbafe82', + }, + ], + last_password_reset: '{{DATE}}', + name: 'John Smith', + nickname: 'jsmith', + phone_number: '+15551234567', + phone_verified: false, + picture: 'http://www.gravatar.com/avatar/?d=identicon', + updated_at: '{{DATE}}', + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + user_metadata: { + theme: 'dark', + }, + username: 'jsmith', + multifactor: [], + enrolledFactors: [], + }, + }, + resource_server: { + identifier: '{{TENANT}}.auth0.com/api/v2', + }, + secrets: {}, + configuration: {}, + organization: { + id: 'org_1234567890', + name: 'My Organization', + display_name: 'My Org', + metadata: { + key: 'value', + }, + }, + custom_domain: { + domain: 'login.{{YOUR_DOMAIN}}.com', + domain_metadata: { + environment: 'production', + region: 'us-west-2', + }, + }, +}; + +class AccessAPIImpl { + #triggerAPI; + constructor(triggerAPI) { + this.#triggerAPI = triggerAPI; + } + deny(code, reason) { + index.validate(index.CustomTokenExchangeDenyInputCodec, { code, reason }, 'Invalid deny arguments'); + this.#triggerAPI.deny(code, reason); + } + rejectInvalidSubjectToken(reason) { + index.validate(index.CustomTokenExchangeRejectInvalidSubjectTokenInputCodec, { reason }, 'Invalid rejectInvalidSubjectToken arguments'); + this.#triggerAPI.rejectInvalidSubjectToken(reason); + } +} + +const ACTOR_MAX_NESTING_DEPTH = 5; +const ACTOR_MAX_CUSTOM_PROPERTY_COUNT = 5; +const ALLOWED_VALUE_TYPES = new Set(['string', 'number', 'boolean']); +const ACTOR_MAX_SUB_LENGTH = 512; +const ACTOR_MAX_PROPERTY_KEY_LENGTH = 64; +const ACTOR_MAX_STRING_VALUE_LENGTH = 64; +function validateActorLevel(source) { + if (typeof source.sub !== 'string' || source.sub.length === 0) { + throw new Error('The actor object must contain a sub claim.'); + } + if (source.sub.length > ACTOR_MAX_SUB_LENGTH) { + throw new Error(`Invalid setActor arguments: sub must NOT have more than ${ACTOR_MAX_SUB_LENGTH} characters`); + } + const { sub: _sub, act: _act, type: _type, ...customProps } = source; + const customKeys = Object.keys(customProps); + if (customKeys.length > ACTOR_MAX_CUSTOM_PROPERTY_COUNT) { + throw new Error(`No more than ${ACTOR_MAX_CUSTOM_PROPERTY_COUNT} custom properties are allowed in the actor object.`); + } + for (const key of customKeys) { + if (!ALLOWED_VALUE_TYPES.has(typeof customProps[key])) { + throw new Error(`Custom property in an actor object must be a string, number, or boolean.`); + } + if (key.length > ACTOR_MAX_PROPERTY_KEY_LENGTH) { + throw new Error(`Custom property key in an actor object must NOT have more than ${ACTOR_MAX_PROPERTY_KEY_LENGTH} characters.`); + } + if (typeof customProps[key] === 'string' && + customProps[key].length > ACTOR_MAX_STRING_VALUE_LENGTH) { + throw new Error(`Custom string property value in an actor object must NOT have more than ${ACTOR_MAX_STRING_VALUE_LENGTH} characters.`); + } + } +} +function validateActor(actor) { + if (!metadata.isObject(actor)) { + throw new Error('The actor must be a plain object.'); + } + validateActorLevel(actor); + let source = actor; + for (let depth = 2; depth <= ACTOR_MAX_NESTING_DEPTH; depth++) { + if (source.act === undefined) + return; + if (!metadata.isObject(source.act)) { + throw new Error('The act claim must be a plain object.'); + } + validateActorLevel(source.act); + source = source.act; + } + // act exists beyond the allowed nesting depth + if (source.act !== undefined) { + throw new Error(`Actor nesting must not exceed ${ACTOR_MAX_NESTING_DEPTH} levels.`); + } +} +class AuthenticationAPIImpl { + #triggerAPI; + constructor(triggerAPI) { + this.#triggerAPI = triggerAPI; + } + setUserById(user_id) { + index.validate(index.CustomTokenExchangeSetUserByIdInputCodec, { user_id }, 'Invalid setUserById arguments'); + this.#triggerAPI.setUserById(user_id); + } + setUserByConnection(connection_name, user_attributes, options) { + index.validate(index.CustomTokenExchangeSetUserByConnectionInputCodec, { connection_name, user_attributes, options }, 'Invalid setUserByConnection arguments'); + this.#triggerAPI.setUserByConnection(connection_name, user_attributes, options); + } + setOrganization(organization_id_or_name) { + index.validate(index.CustomTokenExchangeSetOrganizationInputCodec, { organization_id_or_name }, 'Invalid setOrganization arguments'); + this.#triggerAPI.setOrganization(organization_id_or_name); + } + setActor(actor) { + validateActor(actor); + this.#triggerAPI.setActor(actor); + } +} + +/** Feature flag gating access to the transaction metadata API. */ +const TRANSACTION_METADATA_FLAG = 'actions_cte_transaction_metadata'; +class TransactionAPIImpl { + #metadataAPI; + #event; + #featureFlags; + constructor(metadataAPI, event, featureFlags) { + this.#metadataAPI = metadataAPI; + this.#event = event; + this.#featureFlags = featureFlags; + } + setMetadata(key, value) { + this.#assertFeatureEnabled(); + this.#metadataAPI.setMetadata(key, value); + if (!this.#event.transaction) { + this.#event.transaction = {}; + } + this.#event.transaction.metadata = this.#metadataAPI.getMetadata(); + } + // Prevent the internal transaction metadata API from being executed when the + // gating feature flag is disabled. Remove this guard at GA. + #assertFeatureEnabled() { + if (this.#featureFlags[TRANSACTION_METADATA_FLAG] !== true) { + throw new Error('Method not implemented.'); + } + } +} + +class UserAPIImpl { + #triggerAPI; + constructor(triggerAPI) { + this.#triggerAPI = triggerAPI; + } + setAppMetadata(key, value) { + index.validate(index.CustomTokenExchangeSetMetadataInputCodec, { key, value }, 'Invalid setAppMetadata arguments'); + this.#triggerAPI.setAppMetadata(key, value); + } + setUserMetadata(key, value) { + index.validate(index.CustomTokenExchangeSetMetadataInputCodec, { key, value }, 'Invalid setUserMetadata arguments'); + this.#triggerAPI.setUserMetadata(key, value); + } +} + +class CustomTokenExchangeAPIImpl { + access; + authentication; + user; + cache; + transaction; + constructor(triggerAPI, event, featureFlags) { + this.access = new AccessAPIImpl(triggerAPI); + this.authentication = new AuthenticationAPIImpl(triggerAPI); + this.user = new UserAPIImpl(triggerAPI); + this.cache = triggerAPI.getCacheAPI(); + this.transaction = new TransactionAPIImpl(triggerAPI.getTransactionMetadataAPI(), event, featureFlags); + } +} + +function contextToArguments(ctx) { + return [ + ctx.event, + new CustomTokenExchangeAPIImpl(ctx.triggerAPI, ctx.event, ctx.featureFlags), + ]; +} +const getHandler = handler.getHandlerFactory([ + 'onExecuteCustomTokenExchange', +]); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link CustomTokenExchangeTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +function getDefaultArguments() { + return contextToArguments({ + event: handler.deepClone(event), + triggerAPI: new CustomTokenExchangeTriggerAPIStubImpl(), + featureFlags: {}, + }); +} +/** Loads a CustomTokenExchange v1 action file for use in tests, e.g. `action.execute('onExecuteCustomTokenExchange', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction; diff --git a/src/event-stream/v1/index.d.ts b/src/event-stream/v1/index.d.ts index dc03e8f..a2b37e9 100644 --- a/src/event-stream/v1/index.d.ts +++ b/src/event-stream/v1/index.d.ts @@ -122,6 +122,16 @@ type EventStreamV1Event = { specversion: string; /** Timestamp of when the occurrence happened. Must adhere to RFC 3339. */ time?: string | null; + /** The Auth0 tenant identifier to which the event is associated. */ + a0tenant: string; + /** + * The Auth0 event stream ID of the stream the event was delivered on. + * Present when the event is delivered via an event stream; omitted when + * events are retrieved via the Events API (GET /api/v2/events). + */ + a0stream?: string; + /** The purpose of this event. Set only in special cases such as a test event; omitted for normal events. */ + a0purpose?: 'test' & string; }; }; interface Configuration {} diff --git a/src/event-stream/v1/test/index.d.ts b/src/event-stream/v1/test/index.d.ts new file mode 100644 index 0000000..0264001 --- /dev/null +++ b/src/event-stream/v1/test/index.d.ts @@ -0,0 +1,46 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +import { E as EventStreamV1Event } from '../../../_shared/CUlF8oaW.js'; +/** + * Methods and utilities to help change the behaviour of the event stream flow. + */ +interface EventStreamAPI { + /** + * Store and retrieve data that persists across executions. + */ + readonly cache: CacheAPI; +} +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event extends EventStreamV1Event { + /** + * @private Configuration values associated with this Action. + */ + configuration: Configuration; + /** + * Secret values securely associated with this Action. + */ + secrets: Secrets; +} +interface EventStreamAction { + (event: Event, api: EventStreamAPI): Promise; +} +type EventStreamModule = { + onExecuteEventStream: EventStreamAction; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link EventStreamTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters; +/** Loads an EventStream v1 action file for use in tests, e.g. `action.execute('onExecuteEventStream', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/event-stream/v1/test/index.js b/src/event-stream/v1/test/index.js new file mode 100644 index 0000000..089c38a --- /dev/null +++ b/src/event-stream/v1/test/index.js @@ -0,0 +1,143 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); + +/** + * No-op {@link EventStreamTriggerAPI} for use in mock API implementations. + */ +class EventStreamTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + getCacheAPI() { + return this.#cacheAPI; + } +} + +const event = { + message: { + id: 'evt_vjFVP5pVdfvsfRKatshkVV', + type: 'user.created', + source: 'urn:auth0:example.auth0app.com', + specversion: '1.0', + time: '2025-02-01T12:34:56Z', + data: { + object: { + user_id: 'auth0|507f1f77bcf86cd799439020', + email: 'john.doe@gmail.com', + email_verified: false, + username: 'johndoe', + phone_number: '+15555555555', + phone_verified: false, + created_at: '2025-02-01T12:34:56Z', + updated_at: '2025-02-01T12:34:56Z', + identities: [ + { + connection: 'Username-Password-Authentication', + user_id: '507f1f77bcf86cd799439020', + profileData: { + email: 'john.doe@gmail.com', + email_verified: false, + name: 'John Doe', + username: 'johndoe', + given_name: 'John', + family_name: 'Doe', + phone_number: '+15555555555', + phone_verified: false, + }, + provider: 'custom', + isSocial: false, + }, + ], + app_metadata: { + plan: 'pro', + }, + user_metadata: { + hobby: 'skydiving', + }, + picture: 'https://secure.gravatar.com/avatar/15626c5e0c749cb912f9d1ad48dba440?s=480&r=pg&d=https%3A%2F%2Fssl.gstatic.com%2Fs2%2Fprofiles%2Fimages%2Fsilhouette80.png', + name: 'John Doe', + nickname: 'John Doe', + multifactor: ['sample'], + last_ip: '10.0.0.1', + last_login: '2025-02-01T12:34:56Z', + logins_count: 42, + blocked: false, + given_name: 'John', + family_name: 'Doe', + }, + context: { + client: { + id: 'QG4F6eABIgDzPvcKCMKUjo8c9iet2Skc', + name: 'My App', + metadata: {}, + }, + connection: { + id: 'con_kFOHQUeaCSC1Kjqz', + name: 'Username-Password-Authentication', + strategy: 'auth0', + }, + request: { + geo: { + continent_code: 'NA', + country_code: 'US', + country_name: 'United States', + latitude: 37.3382, + longitude: -121.8863, + subdivision_code: 'CA', + subdivision_name: 'California', + city_name: 'San Jose', + time_zone: 'America/Los_Angeles', + }, + hostname: 'example.auth0app.com', + custom_domain: 'login.example.com', + ip: '203.0.113.1', + method: 'POST', + user_agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + }, + tenant: { + id: 'my-tenant', + }, + }, + }, + a0tenant: 'my-tenant', + a0stream: 'est_vjFVP5pVdfvsfRKatshkVV', + a0purpose: 'test', + }, + configuration: {}, + secrets: {}, +}; + +class EventStreamAPIImpl { + cache; + constructor(triggerAPI) { + this.cache = triggerAPI.getCacheAPI(); + } +} + +function contextToArguments(ctx) { + return [ctx.event, new EventStreamAPIImpl(ctx.triggerAPI)]; +} +const getHandler = handler.getHandlerFactory([ + 'onExecuteEventStream', +]); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link EventStreamTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +function getDefaultArguments() { + return contextToArguments({ + event: handler.deepClone(event), + triggerAPI: new EventStreamTriggerAPIStubImpl(), + }); +} +/** Loads an EventStream v1 action file for use in tests, e.g. `action.execute('onExecuteEventStream', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction; diff --git a/src/password-reset-post-challenge/v1/test/index.d.ts b/src/password-reset-post-challenge/v1/test/index.d.ts new file mode 100644 index 0000000..fc140eb --- /dev/null +++ b/src/password-reset-post-challenge/v1/test/index.d.ts @@ -0,0 +1,252 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +import { R as RenderPromptId, a as RenderPromptOptions } from '../../../_shared/C_R4QU65.js'; +import { P as PasswordResetPostChallengeV1Event } from '../../../_shared/CUlF8oaW.js'; +type FactorSelector = + | { + type: 'otp' | 'email' | 'webauthn-platform' | 'webauthn-roaming' | 'recovery-code'; + options?: { + [key: string]: unknown; + }; + } + | { + type: 'phone'; + options?: { + preferredMethod?: 'sms' | 'voice' | 'both'; + }; + } + | { + type: 'push' | 'push-notification'; + options?: { + otpFallback?: boolean; + }; + }; +interface ChallengeWithOptions { + additionalFactors?: FactorSelector[]; +} +interface AccessAPI { + /** + * Mark the current password reset attempt as denied. This will prevent the end-user from completing + * the password reset flow. This will *NOT* cancel other user-related side-effects + * requested by this Action. The password reset flow will immediately stop following the + * completion of this action and no further Actions will be executed. + * + * @param reason A human-readable explanation for rejecting the password reset. This may be presented + * directly in end-user interfaces. + */ + deny(reason: string): void; +} +interface AuthenticationAPI { + /** + * Request a challenge for multifactor authentication using the supplied factor and optional additional factors. + * + * When a multifactor challenge is requested, subsequent Actions will not be run until that challenge has been + * fulfilled by the user. A user will have satisfied the challenge in any of the following situations: + * + * 1. They successfully complete the challenge for the default factor. + * 2. They successfully complete the challenge for any of the optional factors described in `additionalFactors`. + * + * If any of the factors requested has already been challenged successfully in the current transaction, it will + * be ignored. + * + * If a factor is requested is not enabled on the tenant, it will be ignored. If a factor is requested that the user + * has not enrolled, it will be ignored. If none of the requested factors is enabled or enrolled, the authentication + * transaction will fail (i.e. login will not complete). + * + * _**Note**: This method will result in a factor challenge screen being shown if the user has not already satisfied + * the requirements of the challenge. If `additionalFactors` are supplied, the user will have the option to + * select another factor if they choose to._ + * + * @param factor An object describing the type of factor its options that should be used for the initial challenge. + * @param options Additional options which can also specify `additionalFactors` as a property. + */ + challengeWith(factor: FactorSelector, options?: ChallengeWithOptions): void; + /** + * Request a challenge for multifactor authentication using any of the supplied factors (showing a factor selection + * screen first). + * + * When a multifactor challenge is requested, subsequent Actions will not be run until that challenge has been + * fulfilled by the user. A user will have satisfied the challenge in any of the following situations: + * + * 1. They successfully complete the challenge for any of the factors. + * + * If any of the factors requested has already been challenged successfully in the current transaction, it will + * be ignored. + * + * If a factor is requested is not enabled on the tenant, it will be ignored. If a factor is requested that the user + * has not enrolled, it will be ignored. If none of the requested factors is enabled or enrolled, the authentication + * transaction will fail (i.e. login will not complete). + * + * _**Note**: This method will result in the factor selector screen being shown if the user has not already satisfied + * the requirements of the challenge. If there is a preferred factor, the `api.authentication.challengeWith()` method + * is preferred. The factor selector screen will not be shown if only one factor is passed in or is valid._ + * + * @param factors An array of factors. + */ + challengeWithAny(factors: FactorSelector[]): void; +} +interface PromptAPI { + /** + * Renders a custom prompt. + * + * @param promptId The prompt ID. + * @param promptOptions The render options. + */ + render(promptId: RenderPromptId, promptOptions?: RenderPromptOptions): void; +} +interface TokenCreationOptions { + /** + * Number of seconds before this token will expire + * + * @default 900 15 minutes. + */ + expiresInSeconds?: number; + /** + * The data intended to be passed to the target of the redirect and whose authenticity + * and integrity must be provable. + */ + payload: { + [key: string]: unknown; + }; + /** + * A secret that will be used to sign a JWT that is shared with the redirect target. The + * secret value should be stored as a **secret** and retrieved using + * `event.secrets['']`. + */ + secret: string; +} +interface ValidateSessionTokenOptions { + secret: string; + /** + * The name of the query or body parameter that was sent to the /continue endpoint. + * + * @default 'session_token' + */ + tokenParameterName?: string; +} +interface SendUserToOptions { + /** + * An object representing additional query string parameters that should be appended to + * the redirect URL. + */ + query?: { + [param: string]: string; + }; +} +interface RedirectAPI { + /** + * Create a session token suitable for using as a query string parameter redirect target (via `sendUserTo`) + * that contains data whose authenticity must be provable by the target endpoint. The target endpoint + * can verify the authenticity and integrity of the data by checking the JWT's signature + * using a shared secret. + * + * The shared secret should be stored as a **secret** of the Action and will be readable at + * `event.secrets['']`. + * + * @param options Configure how sensitive data is encoded into the query parameters of the + * resulting url. + * + * @returns A JWT string. + */ + encodeToken(options: TokenCreationOptions): string; + /** + * Cause the password reset pipeline to trigger a browser redirect to the target `url` immediately after + * this action completes. The `createUrl` helper method is provided to simplify encoding + * data as a query parameter in the target `url` such that the data's authenticity and + * integrity can be verified by the target endpoint. + * + * @param baseUrl The url to which to redirect the user. + */ + sendUserTo(url: string, options?: SendUserToOptions): void; + /** + * Retrieve the data encoded in a JWT token passed to the `/continue` endpoint while verifying + * the authenticity and integrity of that data. + * + * @param options Options for retrieving the data encoded in a JWT token passed to the + * `/continue` endpoint following a rediret. + * + * @returns The payload of the JWT token. + */ + validateToken(options: ValidateSessionTokenOptions): any; +} +interface ResultUrlOptions { + /** + * The query parameters to include in the URL. + */ + query?: Record; +} +interface TransactionAPI { + /** + * Set the URL that the user should be redirected to after the password reset. + * + * @param url The URL to redirect the user to. + */ + setResultUrl(url: string, options?: ResultUrlOptions): void; +} +/** + * Methods and utilities to help change the behavior of the password reset flow. + */ +interface PasswordResetPostChallengeAPI { + /** + * Modify the access of the user that is attempting to reset their password. + */ + readonly access: AccessAPI; + /** + * Request changes to the authentication state of the current user's session. + */ + readonly authentication: AuthenticationAPI; + /** + * Configure and initiate external redirects. + */ + readonly redirect: RedirectAPI; + /** + * Make changes to the cache. + */ + readonly cache: CacheAPI; + /** + * Renders a custom prompt. + */ + readonly prompt: PromptAPI; + /** + * Configure the transaction. + */ + readonly transaction: TransactionAPI; +} +interface Secrets { + [secretName: string]: string; +} +interface Configuration {} +interface Event extends PasswordResetPostChallengeV1Event { + /** + * @private Configuration values associated with this Action. + */ + readonly configuration: Configuration; + /** + * Secret values securely associated with this Action. + */ + readonly secrets: Secrets; +} +interface PasswordResetPostChallengeAction { + (event: Event, api: PasswordResetPostChallengeAPI): Promise; +} +type PasswordResetPostChallengeModule = { + onExecutePostChallenge: PasswordResetPostChallengeAction; + onContinuePostChallenge: PasswordResetPostChallengeAction; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link PasswordResetPostChallengeTriggerAPI} and redirect context. The event is cloned per call so + * mutations in one execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters< + PasswordResetPostChallengeModule[keyof PasswordResetPostChallengeModule] +>; +/** Loads a PasswordResetPostChallenge v1 action file for use in tests, e.g. `action.execute('onExecutePostChallenge', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/password-reset-post-challenge/v1/test/index.js b/src/password-reset-post-challenge/v1/test/index.js new file mode 100644 index 0000000..ad23d36 --- /dev/null +++ b/src/password-reset-post-challenge/v1/test/index.js @@ -0,0 +1,396 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +var index = require('../../../_shared/DvTaCl9e.js'); +var url = require('url'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); + +/** + * No-op {@link PasswordResetPostChallengeTriggerAPI} for use in mock API implementations. + */ +class PasswordResetPostChallengeTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + getCacheAPI() { + return this.#cacheAPI; + } + denyAccess(_reason) { } + challengeWith(_factor, _options) { } + challengeWithAny(_factors) { } + renderPrompt(_promptId, _promptOptions) { } + sendUserTo(_url) { } + setResultUrl(_url) { } + encodeToken(_options) { + return ''; + } + validateToken(_options) { + return { valid: false, reason: '' }; + } + abort(_message) { } + hasRedirectPrompt() { + return false; + } + hasRenderPrompt() { + return false; + } +} + +const event = { + authentication: { + methods: [ + { + name: 'mfa', + timestamp: '2018-11-13T20:20:39+00:00', + type: 'email', + }, + ], + riskAssessment: { + supplemental: { + akamai: { + akamaiBot: { + type: 'Akamai-Categorized Bot', + botnetId: 'googlebot', + action: 'Monitor', + botCategory: ['Web Search Engine Bots', 'Search Engine Bots'], + botScore: 0, + botScoreResponseSegment: 'test', + }, + akamaiUserRisk: { + uuid: '86b37525-8047-4a3c-8d7a-23e99901da05', + username: 'testuser@example.com', + emailDomain: 'example.com', + ouid: 'm534264', + requestid: '19e22e', + status: 4, + score: 0, + general: { + aci: '0', + db: 'Chrome 85', + di: '0fc91b5ec42f5a471c16a85e3e388ca57697c1a9', + do: 'Mac OS X 10', + }, + risk: { + ugp: 'ie/M', + unp: '432/H', + }, + trust: { + udbp: 'Chrome85', + udfp: '25ba44ec3b391ba4ce5fbbd2979635e254775e7d', + udop: 'Mac OS X 10', + ugp: 'FR', + unp: '12322', + utp: 'weekday_3', + }, + allow: 0, + action: 'monitor', + }, + }, + }, + }, + }, + authorization: { + roles: [], + }, + client: { + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + external_client_id: 'https://example.com/oauth-client.json', + metadata: {}, + name: 'All Applications', + external_metadata_type: 'cimd', + }, + configuration: {}, + connection: { + id: 'con_fpe5kj482KO1eOzQ', + name: 'Username-Password-Authentication', + strategy: 'auth0', + metadata: {}, + }, + custom_domain: { + domain: 'login.{{YOUR_DOMAIN}}.com', + domain_metadata: { + environment: 'production', + region: 'us-west-2', + }, + }, + prompt: { + id: 'prompt_1234567890', + fields: {}, + vars: {}, + }, + request: { + body: { + client_id: 'client-id', + client_secret: 'client-secret', + audience: '{{TENANT}}.auth0.com/api/v2', + grant_type: 'client_credentials', + }, + geoip: { + cityName: 'Bellevue', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + continentCode: 'NA', + countryCode: 'US', + countryCode3: 'USA', + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + ip: '13.33.86.47', + method: 'POST', + hostname: '{{TENANT}}.auth0.com', + language: 'en', + user_agent: 'curl/7.64.1', + query: { + protocol: 'oauth2', + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + response_type: 'code', + connection: 'Username-Password-Authentication', + prompt: 'login', + scope: 'openid profile', + redirect_uri: 'https://example/tester/callback?connection=Username-Password-Authentication', + }, + }, + secrets: {}, + stats: { + logins_count: 62, + }, + tenant: { + id: '{{TENANT}}', + }, + user: { + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + app_metadata: {}, + created_at: '{{DATE}}', + email: 'j+smith@example.com', + email_verified: true, + identities: [], + name: 'j+smith@example.com', + nickname: 'j+smith', + picture: 'http://www.gravatar.com/avatar/?d=identicon', + updated_at: '{{DATE}}', + user_metadata: {}, + family_name: 'Smith', + given_name: 'John', + last_password_reset: '{{DATE}}', + phone_number: '18882352699', + phone_verified: false, + username: 'jsmith', + enrolledFactors: [], + }, + transaction: { + acr_values: [], + locale: 'en', + requested_scopes: [], + ui_locales: ['en'], + protocol: 'oidc-basic-profile', + redirect_uri: 'http://someuri.com', + prompt: ['none'], + login_hint: 'test@test.com', + response_mode: 'form_post', + response_type: ['id_token'], + state: 'AABBccddEEFFGGTTasrs', + requested_authorization_details: [ + { + type: 'foo', + }, + ], + linking_id: 'abc_dynamic_linking_id_123', + correlation_id: 'abcefg123', + }, + organization: { + display_name: 'My Organization', + id: 'org_juG7cAQ0CymOcVpV', + metadata: {}, + name: 'my-organization', + }, +}; + +class AccessAPIImpl { + #triggerAPI; + constructor(triggerAPI) { + this.#triggerAPI = triggerAPI; + } + deny(reason) { + this.#triggerAPI.denyAccess(reason); + } +} + +class AuthenticationAPIImpl { + #triggerAPI; + constructor(triggerAPI) { + this.#triggerAPI = triggerAPI; + } + challengeWithAny(factors) { + this.#triggerAPI.challengeWithAny(factors); + } + challengeWith(factor, options) { + this.#triggerAPI.challengeWith(factor, options); + } +} + +class PromptAPIImpl { + #triggerAPI; + constructor(triggerAPI) { + this.#triggerAPI = triggerAPI; + } + render(promptId, promptOptions) { + if (this.#triggerAPI.hasRedirectPrompt()) { + this.#triggerAPI.abort('Redirect and render commands cannot be used in the same action.'); + return; + } + if (this.#triggerAPI.hasRenderPrompt()) { + this.#triggerAPI.abort('Only 1 render command per action is allowed.'); + return; + } + const resolvedPromptOptions = promptOptions || {}; + index.validate(index.PasswordResetPostChallengeRenderPromptInputCodec, { + promptId, + promptOptions: resolvedPromptOptions, + }); + if (Buffer.byteLength(JSON.stringify(resolvedPromptOptions), 'utf-8') > + index.MAX_RENDER_PROMPT_OPTIONS_BYTES) { + throw new Error(`The total size of the prompt options exceeds the limit of ${index.MAX_RENDER_PROMPT_OPTIONS_BYTES} bytes.`); + } + this.#triggerAPI.renderPrompt(promptId, resolvedPromptOptions); + } +} + +const DEFAULT_SESSION_TOKEN_EXP = 60 * 15; // 15 minutes +const DEFAULT_SESSION_TOKEN_QUERY_PARAM = 'session_token'; +class RedirectAPIImpl { + #getContinueParams = (request) => { + return { + ...request?.query, + ...request?.body, + }; + }; + #triggerAPI; + #context; + constructor(triggerAPI, context) { + this.#triggerAPI = triggerAPI; + this.#context = context; + } + encodeToken(options) { + const issuedAt = Math.floor(Date.now() / 1000); + const payload = { + iat: issuedAt, + iss: this.#context.request.hostname, + sub: this.#context.user.user_id, + exp: issuedAt + (options.expiresInSeconds ?? DEFAULT_SESSION_TOKEN_EXP), + ip: this.#context.request.ip, + ...options.payload, + }; + return this.#triggerAPI.encodeToken({ + payload, + secret: options.secret, + }); + } + sendUserTo(baseUrl, urlOptions) { + if (this.#triggerAPI.hasRenderPrompt()) { + this.#triggerAPI.abort('Redirect and render commands cannot be used in the same action.'); + return; + } + const url$1 = new url.URL(baseUrl); + if (urlOptions?.query) { + for (const param in urlOptions.query) { + url$1.searchParams.set(param, urlOptions.query[param]); + } + } + this.#triggerAPI.sendUserTo(url$1.href); + } + validateToken(options) { + options.tokenParameterName ||= DEFAULT_SESSION_TOKEN_QUERY_PARAM; + const params = this.#getContinueParams(this.#context.request); + const token = params[options.tokenParameterName]; + if (!token) { + throw new Error(`There is no parameter called '${options.tokenParameterName}' available in either the POST body or query string.`); + } + const response = this.#triggerAPI.validateToken({ + token: token, + secret: options.secret, + issuer: this.#context.request.hostname, + expectedState: params.state, + expectedSub: this.#context.user.user_id, + }); + if (!response.valid) { + throw new Error(`The session token is invalid: ${response.reason}`); + } + return response.payload; + } +} + +class TransactionAPIImpl { + #triggerAPI; + constructor(triggerAPI) { + this.#triggerAPI = triggerAPI; + } + setResultUrl(url$1, urlOptions) { + const resultUrl = new url.URL(url$1); + if (urlOptions?.query) { + for (const param in urlOptions.query) { + resultUrl.searchParams.set(param, urlOptions.query[param]); + } + } + this.#triggerAPI.setResultUrl(resultUrl.href); + } +} + +class PasswordResetPostChallengeAPIImpl { + access; + authentication; + prompt; + redirect; + cache; + transaction; + constructor(triggerAPI, redirectContext) { + this.cache = triggerAPI.getCacheAPI(); + this.access = new AccessAPIImpl(triggerAPI); + this.authentication = new AuthenticationAPIImpl(triggerAPI); + this.prompt = new PromptAPIImpl(triggerAPI); + this.redirect = new RedirectAPIImpl(triggerAPI, redirectContext); + this.transaction = new TransactionAPIImpl(triggerAPI); + } +} + +function contextToArguments(ctx) { + return [ctx.event, new PasswordResetPostChallengeAPIImpl(ctx.triggerAPI, ctx.redirectContext)]; +} +const getHandler = handler.getHandlerFactory([ + 'onExecutePostChallenge', + 'onContinuePostChallenge', +]); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link PasswordResetPostChallengeTriggerAPI} and redirect context. The event is cloned per call so + * mutations in one execution don't leak into the next. + */ +function getDefaultArguments() { + const event$1 = handler.deepClone(event); + const redirectContext = { + request: { + hostname: event$1.request.hostname, + ip: event$1.request.ip, + query: event$1.request.query, + body: event$1.request.body, + }, + user: { + user_id: event$1.user.user_id, + }, + }; + return contextToArguments({ + event: event$1, + triggerAPI: new PasswordResetPostChallengeTriggerAPIStubImpl(), + redirectContext, + }); +} +/** Loads a PasswordResetPostChallenge v1 action file for use in tests, e.g. `action.execute('onExecutePostChallenge', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction; diff --git a/src/post-change-password/v2/test/index.d.ts b/src/post-change-password/v2/test/index.d.ts new file mode 100644 index 0000000..ed72326 --- /dev/null +++ b/src/post-change-password/v2/test/index.d.ts @@ -0,0 +1,48 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +import { c as PostChangePasswordV2Event } from '../../../_shared/CUlF8oaW.js'; +/** + * Methods and utilities to help change the behavior after a user changes their password. + */ +interface PostChangePasswordAPI { + /** + * Store and retrieve data that persists across executions. + */ + readonly cache: CacheAPI; +} +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event extends PostChangePasswordV2Event { + /** + * @private Configuration values associated with this Action. + */ + configuration: Configuration; + /** + * Secret values securely associated with this Action. + */ + secrets: Secrets; +} +interface PostChangePasswordAction { + (event: Event, api: PostChangePasswordAPI): Promise; +} +type PostChangePasswordModule = { + onExecutePostChangePassword: PostChangePasswordAction; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link PostChangePasswordTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters< + PostChangePasswordModule[keyof PostChangePasswordModule] +>; +/** Loads a PostChangePassword v2 action file for use in tests, e.g. `action.execute('onExecutePostChangePassword', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/post-change-password/v2/test/index.js b/src/post-change-password/v2/test/index.js new file mode 100644 index 0000000..ad86720 --- /dev/null +++ b/src/post-change-password/v2/test/index.js @@ -0,0 +1,147 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); + +/** + * No-op {@link PostChangePasswordTriggerAPI} for use in mock API implementations. + */ +class PostChangePasswordTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + getCacheAPI() { + return this.#cacheAPI; + } +} + +const event = { + authentication: { + riskAssessment: { + supplemental: { + akamai: { + akamaiBot: { + type: 'Akamai-Categorized Bot', + action: 'Monitor', + botCategory: ['Web Search Engine Bots', 'Search Engine Bots'], + botScore: 0, + botScoreResponseSegment: 'test', + botnetId: 'googlebot', + }, + akamaiUserRisk: { + action: 'monitor', + allow: 0, + emailDomain: 'example.com', + general: { + aci: '0', + db: 'Chrome 85', + di: '0fc91b5ec42f5a471c16a85e3e388ca57697c1a9', + do: 'Mac OS X 10', + }, + ouid: 'm534264', + requestid: '19e22e', + risk: { + ugp: 'ie/M', + unp: '432/H', + }, + score: 0, + status: 4, + trust: { + udbp: 'Chrome85', + udfp: '25ba44ec3b391ba4ce5fbbd2979635e254775e7d', + udop: 'Mac OS X 10', + ugp: 'FR', + unp: '12322', + utp: 'weekday_3', + }, + username: 'testuser@example.com', + uuid: '86b37525-8047-4a3c-8d7a-23e99901da05', + }, + }, + }, + }, + }, + connection: { + id: 'con_fpe5kj482KO1eOzQ', + name: 'Username-Password-Authentication', + strategy: 'auth0', + metadata: {}, + }, + custom_domain: { + domain: 'login.{{YOUR_DOMAIN}}.com', + domain_metadata: { + environment: 'production', + region: 'us-west-2', + }, + }, + request: { + geoip: { + cityName: 'Bellevue', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + continentCode: 'NA', + countryCode: 'US', + countryCode3: 'USA', + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + ip: '10.12.13.1', + method: 'post', + hostname: '{{TENANT}}.auth0.com', + language: 'en', + user_agent: 'curl/7.64.1', + }, + tenant: { + id: '{{TENANT}}', + }, + transaction: { + correlation_id: 'abcefg123', + }, + user: { + id: '5f7c8ec7c33c6c004bbafe82', + user_id: 'auth0|test12345', + email: 'j+smith@example.com', + email_verified: true, + phone_number: '+13205550100', + phone_verified: true, + last_password_reset: '{{DATE}}', + username: 'j+smith', + }, + configuration: {}, + secrets: {}, +}; + +class PostChangePasswordAPIImpl { + cache; + constructor(triggerAPI) { + this.cache = triggerAPI.getCacheAPI(); + } +} + +function contextToArguments(ctx) { + return [ctx.event, new PostChangePasswordAPIImpl(ctx.triggerAPI)]; +} +const getHandler = handler.getHandlerFactory([ + 'onExecutePostChangePassword', +]); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link PostChangePasswordTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +function getDefaultArguments() { + return contextToArguments({ + event: handler.deepClone(event), + triggerAPI: new PostChangePasswordTriggerAPIStubImpl(), + }); +} +/** Loads a PostChangePassword v2 action file for use in tests, e.g. `action.execute('onExecutePostChangePassword', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction; diff --git a/src/post-login/v3/test/index.d.ts b/src/post-login/v3/test/index.d.ts new file mode 100644 index 0000000..b0055d8 --- /dev/null +++ b/src/post-login/v3/test/index.d.ts @@ -0,0 +1,1054 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +import { R as RenderPromptId, a as RenderPromptOptions } from '../../../_shared/C_R4QU65.js'; +import { T as TxMetadataValue } from '../../../_shared/jH7s8Jy4.js'; +import { d as PostLoginV3Event } from '../../../_shared/CUlF8oaW.js'; +/** Pagination options for fetching the groups a user belongs to. */ +type GetUserGroupsParams = { + take?: number; + from?: string; +}; +type ListUserGroupsResponse = { + /** The list of user groups. */ + groups: UserGroupMembership[]; + /** A cursor for pagination. */ + next?: string; +}; +type UserGroupMembership = { + /** Unique identifier of the group. */ + id: string; + /** Name of the Group */ + name: string; + /** External identifier of the group, often used for SCIM synchronization. */ + external_id?: string; + /** Identifier of the connection this group belongs to (if a connection group). */ + connection_id?: string; + /** Identifier of the tenant this group belongs to. */ + tenant_name: string; + /** Timestamp of when the group was created. */ + created_at: string; + /** Timestamp of when the group was updated. */ + updated_at: string; + /** Timestamp of when the group membership was added. */ + membership_created_at: string; + /** The method with which the group was assigned to the user. */ + assignment: string; +}; +type CheckGroupMembershipResponse = { + /** Map of group identifiers (ID or name) to membership details including status and matching groups */ + memberships: { + [key: string]: { + /** Whether the user is a member of this group */ + is_member: boolean; + /** Array of matching groups when is_member is true */ + matches?: { + /** Unique identifier of the group. */ + id: string; + /** Name of the Group */ + name: string; + /** Can be `connection`, `organization` or `tenant` */ + group_type: string; + }[]; + }; + }; + /** Map of group identifiers to error codes for groups that couldn't be checked */ + errors?: { + [key: string]: string; + }; +}; +/** + * Parameters for fetching user effective roles with checkpoint pagination. + */ +type GetUserEffectiveRolesParams = { + /** The maximum number of roles to return (up to 100). */ + take?: number; + /** Pagination token from the previous response's `next` field. Use this to retrieve the next page of results. */ + from?: string; +}; +/** + * Response type for fetching user effective roles with checkpoint pagination. + */ +type GetUserEffectiveRolesResponse = { + /** The list of user roles. */ + roles: Role[]; + /** A cursor for pagination. */ + next?: string; +}; +/** + * Response type for fetching user effective roles filtered by IDs or names. + */ +type FilteredUserEffectiveRolesResponse = { + /** The list of user roles. */ + roles: Role[]; +}; +type Role = { + /** Unique identifier of the role. */ + id: string; + /** Name of the Role. */ + name: string; + /** Type of the Role. */ + type: string; +}; +/** + * Describes the type of authentication factor (and its options) that can be used to + * challenge a user. + */ +type FactorSelector = + | { + /** A type of authentication factor such as `push-notification`, `phone`, `email`, `otp`, `webauthn-roaming`, `webauthn-platform`, and `recovery-code`. */ + type: 'otp' | 'email' | 'webauthn-platform' | 'webauthn-roaming' | 'recovery-code'; + /** Additional options for configuring a factor of a given type. */ + options?: { + [key: string]: unknown; + }; + } + | { + /** A type of authentication factor such as `phone`. */ + type: 'phone'; + /** Additional options for configuring the phone factor. */ + options?: { + /** The method passed in this field will be preferred over the others if available. */ + preferredMethod?: 'sms' | 'voice' | 'both'; + }; + } + | { + /** A type of authentication factor such as `push-notification`. */ + type: 'push' | 'push-notification'; + /** Additional options for configuring the push factor. */ + options?: { + /** If this is set to false, the OTP fallback method for the push factor will not be available for the user. */ + otpFallback?: boolean; + }; + }; +/** + * Describes the type of authentication factor (and its options) that can be used to + * enroll a user. + */ +type EnrollmentFactorSelector = + | { + /** A type of authentication factor such as `push-notification`, `phone`, `otp`, `webauthn-roaming`, `webauthn-platform`, and `recovery-code`. */ + type: + | 'otp' + | 'webauthn-platform' + | 'webauthn-roaming' + | 'push' + | 'push-notification' + | 'recovery-code'; + /** Additional options for configuring a factor of a given type. */ + options?: { + [key: string]: unknown; + }; + } + | { + /** A type of authentication factor such as `phone`. */ + type: 'phone'; + /** Additional options for configuring the phone factor. */ + options?: { + /** The method passed in this field will be preferred over the others if available. */ + preferredMethod?: 'sms' | 'voice' | 'both'; + }; + }; +type MultifactorProvider = 'none' | 'guardian' | 'google-authenticator' | 'duo' | 'any'; +interface DuoMultifactorProviderOptions { + /** This is the API hostname value from your Duo account. */ + host: string; + /** This is the Client ID (previously Integration key) value from your Duo account. */ + ikey: string; + /** This is the Client secret (previously Secret key) value from your Duo account. */ + skey: string; + /** Use some attribute of the profile as the username in DuoSecurity. This is also useful if you already have your users enrolled in Duo. */ + username?: string; +} +interface RequireMultifactorAuthOptions { + allowRememberBrowser?: boolean; + providerOptions?: DuoMultifactorProviderOptions; +} +interface ChallengeWithOptions { + additionalFactors?: FactorSelector[]; +} +interface EnrollWithOptions { + additionalFactors?: EnrollmentFactorSelector[]; +} +interface SessionRevocationOptions { + /** Default to false. If true, the system ends the session and keeps the refresh tokens. The application may continue to get access tokens for the duration of the refresh token lifetime. */ + preserveRefreshTokens?: boolean; +} +type SAMLAttributeValue = string | number | boolean | Array | null; +interface ValidationAPI { + /** + * Throw an error when there is a validation error. + * + * @param errorCode A customer defined error code for the validation error. + * + * @param errorMessage A customer defined message for the validation error. + */ + error(errorCode: string, errorMessage: string): PostLoginAPI; +} +interface Secrets { + [secretName: string]: string; +} +interface Configuration {} +interface Event extends PostLoginV3Event { + /** + * @private Configuration values associated with this Action. + */ + readonly configuration: Configuration; + /** + * Secret values securely associated with this Action. + */ + readonly secrets: Secrets; +} +interface AccessAPI { + /** + * Mark the current login attempt as denied. This will prevent the end-user from completing + * the login flow. This will *NOT* cancel other user-related side-effects (such as metadata + * changes) requested by this Action. The login flow will immediately stop following the + * completion of this action and no further Actions will be executed. + * + * @param reason A human-readable explanation for rejecting the login. This may be presented + * directly in end-user interfaces. + */ + deny(reason: string): PostLoginAPI; +} +interface AccessTokenAPI { + /** + * Set a custom claim on the Access Token that will be issued upon completion of the login flow. + * + * @param key Name of the claim (note that this may need to be a fully-qualified url). + * @param value The value of the claim. + */ + setCustomClaim(key: string, value: unknown): PostLoginAPI; + /** + * Add a scope on the Access Token that will be issued upon completion of the login flow. + * + * @param scope The scope to be added. + * @throws will throw an error if scope is invalid + */ + addScope(scope: string): void; + /** + * Remove a scope on the Access Token that will be issued upon completion of the login flow. + * + * @param scope The scope to be removed. + * @throws will throw an error if scope is invalid + */ + removeScope(scope: string): void; +} +interface AuthenticationAPI { + /** + * Request a challenge for multifactor authentication using the supplied factor and optional additional factors. + * + * When a multifactor challenge is requested, subsequent Actions will not be run until that challenge has been + * fulfilled by the user. A user will have satisfied the challenge in any of the following situations: + * + * 1. They successfully complete the challenge for the default factor. + * 2. They successfully complete the challenge for any of the optional factors described in `additionalFactors`. + * + * If any of the factors requested has already been challenged successfully in the current transaction, it will + * be ignored. + * + * If a factor is requested is not enabled on the tenant, it will be ignored. If a factor is requested that the user + * has not enrolled, it will be ignored. If none of the requested factors is enabled or enrolled, the authentication + * transaction will fail (i.e. login will not complete). + * + * _**Note**: This method will result in a factor challenge screen being shown if the user has not already satisfied + * the requirements of the challenge. If `additionalFactors` are supplied, the user will have the option to + * select another factor if they choose to._ + * + * @param factor An object describing the type of factor its options that should be used for the initial challenge. + * @param options Additional options which can also specify `additionalFactors` as a property. Factor-specific options (for example `otpFallback` for `push-notification`) belong on `factor.options`. + * + * @example + * Challenge with a specific factor: + * ```js + * api.authentication.challengeWith({ + * type: 'phone', + * options: { preferredMethod: 'both' } + * }); + * ``` + * + * @example + * Challenge with additional factors: + * ```js + * api.authentication.challengeWith({ + * type: 'otp' + * }, { + * additionalFactors: [{ + * type: 'push-notification' + * }, { + * type: 'phone' + * }] + * }); + * ``` + * + * @example + * Challenge with push notification and disable OTP fallback: + * ```js + * api.authentication.challengeWith({ + * type: 'push-notification', + * options: { otpFallback: false } + * }); + * ``` + */ + challengeWith(factor: FactorSelector, options?: ChallengeWithOptions): void; + /** + * Request a challenge for multifactor authentication using any of the supplied factors (showing a factor selection + * screen first). + * + * When a multifactor challenge is requested, subsequent Actions will not be run until that challenge has been + * fulfilled by the user. A user will have satisfied the challenge in any of the following situations: + * + * 1. They successfully complete the challenge for any of the factors. + * + * If any of the factors requested has already been challenged successfully in the current transaction, it will + * be ignored. + * + * If a factor is requested is not enabled on the tenant, it will be ignored. If a factor is requested that the user + * has not enrolled, it will be ignored. If none of the requested factors is enabled or enrolled, the authentication + * transaction will fail (i.e. login will not complete). + * + * _**Note**: This method will result in the factor selector screen being shown if the user has not already satisfied + * the requirements of the challenge. If there is a preferred factor, the `api.authentication.challengeWith()` method + * is preferred. The factor selector screen will not be shown if only one factor is passed in or is valid._ + * + * @param factors An array of factors. + */ + challengeWithAny(factors: FactorSelector[]): void; + /** + * Request an enrollment for multifactor authentication using the supplied factor and optional additional factors. + * + * When a multifactor enrollment is requested, subsequent Actions will not be run until that enrollment has been + * fulfilled by the user. + * + * If any of the factors requested has already been enrolled or challenged successfully in the current transaction, it will + * be ignored. + * + * If a factor that is not enabled in the tenant is requested, it will be ignored. + * If a factor that the user has already enrolled is requested, it will be ignored. + * If none of the requested factors is enabled and not enrolled, the authentication + * transaction will fail (i.e. login will not complete). + * + * @param factor An object describing the type of factor that should be used for the initial enrollment prompts and its options. + * @param options Additional options which can also specify `additionalFactors` as a property. + * + * @example + * Enroll with additional factors: + * ```js + * api.authentication.enrollWith({ + * type: 'otp' + * }, { + * additionalFactors: [{ + * type: 'push-notification' + * }, { + * type: 'phone' + * }] + * }); + * ``` + */ + enrollWith(factor: EnrollmentFactorSelector, options?: EnrollWithOptions): void; + /** + * + * Request an enrollment for multifactor authentication using any of the supplied factors (showing a factor selection + * screen first). + * + * When a multifactor enrollment is requested, subsequent Actions will not be run until that enrollment has been + * fulfilled by the user. + * + * If any of the factors requested has already been enrolled successfully in the current transaction, it will + * be ignored. + * + * If a factor that is not enabled in the tenant is requested, it will be ignored. + * If a factor that the user has already enrolled is requested, it will be ignored. + * If none of the requested factors is enabled and not enrolled, the authentication + * transaction will fail (i.e. login will not complete). + * + * _**Note**: If there is a preferred factor, the `api.authentication.enrollWith()` method + * is preferred. The factor selector screen will not be shown if only one factor is passed in or is valid._ + * + * @param factors An array of additional factors. + */ + enrollWithAny(factors: EnrollmentFactorSelector[]): void; + /** + * Indicate that a custom authentication method has been completed in the current + * session. This method will then be available in the `event.authentication.methods` + * array in subsequent logins. + * + * **IMPORTANT**: This API is only available from within the `onContinuePostLogin` + * function for `PostLogin` Actions. In other words, this may be used to record the + * completion of a custom authentication method after redirecting the user via + * `api.redirect.sendUserTo()`. + * + * @param provider_url An `http:` or `https:` URL that uniquely represents the completed + * authentication method. + */ + recordMethod(provider_url: string): PostLoginAPI; + /** + * Change the primary user for the login transaction. + * + * In scenarios that require linking users, the user identity used to initiate the login may no longer + * exist as a discrete user. That identity may now be a secondary identity of an existing user. In + * such situations, the `setPrimaryUser()` function can be used to indicate that the subject of the + * login should be changed. + * + * **IMPORTANT**: Insecurely linking accounts can allow malicious actors to access legitimate + * user accounts. + * + * **IMPORTANT**: The identity used to authenticate the login _must_ be among the secondary identities + * of the user referenced by `primary_user_id`. The login will fail and tokens will not be issued + * otherwise. + * + * @param primary_user_id The user ID of the user for whom tokens should be issued (the `sub` claim). + */ + setPrimaryUser(primary_user_id: string): void; +} +interface GroupsAPI { + /** + * Get the paginated list of the groups the user belongs to. + * + * @param params - An object containing pagination options. + * @param params.take - The number of groups to retrieve. + * @param params.from - The cursor for pagination. + */ + getUserGroups(params?: GetUserGroupsParams): Promise; + /** + * Checks if the user is a member of any of the specified groups and provides details + * about the matching groups if the user is a member. + * @param groups - An array of group identifiers (IDs or names) to check membership against. + */ + hasGroupMembership(groups: string[]): Promise; +} +interface IdTokenAPI { + /** + * Set a custom claim on the ID Token that will be issued upon completion of the login flow. + * + * @param key Name of the claim (note that this may need to be a fully-qualified url). + * @param value The value of the claim. + */ + setCustomClaim(key: string, value: unknown): PostLoginAPI; +} +interface EnableMultifactorOptions { + /** + * When provider is set to `google-authenticator` or `duo`, the user is prompted for MFA once + * every 30 days. When provider is set to `guardian`, the MFA prompt displays the enrollment + * checkbox for users to choose whether or not to enroll. Defaults to `false`. To learn more, + * read [Customize Multi-Factor Authentication Pages](https://auth0.com/docs/secure/multi-factor-authentication/customize-mfa). + */ + allowRememberBrowser?: boolean; + /** + * Additional options to configure the challenge, only available for the `duo` provider. + */ + providerOptions?: T extends 'duo' ? RequireMultifactorAuthOptions['providerOptions'] : never; +} +interface MultifactorAPI { + /** + * Enable multifactor authentication for this login flow. When enabled, users must complete the + * configured multifactor challenge. The actual multifactor challenge will be deferred to the + * end of the login flow. + * + * @param provider The name of the multifactor provider to use or the value `"any"` to use any + * of the configured providers. + * @param options Additional options for enabling multifactor challenges. + */ + enable( + provider: T, + options?: EnableMultifactorOptions + ): PostLoginAPI; +} +interface PromptAPI { + /** + * Renders a custom prompt. + * + * @param promptId The prompt ID. + * @param promptOptions The render options. + */ + render(promptId: RenderPromptId, promptOptions?: RenderPromptOptions): void; +} +interface TokenCreationOptions { + /** + * Number of seconds before this token will expire + * + * @default 900 15 minutes. + */ + expiresInSeconds?: number; + /** + * The data intended to be passed to the target of the redirect and whose authenticity + * and integrity must be provable. + */ + payload: { + [key: string]: unknown; + }; + /** + * A secret that will be used to sign a JWT that is shared with the redirect target. The + * secret value should be stored as a **secret** and retrieved using + * `event.secrets['']`. + */ + secret: string; +} +interface ValidateSessionTokenOptions { + secret: string; + /** + * The name of the query or body parameter that was sent to the /continue endpoint. + * + * @default 'session_token' + */ + tokenParameterName?: string; +} +interface SendUserToOptions { + /** + * An object representing additional query string parameters that should be appended to + * the redirect URL. + */ + query?: { + [param: string]: string; + }; +} +interface RedirectAPI { + /** + * Create a session token suitable for using as a query string parameter redirect target (via `sendUserTo`) + * that contains data whose authenticity must be provable by the target endpoint. The target endpoint + * can verify the authenticity and integrity of the data by checking the JWT's signature + * using a shared secret. + * + * The shared secret should be stored as a **secret** of the Action and will be readable at + * `event.secrets['']`. + * + * @param options Configure how sensitive data is encoded into the query parameters of the + * resulting url. + * + * @returns A JWT string. + */ + encodeToken(options: TokenCreationOptions): string; + /** + * Cause the login pipeline to trigger a browser redirect to the target `url` immediately after + * this action completes. The `createUrl` helper method is provided to simplify encoding + * data as a query parameter in the target `url` such that the data's authenticity and + * integrity can be verified by the target endpoint. + * + * @param baseUrl The url to which to redirect the user. + */ + sendUserTo(url: string, options?: SendUserToOptions): PostLoginAPI; + /** + * Indicates if the current transaction is eligibile for a user redirect. Certain protocols such + * as `oauth2-resource-owner`, `oauth2-refresh-token` do not support + * redirecting the user. A request with `prompt=none` is also not eligible for a redirect. + * + * @deprecated The `canRedirect` method should not be relied upon to determine whether a + * redirect is allowed or not in this flow. Instead, it is recommended that clients + * appropriately handle any `interaction_required` errors arising from a redirect requested + * in a non-interactive flow. + * + * @returns A boolean indicating if the current transaction is eligible for redirects. + */ + canRedirect(): boolean; + /** + * Retrieve the data encoded in a JWT token passed to the `/continue` endpoint while verifying + * the authenticity and integrity of that data. + * + * @param options Options for retrieving the data encoded in a JWT token passed to the + * `/continue` endpoint following a rediret. + * + * @returns The payload of the JWT token. + */ + validateToken(options: ValidateSessionTokenOptions): any; +} +interface RefreshTokenAPI { + /** + * [Enterprise Customers] Revoke the current user refresh token and mark the current refresh token exchange attempt as denied. This will prevent + * the end-user from completing the refresh token exchange flow and revoke the currently used refresh token. + * The refresh token exchange flow will immediately stop following the completion of this action and no further Actions will be executed. + * + * This method can be used only during Refresh Token Exchange flow, when `event.transaction.protocol === "oauth2-refresh-token"`. + * + * @param reason A human-readable explanation for rejecting the refresh token exchange. This may be presented + * directly in end-user interfaces. + */ + revoke(reason: string): void; + /** + * [Enterprise Customers] Sets a new absolute expiration time for the current refresh token. + * The expiration cannot be set higher than the maximum refresh token lifetime set in the settings. + * When called multiple times, the earliest expiration time will be used. + * + * @param absolute Required, the new absolute expiration time in milliseconds since the unix epoch, after which the Refresh Token will be considered invalid. + */ + setExpiresAt(absolute: number): void; + /** + * [Enterprise Customers] Sets a new idle expiration time for the current refresh token. + * The expiration cannot be set higher than the maximum absolute refresh token lifetime set in the settings. + * When called multiple times, the earliest expiration time will be used. + * + * @param inactivity Required, the new idle inactivity time in milliseconds since the unix epoch, after which the Refresh Token will be considered invalid + * if it is not used during this period. + */ + setIdleExpiresAt(inactivity: number): void; + /** + * Sets a key value pair in the metadata object of the current refresh token. + * + * @param key Required, the key to set in the metadata object. + * @param value Required, the value to set for the key in the metadata object, null values will delete the provided metadata key. + * @throws Will throw an error if the resulting metadata object is invalid. + */ + setMetadata(key: string, value: string | null): void; + /** + * Deletes a key in the metadata object of the current refresh token. + * + * @param key Required, the key to delete from the metadata object. + */ + deleteMetadata(key: string): void; + /** + * Deletes all keys from the metadata object of the current refresh token. + */ + evictMetadata(): void; +} +interface RolesAPI { + /** + * Returns all roles assigned to a user, directly or through group membership, + * optionally scoped to an organization, using checkpoint pagination. + * + * @param params - Checkpoint pagination parameters. + * @param params.take - The maximum number of roles to return (up to 100). + * @param params.from - Pagination token from the previous response's `next` field. + * @returns An object containing up to 100 effective roles and a pagination cursor + * + * @example + * Fetch the first page of roles: + * ```js + * const result = await api.roles.getUserEffectiveRoles({ take: 50 }); + * console.log(result.roles); + * ``` + * + * @example + * Paginate through roles: + * ```js + * let cursor; + * do { + * const result = await api.roles.getUserEffectiveRoles({ take: 100, from: cursor }); + * console.log(result.roles); + * cursor = result.next; + * } while (cursor); + * ``` + */ + getUserEffectiveRoles( + params?: GetUserEffectiveRolesParams + ): Promise; + /** + * Returns roles assigned to a user, directly or through group membership, + * optionally scoped to an organization, filtered by role IDs. + * + * @param ids - Array of role IDs to filter by (up to 100). + * @returns An object containing the filtered list of effective roles. + * + * @example + * Filter roles by specific IDs: + * ```js + * const result = await api.roles.getUserEffectiveRolesByIds([ + * 'rol_1234567890', + * 'rol_0987654321' + * ]); + * console.log(result.roles); + * ``` + */ + getUserEffectiveRolesByIds(ids: string[]): Promise; + /** + * Returns roles assigned to a user, directly or through group membership, + * optionally scoped to an organization, filtered by role names. + * + * @param names - Array of role names to filter by (up to 50). + * @returns An object containing the filtered list of effective roles. + * + * @example + * Filter roles by specific names: + * ```js + * const result = await api.roles.getUserEffectiveRolesByNames([ + * 'Admin', + * 'Editor' + * ]); + * console.log(result.roles); + * ``` + */ + getUserEffectiveRolesByNames(names: string[]): Promise; +} +interface RulesAPI { + /** + * Check whether a Rule with a specific ID has been executed in the current transaction. + * + * @param ruleId The Rule ID. + */ + wasExecuted(ruleId: string): boolean; +} +interface SAMLResponseAPI { + /** + * Set attributes on the SAML assertion being issued to the authenticated user. + * + * @param attribute The SAML attribute to be set. + * @param value The value of the SAML claim. Setting this value to `null` or + * `undefined` will remove the claim from the assertion. + */ + setAttribute(attribute: string, value: SAMLAttributeValue): void; + /** + * Audience of the SAML assertion. + * Default is issuer on SAMLRequest. + */ + setAudience(audience: string): void; + /** + * Recipient of the SAML assertion (SubjectConfirmationData). + * Default is AssertionConsumerUrl on SAMLRequest or callback URL if no SAMLRequest was sent. + */ + setRecipient(recipient: string): void; + /** + * Whether or not a UPN claim should be created. Default is true. + */ + setCreateUpnClaim(createUpnClaim: boolean): void; + /** + * If true (default), for each claim that is not mapped to the common profile, Auth0 passes through those in the output assertion. + * If false, those claims won't be mapped. + */ + setPassthroughClaimsWithNoMapping(passthroughClaimsWithNoMapping: boolean): void; + /** + * If passthroughClaimsWithNoMapping is true and this is false (default), for each claim not mapped to the common profile Auth0 adds a prefix `http://schema.auth0.com`. + * If true it will pass through the claim as-is. + */ + setMapUnknownClaimsAsIs(mapUnknownClaimsAsIs: boolean): void; + /** + * If true (default), it adds more information in the token such as the provider (Google, ADFS, AD, etc.) and the access token, if available. + */ + setMapIdentities(mapIdentities: boolean): void; + /** + * Signature algorithm to sign the SAML assertion or response. + * Default is rsa-sha256. + */ + setSignatureAlgorithm(signatureAlgorithm: 'rsa-sha256'): void; + /** + * @deprecated Use rsa-sha256 instead, rsa-sha1 is not recommended. + */ + setSignatureAlgorithm(signatureAlgorithm: 'rsa-sha1'): void; + /** + * Digest algorithm to calculate digest of the SAML assertion or response. + * Default is sha256. + */ + setDigestAlgorithm(digestAlgorithm: 'sha256'): void; + /** + * @deprecated Use 'sha256' instead, 'sha1' is not recommended. + */ + setDigestAlgorithm(digestAlgorithm: 'sha1'): void; + /** + * Destination of the SAML response. If not specified, it will be AssertionConsumerUrl of SAMLRequest or callback URL if there was no SAMLRequest. + */ + setDestination(destination: string): void; + /** + * Expiration of the token. + * Default is 3600 seconds (1 hour). + */ + setLifetimeInSeconds(lifetimeInSeconds: number): void; + /** + * Whether or not the SAML response should be signed. + * By default the SAML assertion will be signed, but not the SAML response. + * If true, SAML Response will be signed instead of SAML assertion. + * Default to false. + */ + setSignResponse(signResponse: boolean): void; + /** + * Default is urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified. + */ + setNameIdentifierFormat(nameIdentifierFormat: string): void; + /** + * Auth0 will try each of the attributes of this array in order. + * If one of them has a value, it will use that for the Subject/NameID. + * + * The order is: + * - http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier (mapped from user_id), + * - http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress (mapped from email), + * - http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name (mapped from name). + */ + setNameIdentifierProbes(nameIdentifierProbes: string[]): void; + /** + * Default is urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified. + */ + setAuthnContextClassRef(authnContextClassRef: string): void; + /** + * Optionally indicates the public key certificate used to validate SAML requests. + * If set, SAML requests will be required to be signed. + * A sample value would be "-----BEGIN CERTIFICATE-----\nMIIC8jCCAdqgAwIBAgIJObB6jmhG0QIEMA0GCSqGSIb3DQEBBQUAMCAxHjAcBgNV\n[..all the other lines..]-----END CERTIFICATE-----\n". + */ + setSigningCert(signingCert: string): void; + /** + * When set to true, we infer the NameFormat based on the attribute name. NameFormat values are urn:oasis:names:tc:SAML:2.0:attrname-format:uri, urn:oasis:names:tc:SAML:2.0:attrname-format:basic and urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified. + * If set to false, the attribute NameFormat is not set in the assertion. + * Default is true. + */ + setIncludeAttributeNameFormat(includeAttributeNameFormat: boolean): void; + /** + * When set to true, we infer the xs:type of the element. Types are xs:string, xs:boolean, xs:double and xs:anyType. + * When set to false all xs:type are xs:anyType. + * Default is true. + */ + setTypedAttributes(typedAttributes: boolean): void; + /** + * Optionally specify a certificate used to encrypt the SAML assertion. + * The certificate should be obtained from the service provider. + * Both the certificate and public key must be specified. + * A sample value would be "-----BEGIN CERTIFICATE-----\nMIIC8jCCAdqgAwIBAgIJObB6jmhG0QIEMA0GCSqGSIb3DQEBBQUAMCAxHjAcBgNV\n[..all the other lines..]-----END CERTIFICATE-----\n". + */ + setEncryptionCert(encryptionCert: string): void; + /** + * Optionally specify a public key used to encrypt the SAML assertion. + * The public key should be obtained from the service provider. + * Both the public key and certificate must be specified. + * A sample value would be "-----BEGIN PUBLIC KEY-----\nnMIIC8jCCAdqgAwIBAgIJObB6jmhG0QIEMA0GCSqGSIb3DQEBBQUAMCAxHjAcBgNV\n[..all the other lines..]-----END PUBLIC KEY-----\n". + */ + setEncryptionPublicKey(encryptionPublicKey: string): void; + /** + * By default, Auth0 will use the private/public key pair assigned to your tenant to sign SAML responses or assertions. + * For very specific scenarios, you might wish to provide your own certificate and private key. + * + * Both the certificate and private key must be specified. + * A sample value would be "-----BEGIN CERTIFICATE-----\nMIIC8jCCAdqgAwIBAgIJObB6jmhG0QIEMA0GCSqGSIb3DQEBBQUAMCAxHjAcBgNV\n[..all the other lines..]-----END CERTIFICATE-----\n". + */ + setCert(cert: string): void; + /** + * By default, Auth0 will use the private/public key pair assigned to your tenant to sign SAML responses or assertions. + * For very specific scenarios, you might wish to provide your own certificate and private key. + * + * Since this private key is sensitive, **we recommend using the Add Secret functionality of Actions**. + * See here for more details: https://auth0.com/docs/customize/actions/write-your-first-action#add-a-secret + * + * Both the certificate and private key must be specified. + * A sample value would be "-----BEGIN PRIVATE KEY-----\nnMIIC8jCCAdqgAwIBAgIJObB6jmhG0QIEMA0GCSqGSIb3DQEBBQUAMCAxHjAcBgNV\n[..all the other lines..]-----END PRIVATE KEY-----\n". + */ + setKey(key: string): void; + /** + * Optionally specify a RelayState used to return to service provider + */ + setRelayState(relayState: string): void; + /** + * Optionally specify the issuer of the SAML assertion. + * Default is urn:auth0:TENANT + */ + setIssuer(issuer: string): void; + /** + * Set encryption algorithm for SAML assertion. + * Default is aes256-cbc. + * + * @param encryptionAlgorithm - The algorithm to use (aes256-gcm is recommended) + * + * @example + * Set the encryption algorithm to aes256-gcm (recommended) + * ```js + * api.samlResponse.setEncryptionAlgorithm('aes256-gcm'); + * ``` + */ + setEncryptionAlgorithm(encryptionAlgorithm: 'aes256-gcm'): void; + /** + * @deprecated Use 'aes256-gcm' instead, 'aes256-cbc' is not recommended and insecure. + * @param encryptionAlgorithm - The algorithm to use (aes256-cbc is deprecated) + * + * @example + * Set encryption algorithm to aes256-cbc (not recommended) + * ```js + * api.samlResponse.setEncryptionAlgorithm('aes256-cbc'); + * ``` + */ + setEncryptionAlgorithm(encryptionAlgorithm: 'aes256-cbc'): void; +} +interface SessionAPI { + /** + * [Enterprise Customers] Revoke the current user session and mark the current login attempt as denied. This will prevent + * the end-user from completing the login flow and revoke their session. The login flow will immediately + * stop following the completion of this action and no further Actions will be executed. + * + * @param reason A human-readable explanation for rejecting the login. This may be presented + * directly in end-user interfaces. + * + * @param options + * + * @example + * Revoke the session while preserving refresh tokens: + * ```js + * api.session.revoke('reason', { preserveRefreshTokens: true }); + * ``` + */ + revoke(reason: string, options?: SessionRevocationOptions): void; + /** + * [Enterprise Customers] Sets a new absolute expiration time for the current session. + * The expiration cannot be set higher than the maximum session lifetime set in the tenant settings. + * When called multiple times, the earliest expiration time will be used. + * + * @param absolute Required, the new absolute expiration time in milliseconds since the unix epoch, after which the Session will be considered invalid. + */ + setExpiresAt(absolute: number): void; + /** + * [Enterprise Customers] Sets a new idle expiration time for the current session. + * The expiration cannot be set higher than the maximum absolute session lifetime set in the tenant settings. + * When called multiple times, the earliest expiration time will be used. + * + * @param inactivity Required, the new inactivity expiration time in milliseconds since the unix epoch, after which the Session will be considered invalid if there + * is no user interaction during this period. + */ + setIdleExpiresAt(inactivity: number): void; + /** + * [Enterprise Customers] [Early Access] Sets the cookie mode for the current session, allowing it to be either 'persistent' or 'non-persistent' (ephemeral). + * This determines how the session cookie is handled in the browser: + * - 'persistent': The cookie will be stored until it expires or is deleted by the user. + * - 'non-persistent' (ephemeral): The cookie will be deleted when the browser is closed. + * + * If multiple setCookieMode invocations are made, only the last one will take effect. In case 'non-persistent' is set, the cookie will be deleted when the browser is closed, however, the session itself will remain valid until its absolute or idle expiration time is reached + * or the session is revoked through our available APIs. For more information on cookie modes, please refer to our documentation. + * + * @param mode Required, the cookie mode for the current session. + * Can be either 'persistent' or 'non-persistent' (ephemeral). + */ + setCookieMode(mode: 'persistent' | 'non-persistent'): void; + /** + * [Enterprise Customers] [Early Access] Sets a key value pair in the metadata object of the current session. + * + * @param key Required, the key to set in the metadata object. + * @param value Required, the value to set for the key in the metadata object, null values will delete the provided metadata key. + * @throws will throw an error if the resulting metadata object is invalid. + */ + setMetadata(key: string, value: string | null): void; + /** + * [Enterprise Customers] [Early Access] Deletes a key in the metadata object of the current session. + * + * @param key Required, the key to delete from the metadata object. + */ + deleteMetadata(key: string): void; + /** + * [Enterprise Customers] [Early Access] Deletes all keys from the metadata object of the current session. + * + */ + evictMetadata(): void; +} +interface TransactionAPI { + /** + * 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. + */ + setMetadata(key: string, value: TxMetadataValue | null): void; +} +interface UserAPI { + /** + * Set application-specific metadata for the user that is logging in. + * + * Note: This method should not be used in callbacks. Invoking this method won't update the metadata immediately. + * You can call this several times throughout multiple actions of the same flow and the engine will aggregate the + * changes and update the metadata at once before the flow is completed. This function works only with metadata that + * are in the object format. + * + * @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. + */ + setAppMetadata(key: string, value: unknown): PostLoginAPI; + /** + * Set general metadata for the user that is logging in. + * + * Note: This method should not be used in callbacks. Invoking this method won't update the metadata immediately. + * You can call this several times throughout multiple actions of the same flow and the engine will aggregate the + * changes and update the metadata at once before the flow is completed. This function works only with metadata that + * are in the object format. + * + * @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. + */ + setUserMetadata(key: string, value: unknown): PostLoginAPI; +} +/** + * Methods and utilities to help change the behavior of the login flow. + */ +interface PostLoginAPI { + /** + * Modify the access of the user that is logging in, such as rejecting the login attempt. + */ + readonly access: AccessAPI; + /** + * Request changes to the access token being issued. + */ + readonly accessToken: AccessTokenAPI; + /** + * Request changes to the authentication state of the current user's session. + */ + readonly authentication: AuthenticationAPI; + /** + * Request changes to the ID token being issued. + */ + readonly idToken: IdTokenAPI; + /** + * Set or remove the requirement for multifactor authentication on the login attempt. + */ + readonly multifactor: MultifactorAPI; + /** + * Configure and initiate external redirects. + */ + readonly redirect: RedirectAPI; + /** + * Make changes to the metadata of the user that is logging in. + */ + readonly user: UserAPI; + /** + * Make changes to the cache. + */ + readonly cache: CacheAPI; + /** + * Configure custom SAML configurations and attributes. + */ + readonly samlResponse: SAMLResponseAPI; + /** + * Prevent user from logging in by throwing a validation error. + */ + readonly validation: ValidationAPI; + /** + * Identify if a rule has been executed in the current transaction. + */ + readonly rules: RulesAPI; + /** + * Renders a custom prompt. + */ + readonly prompt: PromptAPI; + /** + * Request changes to the current user's refresh token. + */ + readonly refreshToken: RefreshTokenAPI; + /** + * Request changes to the current user's session. + */ + readonly session: SessionAPI; + /** + * Make changes to the transaction. + */ + readonly transaction: TransactionAPI; + /** + * Get information about user groups membership. + */ + readonly groups: GroupsAPI; + /** + * Get information about user roles assignments. + */ + readonly roles: RolesAPI; +} +interface PostLoginAction { + (event: Event, api: PostLoginAPI): Promise; +} +type PostLoginModule = { + onExecutePostLogin: PostLoginAction; + onContinuePostLogin: PostLoginAction; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link PostLoginTriggerAPI} and rules context. The event is cloned per call so mutations in one + * execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters; +/** Loads a PostLogin v3 action file for use in tests, e.g. `action.execute('onExecutePostLogin', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/post-login/v3/test/index.js b/src/post-login/v3/test/index.js new file mode 100644 index 0000000..5233b80 --- /dev/null +++ b/src/post-login/v3/test/index.js @@ -0,0 +1,1445 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +var metadata = require('../../../_shared/Dygdkb3A.js'); +var targetScopes = require('../../../_shared/Bi7NRjgy.js'); +var index = require('../../../_shared/DvTaCl9e.js'); +var url = require('url'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); + +/** + * No-op {@link PostLoginTriggerAPI} for use in mock API implementations. + */ +class PostLoginTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + #transactionMetadataAPI = metadata.createNoopTransactionMetadataAPI(); + #targetScopesAPI; + constructor(initialTargetScopes = []) { + this.#targetScopesAPI = targetScopes.createNoopTargetScopesAPI(initialTargetScopes); + } + getCacheAPI() { + return this.#cacheAPI; + } + getTransactionMetadataAPI() { + return this.#transactionMetadataAPI; + } + getTargetScopesAPI() { + return this.#targetScopesAPI; + } + getUserGroups(_userId, _params) { + return Promise.resolve({ groups: [] }); + } + hasGroupMembership(_userId, _connectionId, _organizationId, _groups) { + return Promise.resolve({ memberships: {} }); + } + getUserEffectiveRoles(_userId, _organizationId, _params) { + return Promise.resolve({ roles: [] }); + } + getUserEffectiveRolesByIds(_userId, _organizationId, _ids) { + return Promise.resolve({ roles: [] }); + } + getUserEffectiveRolesByNames(_userId, _organizationId, _names) { + return Promise.resolve({ roles: [] }); + } + denyAccess(_reason) { } + setCustomClaim(_target, _key, _value) { } + modifyScope(_target, _modifications) { } + challengeWith(_factor, _options) { } + challengeWithAny(_factors) { } + enrollWith(_factor, _options) { } + enrollWithAny(_factors) { } + recordMethod(_providerUrl) { } + hasSetPrimaryUser() { + return false; + } + setPrimaryUser(_primaryUserId) { } + requireMultifactorAuth(_provider, _options) { } + setMetadata(_target, _key, _value) { } + renderPrompt(_promptId, _promptOptions) { } + sendUserTo(_url) { } + hasRedirectPrompt() { + return false; + } + hasRenderPrompt() { + return false; + } + encodeToken(_options) { + return ''; + } + validateToken(_options) { + return { valid: false, reason: '' }; + } + revokeRefreshToken(_reason) { } + setRefreshTokenExpiration(_inactivity, _absolute) { } + setRefreshTokenMetadata(_metadata) { } + revokeSession(_reason, _options) { } + setSessionExpiration(_inactivity, _absolute) { } + setSessionMetadata(_metadata) { } + setCookieMode(_mode) { } + getSAMLAttributes() { + return {}; + } + setSAMLAttribute(_attribute, _value) { } + setSAMLConfiguration(_configs) { } + validationError(_errorCode, _errorMessage) { } + abort(_message) { } +} + +const event = { + transaction: { + id: 'nZniEmn3ejOpcV46_ac1CxJCuO_gAuwR', + acr_values: [], + locale: 'en', + requested_scopes: [], + ui_locales: ['en'], + protocol: 'oidc-basic-profile', + redirect_uri: 'http://someuri.com', + prompt: ['none'], + login_hint: 'test@test.com', + response_mode: 'form_post', + response_type: ['id_token'], + state: 'AABBccddEEFFGGTTasrs', + requested_authorization_details: [ + { + type: 'foo', + }, + ], + linking_id: 'abc_dynamic_linking_id_123', + correlation_id: 'abcefg123', + subject_token_type: 'http://acme.com/subject-token', + actor: { + sub: 'actor-sub', + act: { + sub: 'actor-sub-level-2', + act: { + sub: 'actor-sub-level-3', + act: { + sub: 'actor-sub-level-4', + act: { + sub: 'actor-sub-level-5', + }, + }, + }, + }, + }, + actor_token_type: 'http://my-idp/id-token', + metadata: {}, + }, + agent: { + agent_id: 'agt_abc123', + name: 'My Agent', + agent_metadata: { + env: 'production', + }, + }, + anonymous_session: { + user_id: 'anon@5bdbd57d-cb7a-4cbb-b938-c0499a31ad52', + session_id: 'anon_sess@f727e268-46da-4c7d-ad57-b8c80318c295', + created_at: '2024-01-01T00:00:00.000Z', + expires_at: '2024-01-01T01:00:00.000Z', + metadata: { + source: 'web', + }, + }, + authentication: { + methods: [ + { + name: 'mfa', + timestamp: '2018-11-13T20:20:39+00:00', + type: 'email', + }, + { + name: 'passkey', + timestamp: '2018-11-13T20:22:13+00:00', + }, + ], + riskAssessment: { + confidence: 'low', + version: '1', + assessments: { + UntrustedIP: { + confidence: 'low', + code: 'found_on_deny_list', + details: { + ip: '1.1.1.1', + matches: '1.1.1.1/32', + source: 'STOPFORUMSPAM-1', + category: 'abuse', + }, + }, + NewDevice: { + confidence: 'low', + code: 'no_match', + details: { + device: 'unknown', + useragent: 'unknown', + }, + }, + ImpossibleTravel: { + confidence: 'low', + code: 'impossible_travel_from_last_login', + }, + AgentDetection: { + code: 'verified_agent', + confidence: 'neutral', + details: { + provider: 'openai', + }, + }, + }, + supplemental: { + akamai: { + akamaiBot: { + type: 'Akamai-Categorized Bot', + botnetId: 'googlebot', + action: 'Monitor', + botCategory: ['Web Search Engine Bots', 'Search Engine Bots'], + botScore: 0, + botScoreResponseSegment: 'test', + }, + akamaiUserRisk: { + uuid: '86b37525-8047-4a3c-8d7a-23e99901da05', + username: 'testuser@example.com', + emailDomain: 'example.com', + ouid: 'm534264', + requestid: '19e22e', + status: 4, + score: 0, + general: { + aci: '0', + db: 'Chrome 85', + di: '0fc91b5ec42f5a471c16a85e3e388ca57697c1a9', + do: 'Mac OS X 10', + }, + risk: { + ugp: 'ie/M', + unp: '432/H', + }, + trust: { + udbp: 'Chrome85', + udfp: '25ba44ec3b391ba4ce5fbbd2979635e254775e7d', + udop: 'Mac OS X 10', + ugp: 'FR', + unp: '12322', + utp: 'weekday_3', + }, + allow: 0, + action: 'monitor', + }, + }, + }, + }, + }, + authorization: { + roles: [], + }, + connection: { + id: 'con_fpe5kj482KO1eOzQ', + name: 'Username-Password-Authentication', + strategy: 'auth0', + metadata: {}, + }, + custom_domain: { + domain: 'login.{{YOUR_DOMAIN}}.com', + domain_metadata: { + environment: 'production', + region: 'us-west-2', + }, + }, + experiment: { + experiment_id: 'exp_abc123', + variation_id: 'var_abc123', + variation_name: 'Treatment', + variation_description: 'New login experience', + config: { + theme: { + value: 'dark', + }, + max_attempts: { + value: 5, + }, + feature_enabled: { + value: true, + }, + }, + is_control: false, + }, + organization: { + display_name: 'My Organization', + id: 'org_juG7cAQ0CymOcVpV', + metadata: {}, + name: 'my-organization', + }, + prompt: { + id: 'prompt_1234567890', + vars: {}, + fields: {}, + }, + resource_server: { + identifier: '{{TENANT}}.auth0.com/api/v2', + }, + tenant: { + id: '{{TENANT}}', + }, + secrets: {}, + session: { + id: 'sess_123fake', + device: { + initial_asn: 'AS13322', + last_asn: 'AS13335', + initial_ip: '0.0.0.1', + last_ip: '0.0.0.0', + initial_user_agent: 'sample', + last_user_agent: 'sample', + }, + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + created_at: '{{DATE}}', + updated_at: '{{DATE}}', + authenticated_at: '{{DATE}}', + idle_expires_at: '{{DATE}}', + expires_at: '{{DATE}}', + last_interacted_at: '{{DATE}}', + clients: [ + { + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + }, + ], + cookie: { + mode: 'persistent', + }, + session_transfer: { + parent_refresh_token: { + id: 'rt_456parent-fake', + metadata: { + foo: 'bar', + fizz: 'buzz', + }, + }, + }, + metadata: { + foo: 'bar', + fizz: 'buzz', + }, + actor: { + sub: 'auth0|actor', + }, + }, + session_transfer_token: { + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + scope: ['sample-scope'], + request: { + ip: '0.0.0.1', + asn: 'AS13322', + user_agent: 'sample-user-agent', + geoip: { + cityName: 'Bellevue', + continentCode: 'NA', + countryCode3: 'USA', + countryCode: 'US', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + }, + }, + refresh_token: { + id: 'rt_123fake', + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + created_at: '{{DATE}}', + expires_at: '{{DATE}}', + idle_expires_at: '{{DATE}}', + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + session_id: 'sess_123fake', + rotating: true, + resource_servers: [ + { + audience: '{{TENANT}}.auth0.com', + scopes: 'scope', + }, + ], + device: { + initial_asn: 'AS13322', + initial_ip: '0.0.0.1', + initial_user_agent: 'sample-user-agent', + last_asn: 'AS13335', + last_ip: '0.0.0.0', + last_user_agent: 'sample-user-agent', + }, + last_exchanged_at: '{{DATE}}', + session_transfer: { + parent_refresh_token: { + id: 'rt_456parent-fake', + }, + }, + metadata: { + foo: 'bar', + fizz: 'buzz', + }, + access: 'offline', + }, + configuration: {}, + client: { + authentication: { + type: 'self_signed_tls_client_auth', + certificate: { + raw: '-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----', + subject: 'CN=client.example.com', + subjectAltName: 'DNS:client.example.com, IP:127.0.0.1', + thumbprint256: 'qrvM3e7_ABEiM0RVZneImaq7zN3u_wARIjNEVWZ3iJk', + }, + }, + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + external_client_id: 'https://example.com/oauth-client.json', + name: 'All Applications', + external_metadata_type: 'cimd', + metadata: {}, + refresh_token: { + policies: [ + { + audience: 'https://my-test-api', + scope: ['read:notes'], + }, + ], + }, + }, + request: { + ip: '13.33.86.47', + asn: 'AS13335', + method: 'GET', + query: { + protocol: 'oauth2', + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + response_type: 'code', + connection: 'Username-Password-Authentication', + prompt: 'login', + scope: 'openid profile', + redirect_uri: 'https://example/tester/callback?connection=Username-Password-Authentication', + }, + body: {}, + geoip: { + cityName: 'Bellevue', + continentCode: 'NA', + countryCode3: 'USA', + countryCode: 'US', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + hostname: '{{TENANT}}.auth0.com', + language: 'en', + user_agent: 'curl/7.64.1', + }, + stats: { + logins_count: 62, + }, + user: { + app_metadata: {}, + created_at: '{{DATE}}', + email_verified: true, + email: 'j+smith@example.com', + family_name: 'Smith', + given_name: 'John', + identities: [ + { + connection: 'Username-Password-Authentication', + isSocial: false, + provider: 'auth0', + userId: '5f7c8ec7c33c6c004bbafe82', + accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gU21pdGgiLCJpYXQiOjE1MTYyMzkwMjJ9.Q_w2AVguPRU2KskCXwR7ZHl09TQXEntfEA8Jj2_Jyew', + profileData: {}, + user_id: '5f7c8ec7c33c6c004bbafe82', + }, + ], + last_password_reset: '{{DATE}}', + name: 'j+smith@example.com', + nickname: 'j+smith', + phone_number: '18882352699', + phone_verified: false, + picture: 'http://www.gravatar.com/avatar/?d=identicon', + updated_at: '{{DATE}}', + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + user_metadata: {}, + username: 'jsmith', + multifactor: [], + enrolledFactors: [], + }, + security_context: { + ja3: 'c13a3e168d43e62e0ad96fae6347e2e4', + ja4: 't13d1516h2_8daaf6152771_02713d6af862', + }, +}; + +class ValidationAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + error(errorCode, errorMessage) { + index.validate(index.PostLoginValidationErrorInputCodec, { errorCode, errorMessage }); + this.#triggerAPI.validationError(errorCode, errorMessage); + return this.#api; + } +} + +class AccessAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + deny(reason) { + this.#triggerAPI.denyAccess(reason); + return this.#api; + } +} + +class AccessTokenAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + setCustomClaim(key, value) { + index.helpers.customClaim.assertSettableCustomClaim(key); + this.#triggerAPI.setCustomClaim('accessToken', key, value); + return this.#api; + } + addScope(scope) { + const modifyScope = index.helpers.accessToken.addScope(scope); + this.#triggerAPI.modifyScope(modifyScope.target, modifyScope.modifications); + } + removeScope(scope) { + const modifyScope = index.helpers.accessToken.removeScope(scope); + this.#triggerAPI.modifyScope(modifyScope.target, modifyScope.modifications); + } +} + +class AuthenticationAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + enrollWith(factor, options) { + this.#triggerAPI.enrollWith(factor, options); + } + enrollWithAny(factors) { + this.#triggerAPI.enrollWithAny(factors); + } + challengeWithAny(factors) { + this.#triggerAPI.challengeWithAny(factors); + } + challengeWith(factor, options) { + this.#triggerAPI.challengeWith(factor, options); + } + recordMethod(provider_url) { + this.#triggerAPI.recordMethod(provider_url); + return this.#api; + } + setPrimaryUser(primary_user_id) { + // We only want to allow for a single primaryUser change per execution + // across both Rules and Actions. We do a preliminary check here, but + // validation of this limit ultimately needs to be done in auth0-server. + if (this.#triggerAPI.hasSetPrimaryUser()) { + throw new Error('The primary user can only be altered once per transaction'); + } + if (typeof primary_user_id !== 'string' || primary_user_id.length === 0) { + throw new Error('The provided primary_user_id must be a non-empty string'); + } + if (primary_user_id.length > index.MAX_USER_ID_LENGTH) { + throw new Error('The provided primary_user_id exceeds the maximum length.'); + } + this.#triggerAPI.setPrimaryUser(primary_user_id); + } +} + +class GroupsAPIImpl { + #triggerAPI; + #event; + constructor(triggerAPI, event) { + this.#triggerAPI = triggerAPI; + this.#event = event; + } + async getUserGroups({ from, take } = {}) { + if (from !== undefined && typeof from !== 'string') { + throw new Error('The "from" parameter must be a string if provided.'); + } + if (take !== undefined && (typeof take !== 'number' || !Number.isInteger(take) || take <= 0)) { + throw new Error('The "take" parameter must be a positive integer if provided.'); + } + return this.#triggerAPI.getUserGroups(this.#event.user.user_id, { from, take }); + } + async hasGroupMembership(groups) { + if (!groups || !Array.isArray(groups) || !groups.length) { + throw new Error('The "groups" parameter must be a non-empty array of strings.'); + } + return this.#triggerAPI.hasGroupMembership(this.#event.user.user_id, this.#event.connection?.id, this.#event.organization?.id, groups); + } +} + +class IdTokenAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + setCustomClaim(key, value) { + index.helpers.customClaim.assertSettableCustomClaim(key); + this.#triggerAPI.setCustomClaim('idToken', key, value); + return this.#api; + } +} + +class MultifactorAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + enable(provider, options) { + this.#triggerAPI.requireMultifactorAuth(provider, { + allowRememberBrowser: options?.allowRememberBrowser, + providerOptions: options?.providerOptions, + }); + return this.#api; + } +} + +class PromptAPIImpl { + #triggerAPI; + constructor(triggerAPI) { + this.#triggerAPI = triggerAPI; + } + render(promptId, promptOptions) { + if (this.#triggerAPI.hasRedirectPrompt()) { + this.#triggerAPI.abort('Redirect and render commands cannot be used in the same action.'); + return; + } + if (this.#triggerAPI.hasRenderPrompt()) { + this.#triggerAPI.abort('Only 1 render command per action is allowed.'); + return; + } + const resolvedPromptOptions = promptOptions || {}; + index.validate(index.PostLoginRenderPromptInputCodec, { + promptId, + promptOptions: resolvedPromptOptions, + }); + if (Buffer.byteLength(JSON.stringify(resolvedPromptOptions), 'utf-8') > + index.MAX_RENDER_PROMPT_OPTIONS_BYTES) { + throw new Error(`The total size of the prompt options exceeds the limit of ${index.MAX_RENDER_PROMPT_OPTIONS_BYTES} bytes.`); + } + this.#triggerAPI.renderPrompt(promptId, resolvedPromptOptions); + } +} + +const DEFAULT_SESSION_TOKEN_EXP = 60 * 15; // 15 minutes +const DEFAULT_SESSION_TOKEN_QUERY_PARAM = 'session_token'; +const NO_REDIRECT_PROTOCOLS = [ + 'oauth2-resource-owner', + 'oauth2-refresh-token', + 'oauth2-password', + 'oauth2-webauthn', +]; +class RedirectAPIImpl { + #getContinueParams = (request) => { + const queryParams = (request && request.query) || {}; + const bodyParams = (request && request.body) || {}; + return { + ...queryParams, + ...bodyParams, + }; + }; + #api; + #triggerAPI; + #event; + constructor(triggerAPI, api, event) { + this.#api = api; + this.#triggerAPI = triggerAPI; + this.#event = event; + } + encodeToken(options) { + const issuedAt = Math.floor(Date.now() / 1000); + const payload = { + iat: issuedAt, + iss: this.#event.request.hostname, + sub: this.#event.user.user_id, + exp: issuedAt + (options.expiresInSeconds ?? DEFAULT_SESSION_TOKEN_EXP), + ip: this.#event.request.ip, + ...options.payload, + }; + return this.#triggerAPI.encodeToken({ + payload, + secret: options.secret, + }); + } + sendUserTo(baseUrl, urlOptions) { + if (this.#triggerAPI.hasRenderPrompt()) { + this.#triggerAPI.abort('Redirect and render commands cannot be used in the same action.'); + return this.#api; + } + const url$1 = new url.URL(baseUrl); + if (urlOptions?.query) { + for (const param in urlOptions.query) { + url$1.searchParams.set(param, urlOptions.query[param]); + } + } + this.#triggerAPI.sendUserTo(url$1.href); + return this.#api; + } + canRedirect() { + const queryParams = this.#event?.request?.query ?? {}; + // If prompt is none, this will throw an interaction needed error. + if (queryParams.prompt === 'none') { + return false; + } + return !NO_REDIRECT_PROTOCOLS.includes((this.#event.transaction && this.#event.transaction.protocol) || 'unknown-protocol'); + } + validateToken(options) { + options.tokenParameterName ||= DEFAULT_SESSION_TOKEN_QUERY_PARAM; + const params = this.#getContinueParams(this.#event.request); + const token = params[options.tokenParameterName]; + if (!token) { + throw new Error(`There is no parameter called '${options.tokenParameterName}' available in either the POST body or query string.`); + } + const response = this.#triggerAPI.validateToken({ + token: token, + secret: options.secret, + issuer: this.#event.request.hostname, + expectedState: params.state, + expectedSub: this.#event.user.user_id, + }); + if (!response.valid) { + throw new Error(`The session token is invalid: ${response.reason}`); + } + return response.payload; + } +} + +// This file is copied from auth0-sessions-lib due to restrictions in package installation +// Please make sure to keep it in sync with the original file while a permanent solution is found +// Note that some modifications exist due to typing +// In auth0-sessions-lib see: https://github.com/atko-cic/auth0-sessions-lib//tree/master/src/sessions/validators/ServerSessionValidator.ts +class RefreshTokenValidator { + MAX_METADATA_KEY_LENGTH = 255; + MAX_METADATA_VALUE_LENGTH = 255; + MAX_METADATA_KEY_COUNT = 25; + METADATA_KEY_REGEX = new RegExp(`^[a-zA-Z0-9_-]{1,${this.MAX_METADATA_KEY_LENGTH}}$`); + constructor() { } + validateMetadata(metadata) { + if (metadata === null || metadata === undefined) { + return { valid: true }; + } + // Type validation + if (typeof metadata !== 'object' || Array.isArray(metadata)) { + return { valid: false, error: 'Metadata must be an object' }; + } + // Key count validation + if (Object.entries(metadata).length > this.MAX_METADATA_KEY_COUNT) { + return { valid: false, error: 'Metadata must not contain more than 25 entries' }; + } + for (const [key, value] of Object.entries(metadata)) { + // Key length validation + if (key.length > this.MAX_METADATA_KEY_LENGTH) { + return { + valid: false, + error: `All metadata keys must be at most ${this.MAX_METADATA_KEY_LENGTH} characters long`, + }; + } + // Key null byte validation + if (key.includes('\x00')) { + return { + valid: false, + error: 'Metadata keys must not contain null bytes (\x00)', + }; + } + // Key character validation + if (!this.METADATA_KEY_REGEX.test(key)) { + return { + valid: false, + error: 'Metadata keys may only include letters, numbers, underscores, or hyphens', + }; + } + // Value length validation + if (value.length > this.MAX_METADATA_VALUE_LENGTH) { + return { + valid: false, + error: `All metadata values must be at most ${this.MAX_METADATA_VALUE_LENGTH} characters long`, + }; + } + // Value null byte validation + if (value.includes('\x00')) { + return { + valid: false, + error: 'Metadata values must not contain null bytes (\x00)', + }; + } + } + return { valid: true }; + } +} + +class RefreshTokenAPIImpl { + #triggerAPI; + #event; + #refreshTokenMetadata; + #refreshTokenMetadataValidator; + /** + * Initialises a new instance of the class. + * @param triggerAPI The trigger API used to process the commands. + * @param event The event object. + */ + constructor(triggerAPI, event) { + this.#triggerAPI = triggerAPI; + this.#event = event; + this.#refreshTokenMetadataValidator = new RefreshTokenValidator(); + // unlike session, refresh token metadata operations ARE allowed without a refresh token (even if event.refresh_token is undefined) + // because metadata can be added before the refresh token is created + if (this.#event.refresh_token?.metadata) { + this.#refreshTokenMetadata = handler.deepClone(this.#event.refresh_token.metadata); + } + else { + this.#refreshTokenMetadata = {}; + } + } + /** + * @param method The name of the method being called, used in the error message. + */ + #throwIfOnlineRefreshToken(method) { + if (this.#event.refresh_token?.access === 'online') { + throw new Error(`Cannot call ${method} on an online refresh token.`); + } + } + /** + * Sets the new absolute expiration for the underlying entity. + * + * @param absolute Required, the new absolute expiration time after which the entity will be considered invalid. + */ + setExpiresAt(absolute) { + this.#throwIfOnlineRefreshToken('setExpiresAt'); + if (!absolute) { + throw new Error('Either inactivity or absolute expiration time must be provided'); + } + index.validate(index.PostLoginSetRefreshTokenExpirationInputCodec, { absolute }); + this.#triggerAPI.setRefreshTokenExpiration(undefined, absolute); + } + /** + * Sets the new inactivity expiration for the underlying entity. + * + * @param inactivity Required, the new inactivity expiration time after which the entity will be considered invalid + * if there is no user interaction during this period. + */ + setIdleExpiresAt(inactivity) { + this.#throwIfOnlineRefreshToken('setIdleExpiresAt'); + if (!inactivity) { + throw new Error('Either inactivity or absolute expiration time must be provided'); + } + index.validate(index.PostLoginSetRefreshTokenExpirationInputCodec, { inactivity }); + this.#triggerAPI.setRefreshTokenExpiration(inactivity, undefined); + } + revoke(reason) { + index.validate(index.PostLoginRevokeRefreshTokenInputCodec, { message: reason }); + this.#triggerAPI.revokeRefreshToken(reason); + } + setMetadata(key, value) { + try { + this.#throwIfOnlineRefreshToken('setMetadata'); + if (!this.#refreshTokenMetadata) { + throw new Error('Cannot set metadata on a refresh token that does not exist.'); + } + if (typeof key !== 'string') { + throw new Error('Invalid metadata: Metadata key must be a string'); + } + // Deletion path + if (value === null) { + if (!(key in this.#refreshTokenMetadata)) { + return; // Key does not exist, nothing to delete + } + delete this.#refreshTokenMetadata[key]; + // Continue validation / setting value + } + else { + if (typeof value !== 'string') { + throw new Error('Invalid metadata: Metadata value must be a string'); + } + this.#refreshTokenMetadata[key] = value; + const validationResult = this.#refreshTokenMetadataValidator.validateMetadata(this.#refreshTokenMetadata); + if (!validationResult.valid) { + throw new Error(`Invalid metadata: ${validationResult.error}`); + } + } + // Only update event.refresh_token.metadata if a refresh token already + // exists in the transaction. Creating event.refresh_token when it is + // undefined would cause downstream Actions checking + // `if (event.refresh_token)` to incorrectly believe a refresh token + // is present. + if (this.#event.refresh_token) { + this.#event.refresh_token.metadata = handler.deepClone(this.#refreshTokenMetadata); + } + // Always emit the command so metadata is persisted for when a refresh + // token is eventually created by the auth pipeline. + this.#triggerAPI.setRefreshTokenMetadata(this.#refreshTokenMetadata); + } + catch (e) { + const message = e instanceof Error ? e.message : String(e); + throw new Error(`Failed to set refresh token metadata: ${message}`); + } + } + deleteMetadata(key) { + this.#throwIfOnlineRefreshToken('deleteMetadata'); + if (!this.#refreshTokenMetadata || Object.keys(this.#refreshTokenMetadata).length === 0) { + return; + } + if (key in this.#refreshTokenMetadata) { + delete this.#refreshTokenMetadata[key]; + if (this.#event.refresh_token) { + this.#event.refresh_token.metadata = handler.deepClone(this.#refreshTokenMetadata); + } + this.#triggerAPI.setRefreshTokenMetadata(this.#refreshTokenMetadata); + } + } + evictMetadata() { + this.#throwIfOnlineRefreshToken('evictMetadata'); + if (!this.#refreshTokenMetadata || Object.keys(this.#refreshTokenMetadata).length === 0) { + return; + } + this.#refreshTokenMetadata = {}; + if (this.#event.refresh_token) { + this.#event.refresh_token.metadata = {}; + } + this.#triggerAPI.setRefreshTokenMetadata({}); + } +} + +class RolesAPIImpl { + #triggerAPI; + #event; + constructor(triggerAPI, event) { + this.#triggerAPI = triggerAPI; + this.#event = event; + } + async getUserEffectiveRoles({ from, take, } = {}) { + if (from !== undefined && typeof from !== 'string') { + throw new Error('The "from" parameter must be a string if provided.'); + } + if (take !== undefined && (typeof take !== 'number' || !Number.isInteger(take) || take <= 0)) { + throw new Error('The "take" parameter must be a positive integer if provided.'); + } + return this.#triggerAPI.getUserEffectiveRoles(this.#event.user.user_id, this.#event.organization?.id, { from, take }); + } + async getUserEffectiveRolesByIds(ids) { + if (!ids || !Array.isArray(ids) || !ids.length) { + throw new Error('The "ids" parameter must be a non-empty array of strings.'); + } + return this.#triggerAPI.getUserEffectiveRolesByIds(this.#event.user.user_id, this.#event.organization?.id, ids); + } + async getUserEffectiveRolesByNames(names) { + if (!names || !Array.isArray(names) || !names.length) { + throw new Error('The "names" parameter must be a non-empty array of strings.'); + } + return this.#triggerAPI.getUserEffectiveRolesByNames(this.#event.user.user_id, this.#event.organization?.id, names); + } +} + +class RulesAPIImpl { + #context; + constructor(context) { + this.#context = context; + } + wasExecuted(ruleId) { + return this.#context?.includes(ruleId) || false; + } +} + +class SAMLResponseAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + setAttribute(attribute, value) { + // Resolves to true for both 'null' and 'undefined' but normalizes to 'null'. + // Allows developer flexibility to use either in the SAML API. + value ??= null; + // Early validation of the serialized SAML attribute object size before validation by auth0-server + const samlAttributes = this.#triggerAPI.getSAMLAttributes(); + samlAttributes[attribute] = value; + if (Buffer.byteLength(JSON.stringify(samlAttributes)) > index.MAX_SAML_BYTES) { + throw new Error(`The total size of the SAML attributes exceeds the limit of ${index.MAX_SAML_BYTES} bytes.`); + } + index.validate(index.PostLoginSetSAMLAttributeInputCodec, { + attribute, + value, + }); + this.#triggerAPI.setSAMLAttribute(attribute, value); + return this.#api; + } + setAudience(audience) { + return this.#setConfig('audience', audience); + } + setRecipient(recipient) { + return this.#setConfig('recipient', recipient); + } + setCreateUpnClaim(createUpnClaim) { + return this.#setConfig('createUpnClaim', createUpnClaim); + } + setPassthroughClaimsWithNoMapping(passthroughClaimsWithNoMapping) { + return this.#setConfig('passthroughClaimsWithNoMapping', passthroughClaimsWithNoMapping); + } + setMapUnknownClaimsAsIs(mapUnknownClaimsAsIs) { + return this.#setConfig('mapUnknownClaimsAsIs', mapUnknownClaimsAsIs); + } + setMapIdentities(mapIdentities) { + return this.#setConfig('mapIdentities', mapIdentities); + } + setSignatureAlgorithm(signatureAlgorithm) { + return this.#setConfig('signatureAlgorithm', signatureAlgorithm); + } + setDigestAlgorithm(digestAlgorithm) { + return this.#setConfig('digestAlgorithm', digestAlgorithm); + } + setDestination(destination) { + return this.#setConfig('destination', destination); + } + setLifetimeInSeconds(lifetimeInSeconds) { + return this.#setConfig('lifetimeInSeconds', lifetimeInSeconds); + } + setSignResponse(signResponse) { + return this.#setConfig('signResponse', signResponse); + } + setNameIdentifierFormat(nameIdentifierFormat = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified') { + return this.#setConfig('nameIdentifierFormat', nameIdentifierFormat); + } + setNameIdentifierProbes(nameIdentifierProbes) { + return this.#setConfig('nameIdentifierProbes', nameIdentifierProbes); + } + setAuthnContextClassRef(authnContextClassRef) { + return this.#setConfig('authnContextClassRef', authnContextClassRef); + } + setSigningCert(signingCert) { + return this.#setConfig('signingCert', signingCert); + } + setIncludeAttributeNameFormat(includeAttributeNameFormat) { + return this.#setConfig('includeAttributeNameFormat', includeAttributeNameFormat); + } + setTypedAttributes(typedAttributes) { + return this.#setConfig('typedAttributes', typedAttributes); + } + setEncryptionCert(encryptionCert) { + return this.#setConfig('encryptionCert', encryptionCert); + } + setEncryptionPublicKey(encryptionPublicKey) { + return this.#setConfig('encryptionPublicKey', encryptionPublicKey); + } + setCert(cert) { + return this.#setConfig('cert', cert); + } + setKey(key) { + return this.#setConfig('key', key); + } + setRelayState(relayState) { + return this.#setConfig('RelayState', relayState); + } + setIssuer(issuer) { + return this.#setConfig('issuer', issuer); + } + setEncryptionAlgorithm(encryptionAlgorithm) { + return this.#setConfig('encryptionAlgorithm', encryptionAlgorithm); + } + #setConfig = (key, value) => { + // If the validator returns an error code, we throw an error here. + // This will cause the execution to fail and other commands from actions to be discarded. + // We believe that if the customer wants to set a configuration, it may be critical to their + // usecase. Silently discarding that configuration in service of avoiding breaking their logins + // would go against their intent and could potentially lead to security vulnerabilities. + const configs = { [key]: value }; + index.validate(index.PostLoginSetSAMLConfigurationInputCodec, configs); + this.#triggerAPI.setSAMLConfiguration(configs); + return this.#api; + }; +} + +// This file is copied from auth0-sessions-lib due to restrictions in package installation +// Please make sure to keep it in sync with the original file while a permanent solution is found +// Note that some modifications exist due to typing +// In auth0-sessions-lib see: https://github.com/atko-cic/auth0-sessions-lib//tree/master/src/sessions/validators/ServerSessionValidator.ts +class ServerSessionValidator { + MAX_METADATA_KEY_LENGTH = 255; + MAX_METADATA_VALUE_LENGTH = 255; + MAX_METADATA_KEY_COUNT = 25; + METADATA_KEY_REGEX = new RegExp(`^[a-zA-Z0-9_-]{1,${this.MAX_METADATA_KEY_LENGTH}}$`); + constructor() { } + validateMetadata(metadata) { + if (metadata === null || metadata === undefined) { + return { valid: true }; + } + // Type validation + if (typeof metadata !== 'object' || Array.isArray(metadata)) { + return { valid: false, error: 'Metadata must be an object' }; + } + // Key count validation + if (Object.entries(metadata).length > this.MAX_METADATA_KEY_COUNT) { + return { valid: false, error: 'Metadata must not contain more than 25 entries' }; + } + for (const [key, value] of Object.entries(metadata)) { + // Key length validation + if (key.length > this.MAX_METADATA_KEY_LENGTH) { + return { + valid: false, + error: `All metadata keys must be at most ${this.MAX_METADATA_KEY_LENGTH} characters long`, + }; + } + // Key null byte validation + if (key.includes('\x00')) { + return { + valid: false, + error: 'Metadata keys must not contain null bytes (\x00)', + }; + } + // Key character validation + if (!this.METADATA_KEY_REGEX.test(key)) { + return { + valid: false, + error: 'Metadata keys may only include letters, numbers, underscores, or hyphens', + }; + } + // Value length validation + if (value.length > this.MAX_METADATA_VALUE_LENGTH) { + return { + valid: false, + error: `All metadata values must be at most ${this.MAX_METADATA_VALUE_LENGTH} characters long`, + }; + } + // Value null byte validation + if (value.includes('\x00')) { + return { + valid: false, + error: 'Metadata values must not contain null bytes (\x00)', + }; + } + } + return { valid: true }; + } +} + +class SessionAPIImpl { + #triggerAPI; + #event; + #sessionMetadata; + #sessionMetadataValidator; + /** + * Initialises a new instance of the class. + * @param triggerAPI The trigger API used to process the commands. + * @param event The event object. + */ + constructor(triggerAPI, event) { + this.#triggerAPI = triggerAPI; + this.#event = event; + this.#sessionMetadataValidator = new ServerSessionValidator(); + // unlike refreshToken, session metadata operations should not + // be allowed without an active session (no event.session) + if (this.#event.session) { + this.#sessionMetadata = this.#event.session.metadata + ? handler.deepClone(this.#event.session.metadata) + : {}; + } + } + /** + * Sets the new absolute expiration for the underlying entity. + * + * @param absolute Required, the new absolute expiration time after which the entity will be considered invalid. + */ + setExpiresAt(absolute) { + if (!absolute) { + throw new Error('Either inactivity or absolute expiration time must be provided'); + } + index.validate(index.PostLoginSetSessionExpirationInputCodec, { absolute }); + this.#triggerAPI.setSessionExpiration(undefined, absolute); + } + /** + * Sets the new inactivity expiration for the underlying entity. + * + * @param inactivity Required, the new inactivity expiration time after which the entity will be considered invalid + * if there is no user interaction during this period. + */ + setIdleExpiresAt(inactivity) { + if (!inactivity) { + throw new Error('Either inactivity or absolute expiration time must be provided'); + } + index.validate(index.PostLoginSetSessionExpirationInputCodec, { inactivity }); + this.#triggerAPI.setSessionExpiration(inactivity, undefined); + } + revoke(reason, options) { + index.validate(index.PostLoginRevokeSessionInputCodec, { message: reason, options }); + this.#triggerAPI.revokeSession(reason, options); + } + /** + * [Enterprise Customers] [Early Access] Sets the cookie mode for the current session, allowing it to be either 'persistent' or 'non-persistent' (ephemeral). + * This determines how the session cookie is handled in the browser: + * - 'persistent': The cookie will be stored until it expires or is deleted by the user. + * - 'non-persistent' (ephemeral): The cookie will be deleted when the browser is closed. + * + * If multiple setCookieMode invocations are made, only the last one will take effect. In case 'non-persistent' is set, the cookie will be deleted when the browser is closed, however, the session itself will remain valid until its absolute or idle expiration time is reached + * or the session is revoked through our available APIs. For more information on cookie modes, please refer to our documentation. + * + * @param mode Required, the cookie mode for the current session. + * Can be either 'persistent' or 'non-persistent' (ephemeral). + */ + setCookieMode(mode) { + if (!mode) { + throw new Error('Cookie mode must be provided'); + } + if (!['persistent', 'non-persistent'].includes(mode)) { + throw new Error(`Invalid cookie mode: ${mode}. Valid values are 'persistent' and 'non-persistent'.`); + } + index.validate(index.PostLoginSetCookieModeInputCodec, { mode }); + this.#triggerAPI.setCookieMode(mode); + // Ensure we just apply the side effect if the session exists. + // We need to check if the session is not empty due to ongoing deprecation of + // cases where the session is not present in the flow but exposed as empty object. + if (this.#event.session && + typeof this.#event.session === 'object' && + Object.keys(this.#event.session).length > 0) { + const session = this.#event.session; + this.#event.session = { + ...session, + cookie: { + ...session?.cookie, + mode: mode, + }, + }; + } + } + setMetadata(key, value) { + try { + if (!this.#event.session || !this.#sessionMetadata) { + throw new Error('Cannot set metadata on a session that does not exist.'); + } + if (typeof key !== 'string') { + throw new Error('Invalid metadata: Metadata key must be a string'); + } + // Deletion path + if (value === null) { + if (!(key in this.#sessionMetadata)) { + return; // Key does not exist, nothing to delete + } + delete this.#sessionMetadata[key]; + // Continue validation / setting value + } + else { + if (typeof value !== 'string') { + throw new Error('Invalid metadata: Metadata value must be a string'); + } + this.#sessionMetadata[key] = value; + const validationResult = this.#sessionMetadataValidator.validateMetadata(this.#sessionMetadata); + if (!validationResult.valid) { + throw new Error(`Invalid metadata: ${validationResult.error}`); + } + } + this.#event.session.metadata = handler.deepClone(this.#sessionMetadata); + this.#triggerAPI.setSessionMetadata(this.#sessionMetadata); + } + catch (e) { + const message = e instanceof Error ? e.message : String(e); + throw new Error(`Failed to set session metadata: ${message}`); + } + } + deleteMetadata(key) { + if (!this.#event.session || !this.#sessionMetadata) { + return; + } + if (key in this.#sessionMetadata) { + delete this.#sessionMetadata[key]; + this.#event.session.metadata = handler.deepClone(this.#sessionMetadata); + this.#triggerAPI.setSessionMetadata(this.#sessionMetadata); + } + } + evictMetadata() { + if (!this.#event.session || !this.#sessionMetadata) { + return; + } + this.#sessionMetadata = {}; + this.#event.session.metadata = {}; + this.#triggerAPI.setSessionMetadata({}); + } +} + +/** Feature flag gating access to the transaction target scopes API. */ +const TRANSACTION_TARGET_SCOPES_FLAG = 'actions_pl_transaction_target_scopes'; +class TransactionAPIImpl { + #metadataAPI; + #targetScopesAPI; + #event; + #featureFlags; + constructor(triggerAPI, event, featureFlags) { + this.#metadataAPI = triggerAPI.getTransactionMetadataAPI(); + this.#targetScopesAPI = triggerAPI.getTargetScopesAPI(); + this.#event = event; + this.#featureFlags = featureFlags; + } + setMetadata(key, value) { + this.#metadataAPI.setMetadata(key, value); + this.#ensureTransaction().metadata = this.#metadataAPI.getMetadata(); + } + addTargetScope(scope) { + this.#assertFeatureEnabled(); + this.#targetScopesAPI.addTargetScope(scope); + this.#syncEventTargetScopes(); + } + removeTargetScope(scope) { + this.#assertFeatureEnabled(); + this.#targetScopesAPI.removeTargetScope(scope); + this.#syncEventTargetScopes(); + } + setTargetScopes(scopes) { + this.#assertFeatureEnabled(); + this.#targetScopesAPI.setTargetScopes(scopes); + this.#syncEventTargetScopes(); + } + clearTargetScopes() { + this.#assertFeatureEnabled(); + this.#targetScopesAPI.clearTargetScopes(); + this.#syncEventTargetScopes(); + } + // `event.transaction` is optional on the Post Login event, so it may need to be + // created before it can be mutated. + #ensureTransaction() { + if (!this.#event.transaction) { + this.#event.transaction = {}; + } + return this.#event.transaction; + } + // Mirror the scope set onto the event so subsequent reads within the same + // Action observe the change. Only ever reached once the gating feature flag + // has been asserted, so this needs no gate of its own. + #syncEventTargetScopes() { + this.#ensureTransaction().target_scopes = this.#targetScopesAPI.getTargetScopes(); + } + // Prevent the internal transaction target scopes API from being executed when + // the gating feature flag is disabled. Remove this guard at GA. + #assertFeatureEnabled() { + if (this.#featureFlags[TRANSACTION_TARGET_SCOPES_FLAG] !== true) { + throw new Error('Method not implemented.'); + } + } +} + +class UserAPIImpl { + #api; + #triggerAPI; + #event; + constructor(triggerAPI, api, event) { + this.#api = api; + this.#triggerAPI = triggerAPI; + this.#event = event; + } + setAppMetadata(key, value) { + // Guard against situations where the persisted user's metadata is NOT already + // an object. This type is not enforced in some scenarios like CustomDB where a developer + // can return anything in the app_metadata field. + if (!metadata.isObject(this.#event.user.app_metadata)) { + throw new Error('Unexpected app_metadata format. Must be a valid JSON object'); + } + this.#triggerAPI.setMetadata('application', key, value); + return this.#api; + } + setUserMetadata(key, value) { + // Guard against situations where the persisted user's metadata is NOT already + // an object. This type is not enforced in some scenarios like CustomDB where a developer + // can return anything in the user_metadata field. + if (!metadata.isObject(this.#event.user.user_metadata)) { + throw new Error('Unexpected user_metadata format. Must be a valid JSON object.'); + } + this.#triggerAPI.setMetadata('user', key, value); + return this.#api; + } +} + +class PostLoginAPIImpl { + access; + accessToken; + authentication; + cache; + groups; + roles; + idToken; + multifactor; + prompt; + refreshToken; + session; + redirect; + samlResponse; + user; + validation; + rules; + transaction; + constructor(event, triggerAPI, rulesContext, featureFlags) { + this.access = new AccessAPIImpl(triggerAPI, this); + this.accessToken = new AccessTokenAPIImpl(triggerAPI, this); + this.authentication = new AuthenticationAPIImpl(triggerAPI, this); + this.cache = triggerAPI.getCacheAPI(); + this.groups = new GroupsAPIImpl(triggerAPI, event); + this.roles = new RolesAPIImpl(triggerAPI, event); + this.idToken = new IdTokenAPIImpl(triggerAPI, this); + this.multifactor = new MultifactorAPIImpl(triggerAPI, this); + this.prompt = new PromptAPIImpl(triggerAPI); + this.refreshToken = new RefreshTokenAPIImpl(triggerAPI, event); + this.session = new SessionAPIImpl(triggerAPI, event); + this.redirect = new RedirectAPIImpl(triggerAPI, this, event); + this.samlResponse = new SAMLResponseAPIImpl(triggerAPI, this); + this.user = new UserAPIImpl(triggerAPI, this, event); + this.validation = new ValidationAPIImpl(triggerAPI, this); + this.rules = new RulesAPIImpl(rulesContext); + this.transaction = new TransactionAPIImpl(triggerAPI, event, featureFlags); + } +} + +function contextToArguments(context) { + return [ + context.event, + new PostLoginAPIImpl(context.event, context.triggerAPI, context.rules, context.featureFlags), + ]; +} +const getHandler = handler.getHandlerFactory([ + 'onExecutePostLogin', + 'onContinuePostLogin', +]); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link PostLoginTriggerAPI} and rules context. The event is cloned per call so mutations in one + * execution don't leak into the next. + */ +function getDefaultArguments() { + const event$1 = handler.deepClone(event); + const context = { + event: event$1, + triggerAPI: new PostLoginTriggerAPIStubImpl([...(event$1.transaction?.target_scopes ?? [])]), + rules: [], + featureFlags: {}, + }; + return contextToArguments(context); +} +/** Loads a PostLogin v3 action file for use in tests, e.g. `action.execute('onExecutePostLogin', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction; diff --git a/src/post-user-registration/v2/test/index.d.ts b/src/post-user-registration/v2/test/index.d.ts new file mode 100644 index 0000000..fdc2051 --- /dev/null +++ b/src/post-user-registration/v2/test/index.d.ts @@ -0,0 +1,48 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +import { g as PostUserRegistrationV2Event } from '../../../_shared/CUlF8oaW.js'; +/** + * Methods and utilities to help change the behavior after a signup. + */ +interface PostUserRegistrationAPI { + /** + * Store and retrieve data that persists across executions. + */ + readonly cache: CacheAPI; +} +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event extends PostUserRegistrationV2Event { + /** + * @private Configuration values associated with this Action. + */ + configuration: Configuration; + /** + * Secret values securely associated with this Action. + */ + secrets: Secrets; +} +interface PostUserRegistrationAction { + (event: Event, api: PostUserRegistrationAPI): Promise; +} +type PostUserRegistrationModule = { + onExecutePostUserRegistration: PostUserRegistrationAction; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link PostUserRegistrationTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters< + PostUserRegistrationModule[keyof PostUserRegistrationModule] +>; +/** Loads a PostUserRegistration v2 action file for use in tests, e.g. `action.execute('onExecutePostUserRegistration', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/post-user-registration/v2/test/index.js b/src/post-user-registration/v2/test/index.js new file mode 100644 index 0000000..107466e --- /dev/null +++ b/src/post-user-registration/v2/test/index.js @@ -0,0 +1,191 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); + +/** + * No-op {@link PostUserRegistrationTriggerAPI} for use in mock API implementations. + */ +class PostUserRegistrationTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + getCacheAPI() { + return this.#cacheAPI; + } +} + +const event = { + authentication: { + riskAssessment: { + supplemental: { + akamai: { + akamaiBot: { + type: 'Akamai-Categorized Bot', + action: 'Monitor', + botCategory: ['Web Search Engine Bots', 'Search Engine Bots'], + botScore: 0, + botScoreResponseSegment: 'test', + botnetId: 'googlebot', + }, + akamaiUserRisk: { + action: 'monitor', + allow: 0, + emailDomain: 'example.com', + general: { + aci: '0', + db: 'Chrome 85', + di: '0fc91b5ec42f5a471c16a85e3e388ca57697c1a9', + do: 'Mac OS X 10', + }, + ouid: 'm534264', + requestid: '19e22e', + risk: { + ugp: 'ie/M', + unp: '432/H', + }, + score: 0, + status: 4, + trust: { + udbp: 'Chrome85', + udfp: '25ba44ec3b391ba4ce5fbbd2979635e254775e7d', + udop: 'Mac OS X 10', + ugp: 'FR', + unp: '12322', + utp: 'weekday_3', + }, + username: 'testuser@example.com', + uuid: '86b37525-8047-4a3c-8d7a-23e99901da05', + }, + }, + }, + }, + }, + request: { + hostname: '{{TENANT}}.example.com', + ip: '13.33.86.47', + language: 'en', + user_agent: 'curl/7.64.1', + method: 'POST', + geoip: { + cityName: 'Bellevue', + continentCode: 'NA', + countryCode3: 'USA', + countryCode: 'US', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + }, + connection: { + id: 'con_fpe5kj482KO1eOzQ', + name: 'Username-Password-Authentication', + metadata: {}, + strategy: 'auth0', + }, + custom_domain: { + domain: 'login.{{YOUR_DOMAIN}}.com', + domain_metadata: { + environment: 'production', + region: 'us-west-2', + }, + }, + experiment: { + experiment_id: 'exp_abc123', + variation_id: 'var_abc123', + variation_name: 'Treatment', + variation_description: 'New login experience', + config: { + theme: { + value: 'dark', + }, + max_attempts: { + value: 5, + }, + feature_enabled: { + value: true, + }, + }, + is_control: false, + }, + tenant: { + id: '{{TENANT}}', + }, + transaction: { + acr_values: [], + correlation_id: '1234567890', + id: '', + locale: '', + requested_scopes: [], + ui_locales: [], + protocol: 'oauth2-refresh-token', + redirect_uri: 'http://someuri.com', + prompt: ['none'], + login_hint: 'test@test.com', + response_mode: 'form_post', + response_type: ['id_token'], + state: 'AABBccddEEFFGGTTasrs', + }, + user: { + tenant: '{{TENANT}}', + username: 'j+smith', + email: 'j+smith@example.com', + phoneNumber: '123-123-1234', + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + created_at: '{{DATE}}', + email_verified: true, + family_name: 'Smith', + given_name: 'John', + last_password_reset: '{{DATE}}', + name: 'John Smith', + nickname: 'j+smith', + phone_verified: true, + picture: 'http://www.gravatar.com/avatar/?d=identicon', + updated_at: '{{DATE}}', + app_metadata: {}, + user_metadata: {}, + phone_number: '123-123-1234', + }, + configuration: {}, + secrets: {}, + security_context: { + ja3: 'c13a3e168d43e62e0ad96fae6347e2e4', + ja4: 't13d1516h2_8daaf6152771_02713d6af862', + }, +}; + +class PostUserRegistrationAPIImpl { + cache; + constructor(triggerAPI) { + this.cache = triggerAPI.getCacheAPI(); + } +} + +function contextToArguments(ctx) { + return [ctx.event, new PostUserRegistrationAPIImpl(ctx.triggerAPI)]; +} +const getHandler = handler.getHandlerFactory([ + 'onExecutePostUserRegistration', +]); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link PostUserRegistrationTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +function getDefaultArguments() { + return contextToArguments({ + event: handler.deepClone(event), + triggerAPI: new PostUserRegistrationTriggerAPIStubImpl(), + }); +} +/** Loads a PostUserRegistration v2 action file for use in tests, e.g. `action.execute('onExecutePostUserRegistration', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction; diff --git a/src/pre-user-registration/v2/test/index.d.ts b/src/pre-user-registration/v2/test/index.d.ts new file mode 100644 index 0000000..e862c48 --- /dev/null +++ b/src/pre-user-registration/v2/test/index.d.ts @@ -0,0 +1,113 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +import { h as PreUserRegistrationV2Event } from '../../../_shared/CUlF8oaW.js'; +interface AccessAPI { + /** + * Deny the user from being able to register. The signup flow will immediately stop following the + * completion of this action and no further Actions will be executed. + * + * @param reason An internal reason describing why this registration attempt is being denied. This value + * will appear in tenant logs. + * + * @param userMessage A human-readable explanation for rejecting the registration attempt. This may be presented + * directly in end-user interfaces. + */ + deny(reason: string, userMessage: string): PreUserRegistrationAPI; +} +interface ValidationAPI { + /** + * Deny the user from being able to register. The signup flow will immediately stop following the + * completion of this action and no further Actions will be executed. + * + * @param errorCode A customer defined error code describing why this registration attempt is being denied. This value + * will appear in tenant logs. + * + * @param errorMessage A customer defined explanation for rejecting the registration attempt. This may be presented + * directly in end-user interfaces. + */ + error(errorCode: string, errorMessage: string): PreUserRegistrationAPI; +} +interface UserAPI { + /** + * Set application-specific metadata for the user that is logging in. + * + * Note: This method should not be used in callbacks. Invoking this method won't update the metadata immediately. + * You can call this several times throughout multiple actions of the same flow and the engine will aggregate the + * changes and update the metadata at once before the flow is completed. + * + * @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. + */ + setAppMetadata(key: string, value: unknown): PreUserRegistrationAPI; + /** + * Set general metadata for the user that is logging in. + * + * Note: This method should not be used in callbacks. Invoking this method won't update the metadata immediately. + * You can call this several times throughout multiple actions of the same flow and the engine will aggregate the + * changes and update the metadata at once before the flow is completed. + * + * @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. + */ + setUserMetadata(key: string, value: unknown): PreUserRegistrationAPI; +} +/** + * Methods and utilities to help change the behavior of the login flow. + */ +interface PreUserRegistrationAPI { + /** + * Modify the access of the user that is logging in, such as rejecting the login attempt. + */ + readonly access: AccessAPI; + /** + * Make changes to the metadata of the user that is logging in. + */ + readonly user: UserAPI; + /** + * Store and retrieve data that persists across executions. + */ + readonly cache: CacheAPI; + /** + * Modify the access of the user that is logging in, such as rejecting the login attempt due to validation errors. + */ + readonly validation: ValidationAPI; +} +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event extends PreUserRegistrationV2Event { + /** + * @private Configuration values associated with this Action. + */ + configuration: Configuration; + /** + * Secret values securely associated with this Action. + */ + secrets: Secrets; +} +interface PreUserRegistrationAction { + (event: Event, api: PreUserRegistrationAPI): Promise; +} +type PreUserRegistrationModule = { + onExecutePreUserRegistration: PreUserRegistrationAction; + onContinuePreUserRegistration: PreUserRegistrationAction; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link PreUserRegistrationTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters< + PreUserRegistrationModule[keyof PreUserRegistrationModule] +>; +/** Loads a PreUserRegistration v2 action file for use in tests, e.g. `action.execute('onExecutePreUserRegistration', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/pre-user-registration/v2/test/index.js b/src/pre-user-registration/v2/test/index.js new file mode 100644 index 0000000..22ba0aa --- /dev/null +++ b/src/pre-user-registration/v2/test/index.js @@ -0,0 +1,254 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +var index = require('../../../_shared/DvTaCl9e.js'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); + +/** + * No-op {@link PreUserRegistrationTriggerAPI} for use in mock API implementations. + */ +class PreUserRegistrationTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + getCacheAPI() { + return this.#cacheAPI; + } + denyAccess(_reason, _userMessage) { } + setAppMetadata(_key, _value) { } + setUserMetadata(_key, _value) { } + setUserId(_user_id) { } + validationError(_errorCode, _errorMessage) { } +} + +const event = { + anonymous_session: { + user_id: 'anon@5bdbd57d-cb7a-4cbb-b938-c0499a31ad52', + session_id: 'anon_sess@f727e268-46da-4c7d-ad57-b8c80318c295', + created_at: '2024-01-01T00:00:00.000Z', + expires_at: '2024-01-01T01:00:00.000Z', + metadata: { + source: 'web', + }, + }, + authentication: { + riskAssessment: { + supplemental: { + akamai: { + akamaiBot: { + type: 'Akamai-Categorized Bot', + action: 'Monitor', + botCategory: ['Web Search Engine Bots', 'Search Engine Bots'], + botScore: 0, + botScoreResponseSegment: 'test', + botnetId: 'googlebot', + }, + akamaiUserRisk: { + action: 'monitor', + allow: 0, + emailDomain: 'example.com', + general: { + aci: '0', + db: 'Chrome 85', + di: '0fc91b5ec42f5a471c16a85e3e388ca57697c1a9', + do: 'Mac OS X 10', + }, + ouid: 'm534264', + requestid: '19e22e', + risk: { + ugp: 'ie/M', + unp: '432/H', + }, + score: 0, + status: 4, + trust: { + udbp: 'Chrome85', + udfp: '25ba44ec3b391ba4ce5fbbd2979635e254775e7d', + udop: 'Mac OS X 10', + ugp: 'FR', + unp: '12322', + utp: 'weekday_3', + }, + username: 'testuser@example.com', + uuid: '86b37525-8047-4a3c-8d7a-23e99901da05', + }, + }, + }, + }, + }, + request: { + method: 'POST', + ip: '13.33.86.47', + geoip: { + cityName: 'Bellevue', + continentCode: 'NA', + countryCode3: 'USA', + countryCode: 'US', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + hostname: '{{TENANT}}.example.com', + language: 'en', + user_agent: 'curl/7.64.1', + body: { + 'ulp-custom-field': 'UlpCustomField', + }, + }, + transaction: { + acr_values: [], + correlation_id: '1234567890', + locale: 'en', + requested_scopes: [], + ui_locales: ['en'], + protocol: 'oidc-basic-profile', + }, + connection: { + id: 'con_fpe5kj482KO1eOzQ', + strategy: 'stragegy', + name: 'Username-Password-Authentication', + metadata: {}, + }, + custom_domain: { + domain: 'login.{{YOUR_DOMAIN}}.com', + domain_metadata: { + environment: 'production', + region: 'us-west-2', + }, + }, + experiment: { + experiment_id: 'exp_abc123', + variation_id: 'var_abc123', + variation_name: 'Treatment', + variation_description: 'New login experience', + config: { + theme: { + value: 'dark', + }, + max_attempts: { + value: 5, + }, + feature_enabled: { + value: true, + }, + }, + is_control: false, + }, + tenant: { + id: '{{TENANT}}', + }, + configuration: {}, + secrets: {}, + client: { + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + external_client_id: 'https://example.com/oauth-client.json', + metadata: {}, + name: 'All Applications', + external_metadata_type: 'cimd', + }, + user: { + app_metadata: {}, + email: 'j+smith@example.com', + family_name: 'Smith', + given_name: 'John', + name: 'John Smith', + nickname: 'j+smith', + picture: 'http://www.gravatar.com/avatar/?d=identicon', + user_metadata: {}, + username: 'j+smith', + phone_number: '123-123-1234', + }, + security_context: { + ja3: 'c13a3e168d43e62e0ad96fae6347e2e4', + ja4: 't13d1516h2_8daaf6152771_02713d6af862', + }, +}; + +class AccessAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + deny(reason, userMessage) { + this.#triggerAPI.denyAccess(reason, userMessage); + return this.#api; + } +} +class ValidationAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + error(errorCode, errorMessage) { + index.validate(index.PreUserRegistrationValidationErrorInputCodec, { errorCode, errorMessage }, 'Failed to add validation error'); + this.#triggerAPI.validationError(errorCode, errorMessage); + return this.#api; + } +} +class UserAPIImpl { + #api; + #triggerAPI; + constructor(triggerAPI, api) { + this.#api = api; + this.#triggerAPI = triggerAPI; + } + setAppMetadata(key, value) { + this.#triggerAPI.setAppMetadata(key, value); + return this.#api; + } + setUserMetadata(key, value) { + this.#triggerAPI.setUserMetadata(key, value); + return this.#api; + } + setUserId(user_id) { + index.validate(index.PreUserRegistrationSetUserIdInputCodec, { user_id }, 'Invalid setUserId arguments'); + this.#triggerAPI.setUserId(user_id); + return this.#api; + } +} +class PreUserRegistrationAPIImpl { + access; + user; + cache; + validation; + constructor(triggerAPI) { + this.cache = triggerAPI.getCacheAPI(); + this.access = new AccessAPIImpl(triggerAPI, this); + this.user = new UserAPIImpl(triggerAPI, this); + this.validation = new ValidationAPIImpl(triggerAPI, this); + } +} + +function contextToArguments(ctx) { + return [ctx.event, new PreUserRegistrationAPIImpl(ctx.triggerAPI)]; +} +const getHandler = handler.getHandlerFactory([ + 'onExecutePreUserRegistration', + 'onContinuePreUserRegistration', +]); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link PreUserRegistrationTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +function getDefaultArguments() { + return contextToArguments({ + event: handler.deepClone(event), + triggerAPI: new PreUserRegistrationTriggerAPIStubImpl(), + }); +} +/** Loads a PreUserRegistration v2 action file for use in tests, e.g. `action.execute('onExecutePreUserRegistration', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction; diff --git a/src/send-phone-message/v2/test/index.d.ts b/src/send-phone-message/v2/test/index.d.ts new file mode 100644 index 0000000..866292a --- /dev/null +++ b/src/send-phone-message/v2/test/index.d.ts @@ -0,0 +1,48 @@ +import { + C as CacheAPI, + M as ModuleRegistration, + L as LoadedAction, +} from '../../../_shared/BquXyhu2.js'; +import { S as SendPhoneMessageV2Event } from '../../../_shared/CUlF8oaW.js'; +/** + * Methods and utilities to help change the behavior of sending a phone message. + */ +interface SendPhoneMessageAPI { + /** + * Store and retrieve data that persists across executions. + */ + readonly cache: CacheAPI; +} +interface Configuration {} +interface Secrets { + [secretName: string]: string; +} +interface Event extends SendPhoneMessageV2Event { + /** + * @private Configuration values associated with this Action. + */ + configuration: Configuration; + /** + * Secret values securely associated with this Action. + */ + secrets: Secrets; +} +interface SendPhoneMessageAction { + (event: Event, api: SendPhoneMessageAPI): Promise; +} +type SendPhoneMessageModule = { + onExecuteSendPhoneMessage: SendPhoneMessageAction; +}; +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link SendPhoneMessageTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +declare function getDefaultArguments(): Parameters< + SendPhoneMessageModule[keyof SendPhoneMessageModule] +>; +/** Loads a SendPhoneMessage v2 action file for use in tests, e.g. `action.execute('onExecuteSendPhoneMessage', event, api)`. */ +declare const loadAction: ( + filename: string, + modules?: readonly ModuleRegistration[] +) => Promise>; +export { getDefaultArguments, loadAction }; diff --git a/src/send-phone-message/v2/test/index.js b/src/send-phone-message/v2/test/index.js new file mode 100644 index 0000000..a078e95 --- /dev/null +++ b/src/send-phone-message/v2/test/index.js @@ -0,0 +1,130 @@ +'use strict'; + +var handler = require('../../../_shared/_XK5Fidc.js'); +require('node:vm'); +require('async_hooks'); +require('console'); +require('stream'); +require('node:fs/promises'); +require('node:module'); + +/** + * No-op {@link SendPhoneMessageTriggerAPI} for use in mock API implementations. + */ +class SendPhoneMessageTriggerAPIStubImpl { + #cacheAPI = handler.createNoopCacheAPI(); + getCacheAPI() { + return this.#cacheAPI; + } +} + +const event = { + message_options: { + action: 'enrollment', + code: '1234556ADSFA547865', + message_type: 'sms', + recipient: '+1-808-555-5555', + text: 'Here is your one time password!', + }, + client: { + client_id: 'gmOWNgklfRm4tyl5YYnl3JDSJy19h1bR', + name: 'All Applications', + metadata: {}, + }, + custom_domain: { + domain: 'login.{{YOUR_DOMAIN}}.com', + domain_metadata: { + environment: 'production', + region: 'us-west-2', + }, + }, + request: { + geoip: { + cityName: 'Bellevue', + countryName: 'United States of America', + latitude: 47.61793, + longitude: -122.19584, + continentCode: 'NA', + countryCode: 'US', + countryCode3: 'USA', + subdivisionCode: 'WA', + subdivisionName: 'Washington', + timeZone: 'America/Los_Angeles', + }, + ip: '13.33.86.47', + method: 'POST', + hostname: '{{TENANT}}.auth0.com', + language: 'en', + user_agent: 'curl/7.64.1', + }, + tenant: { + id: '{{TENANT}}', + }, + user: { + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + name: 'John Smith', + email: 'j+smith@example.com', + app_metadata: {}, + user_metadata: {}, + picture: 'http://www.gravatar.com/avatar/?d=identicon', + created_at: '{{DATE}}', + email_verified: true, + updated_at: '{{DATE}}', + multifactor: ['guardian'], + family_name: 'Smith', + given_name: 'John', + nickname: 'j+smith', + last_password_reset: '{{DATE}}', + identities: [ + { + connection: 'Username-Password-Authentication', + isSocial: false, + provider: 'auth0', + userId: '5f7c8ec7c33c6c004bbafe82', + accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gU21pdGgiLCJpYXQiOjE1MTYyMzkwMjJ9.Q_w2AVguPRU2KskCXwR7ZHl09TQXEntfEA8Jj2_Jyew', + profileData: {}, + user_id: 'auth0|5f7c8ec7c33c6c004bbafe82', + }, + ], + phone_number: '+1-808-555-5555', + phone_verified: false, + username: 'j+smith', + }, + configuration: {}, + secrets: {}, + security_context: { + ja3: 'c13a3e168d43e62e0ad96fae6347e2e4', + ja4: 't13d1516h2_8daaf6152771_02713d6af862', + }, + transaction: { + correlation_id: 'abcdef123456', + }, +}; + +class SendPhoneMessageAPIImpl { + cache; + constructor(triggerAPI) { + this.cache = triggerAPI.getCacheAPI(); + } +} + +function contextToArguments(ctx) { + return [ctx.event, new SendPhoneMessageAPIImpl(ctx.triggerAPI)]; +} +const getHandler = handler.getHandlerFactory(['onExecuteSendPhoneMessage']); + +/** + * Builds `contextToArguments` input for tests: a fresh clone of the example event paired with a stubbed + * {@link SendPhoneMessageTriggerAPI}. The event is cloned per call so mutations in one execution don't leak into the next. + */ +function getDefaultArguments() { + return contextToArguments({ + event: handler.deepClone(event), + triggerAPI: new SendPhoneMessageTriggerAPIStubImpl(), + }); +} +/** Loads a SendPhoneMessage v2 action file for use in tests, e.g. `action.execute('onExecuteSendPhoneMessage', event, api)`. */ +const loadAction = handler.createLoader(getHandler); + +exports.getDefaultArguments = getDefaultArguments; +exports.loadAction = loadAction;