Skip to main content

Advanced Configurations

A comprehensive reference for every building block in the Flow Builder, covering flow types, widgets, steps, components, and executors, with configuration details and guidance for building robust flows.

Node Types

START

The START node marks the entry point of every flow. Each flow has exactly one START node. It appears automatically when you open a new flow and cannot be deleted.

Prompt Node

A PROMPT node renders a screen to the user. It presents form fields, buttons, and other UI elements defined by its prompts. The flow pauses at a PROMPT node until the user fills the required inputs and selects an action to advance. Each PROMPT node is built from a list of prompts. Each prompt entry declares:

  • inputs (optional): form fields the user must fill before the flow can advance.
  • action (optional): a button or link the user selects to advance. An action carries a nextNode reference, so different actions can route the flow to different paths.

A prompt entry can carry an action only, or both. The node collects all input values across every prompt entry and pauses until the requirements for the selected action are satisfied.

Input fields

FieldRequiredDescription
identifierYesUnique key used to store the submitted value.
refNoUI binding reference that links to a meta component by id.
typeNoField type: text (default), password, or select.
requiredNoWhen true, the field must be filled before the flow can advance.

Action

FieldRequiredDescription
refYesUnique identifier for the action.
nextNodeYesID of the node to advance to when this action is selected.
typeNoSUBMIT or REJECT.

Example

{
"id": "collect-credentials",
"type": "PROMPT",
"prompts": [
{
"inputs": [
{ "identifier": "username", "type": "text", "required": true },
{ "identifier": "password", "type": "password", "required": true }
],
"action": { "ref": "action-password", "nextNode": "verify-password", "type": "SUBMIT" }
},
{
"inputs": [
{ "identifier": "username", "type": "text", "required": true },
{ "identifier": "phone", "type": "text", "required": true }
],
"action": { "ref": "action-otp", "nextNode": "send-otp", "type": "SUBMIT" }
}
]
}
Display-Only Mode

A display-only PROMPT node renders a screen without collecting user input. It is used in three cases:

  • Terminal success: Ends the flow with a complete status.
  • Terminal error: Ends the flow with a failure status.
  • Intermediate display: Pauses the flow mid-execution; continuation happens via a separate mechanism.

A PROMPT node is treated as display-only when it has a next property but no prompts entries. The display is rendered to the user and execution automatically advances to the node specified by next.

PropertyRequiredDescription
nextYesID of the next node to advance to automatically. Mutually exclusive with prompts.
messageNoA textual message included in the node response for non-verbose rendering.
{
"id": "welcome-screen",
"type": "PROMPT",
"next": "identify-user",
"message": "Welcome! Setting up your account..."
}
Meta Field

The meta field is a UI rendering descriptor that carries a component tree that tells the client how to render the PROMPT screen. This is a PROMPT-node-only configuration field.

meta is only included in a flow response when the client requests verbose mode. Even then, the runtime processes it before sending: it trims, enriches, and patches the component tree to match only what is still needed for the current execution step.

To enable verbose mode, set "verbose": true in the flow execution request body. It defaults to false.

Top-level shape

{
"meta": {
"components": [ ... ]
}
}

The components array is the only field the runtime acts on. Any other fields in meta are passed through as-is.

Component types

TypeRole
BLOCKContainer that groups inputs and actions. Input and action components must live inside a BLOCK.
ACTIONA button or link. Its id must match the ref of a prompt action.
DYNAMIC_INPUT_PLACEHOLDERMarks where dynamically derived inputs (those arriving via ForwardedData, not declared in the node's prompts) should be inserted.
Any other typeTreated as a structural or display component (e.g. a text label). Kept as-is; its children are recursively trimmed.

Note: DYNAMIC_INPUT_PLACEHOLDER is only valid for a PROMPT node reached via a Provisioning node's onIncomplete edge.

Each component is identified by an id field and optionally a ref field. When both are present, ref takes priority for matching against prompt inputs.

Example

{
"id": "login-screen",
"type": "PROMPT",
"prompts": [
{
"inputs": [
{ "identifier": "username", "ref": "field-username", "type": "text", "required": true },
{ "identifier": "password", "ref": "field-password", "type": "password", "required": true }
],
"action": { "ref": "action-submit", "nextNode": "verify" }
}
],
"meta": {
"components": [
{
"id": "main-block",
"type": "BLOCK",
"components": [
{ "id": "field-username", "ref": "field-username", "type": "text", "label": "Username", "required": true },
{ "id": "field-password", "ref": "field-password", "type": "password", "label": "Password", "required": true },
{ "id": "action-submit", "type": "ACTION", "label": "Sign In" }
]
}
]
}
}
Login Options Variant

A PROMPT node with the LOGIN_OPTIONS variant acts as the login method chooser in an authentication flow. It controls which authentication options are presented to the user and can selectively show or hide actions based on keys resolved at runtime.

How it works

Each action on a LOGIN_OPTIONS node can be mapped to a key string via the authMethodMapping node property. The mapping is one-to-one: each key maps to exactly one action ref. When the flow reaches this node, ThunderID filters the node's actions based on the resolved keys:

  • Actions whose key matches a resolved value are included.
  • Actions that are not mapped to any key are always shown, regardless of resolved values.
  • If only one action remains after filtering, the chooser screen is skipped and that option is selected automatically.
  • If no action matches any resolved value, all actions are shown as a fallback.

To surface multiple gated options, multiple keys can be resolved, each mapped to a different action.

Node property

PropertyRequiredDescription
authMethodMappingNoObject mapping key strings to action refs. Actions listed here are selectively shown based on resolved keys. Actions not listed are always shown.

Example

The node below has three actions. action-password and action-otp are conditionally shown based on the resolved keys; action-passkey has no mapping entry and is always shown.

{
"variant": "LOGIN_OPTIONS",
"properties": {
"authMethodMapping": {
"password": "action-password",
"otp": "action-otp"
}
},
"prompts": [
{ "action": { "ref": "action-password", "nextNode": "pwd-task" } },
{ "action": { "ref": "action-otp", "nextNode": "otp-task" } },
{ "action": { "ref": "action-passkey", "nextNode": "pk-task" } }
]
}

Runtime Behavior for this Example

Resolved keysActions shown
passwordaction-password, action-passkey
otpaction-otp, action-passkey
password, otpaction-password, action-otp, action-passkey
(none, or no match)All three actions

Task Execution Node

A TASK EXECUTION node runs a named executor, a discrete unit of server-side logic such as sending a notification, creating a user, or validating a credential. The node routes to different successor nodes based on whether the executor succeeds, fails, or requires additional input.

Node configuration

FieldRequiredDescription
executorYesName of the executor to run.
modeNoOperational mode passed to the executor. Use when an executor supports multiple steps or variants (e.g. send, verify).
inputsNoOverride the executor's default inputs for this node. When omitted, the executor's own default inputs are used.
onSuccessNoID of the node to advance to when the executor completes successfully.
onFailureNoID of the node to forward to when the executor reports a failure. The failure reason is stored in runtime data so the target node (typically a PROMPT) can display it.
onIncompleteNoID of the PROMPT node to forward to when the executor needs additional user input.

When a node declares its own inputs, those replace the executor's defaults for that specific node instance. This lets the same executor be used in different parts of a flow with different field sets. See Providing Inputs to the Executor.

Node-level properties are injected into runtime data before the executor runs, making them available to the executor and to placeholder resolution downstream. See Executor Details and Configuration.

Example

{
"id": "send-sms",
"type": "TASK_EXECUTION",
"executor": {
"name": "SMSExecutor"
},
"properties": {
"senderId": "sms-sender-01"
},
"onSuccess": "verify-otp-screen",
"onFailure": "login-screen"
}

Call Node

A CALL node invokes another flow as a sub-flow. When the callee flow reaches an END node, execution resumes in the caller at the CALL node's onSuccess target. If the callee terminates with an error, execution resumes at onFailure when set; otherwise the caller flow terminates with the callee's error.

Any flow can call any other flow, regardless of flowType. The referenced flow is resolved at runtime by its ID, so the callee can be edited independently. Nested CALLs are supported up to a fixed maximum depth of 10; exceeding it terminates the flow with a call-depth-exceeded error.

Node configuration

FieldRequiredDescription
flow.refYesID of the flow to invoke.
onSuccessYesID of the node to advance to when the callee flow completes successfully.
onFailureNoID of the node to forward to when the callee flow terminates with an error. Unlike TASK EXECUTION, the target does not have to be a PROMPT.

Example

{
"id": "call-signup",
"type": "CALL",
"flow": {
"ref": "self-registration-flow-id"
},
"onSuccess": "assert-generation",
"onFailure": "login-screen"
}

END

The END node marks a successful completion of the flow. When the flow reaches an END node, ThunderID issues an assertion confirming the user authenticated or registered successfully. A flow can have multiple END nodes if different paths each lead to a valid completion.

Building Blocks

This section covers all items available in the left panel of the Flow Builder.

Widgets

Widgets are pre-built, reusable flow segments that combine a View node and one or more Executor nodes. Adding a Widget gives you a complete, pre-wired block covering an entire authentication step.

WidgetDescription
Username + PasswordA login form wired to the Identifier + Password executor.
Continue with GoogleSocial login using Google.
Continue with GitHubSocial login using GitHub.
Continue with SMS OTPAn OTP input view wired to the Verify OTP executor.
Sign In with PasskeyA passkey prompt wired to the passkey verification executors.
ProvisioningA Provisioning executor wired to a dynamic input view that prompts for missing schema attributes.
Self Sign Up LinkA rich text link wired to a CALL node that invokes a registration flow.
Sign In LinkA rich text link wired to a CALL node that invokes an authentication flow.
Recovery LinkA rich text link wired to a CALL node that invokes a recovery flow.

Steps

Steps are individual View nodes, a UI screen only, with no pre-wired executors. Use a Step when you need a custom combination not covered by a Widget.

StepDescription
Blank ViewAn empty View node. Add Components from the left panel to design the screen.
SMS OTP ViewA pre-designed OTP input screen for SMS verification.
Passkey ViewA pre-designed screen for passkey authentication.
Username + PasswordA pre-designed login form with username and password fields.
Consent ViewA screen for capturing user consent.

Components

Components are the individual UI elements inside a View node. Add them to a Blank View to design a custom screen, or extend an existing Step with additional elements.

ComponentDescription
FormA container that groups input fields and a submit button.
Text InputA single-line text field.
Password InputA password field with masked input.
Email InputAn email address field.
Phone InputA phone number field.
Number InputA numeric field.
Date InputA date picker field.
OTP InputA one-time password input field.
CheckboxA checkbox for consent or toggling an option.
ButtonA clickable button that triggers a transition to the next node.
ResendA control that lets the user request a new OTP or code.
TextA read-only label or paragraph.
Rich TextA styled text block that supports links and formatting. Optionally carries an action: { ref } field; when set, anchors marked with data-action-ref dispatch a flow action instead of navigating, letting the rich text drive a CALL or other target the same way a button would.
DividerA horizontal rule that visually separates sections of a screen.
ImageAn image element such as a logo or illustration.
StackA layout container that groups other components.
IconA small icon element.
TimerA countdown timer, used on consent screen.
Dynamic Input PlaceholderMarks where dynamically forwarded inputs (e.g. from the Provisioning executor's incomplete path) should be rendered inside a View.

Executors

Executors are backend operation nodes. Each Executor performs one specific operation and exposes up to three outcome paths: success (green dot), failure (red dot), and incomplete (yellow dot). Following is the list of available executors.

warning

Every flow you deploy must reference only executors that are registered on the server. If you configure flow.executors to register a subset of built-in executors, flows that reference executors outside that list fail validation at startup unless you register a custom executor with the same name.

ExecutorDescriptionConfiguration Prerequisites
Identifier + PasswordVerifies a username (or email) and password against the user store.-
OAuthAuthenticates via generic OAuth 2.0 provider.OAuth identity provider configured
OIDC AuthAuthenticates via generic OIDC provider.OIDC identity provider configured
GoogleAuthenticates the user via Google sign-in.Google identity provider configured
GitHubAuthenticates the user via GitHub sign-in.GitHub identity provider configured
Request PasskeyInitiates a passkey authentication challenge.Relying party configured
Verify PasskeyVerifies the user's passkey response.Request Passkey must have run
Start Passkey RegistrationBegins the passkey registration ceremony.Relying party configured
Finish Passkey RegistrationCompletes the passkey registration ceremony.Start Passkey Registration must have run
Generate OTPGenerates a time-limited OTP and forwards it for delivery.-
Verify OTPVerifies the OTP code the user entered.Generate OTP must have run
Generate Magic LinkGenerates a magic link authentication token and sends it to the user.-
Verify Magic LinkVerifies the magic link token submitted by the user.Generate Magic Link must have run
Identify UserLooks up a user by identifier in the user store.-
Resolve UserHandles disambiguation when multiple users match an identifier.-
Resolve Federated UserResolves ambiguous federated user after social login.OAuth/OIDC executor must have run; Identify User must have run
User Type ResolverResolves the user type based on configured rules.User type configured on the application
ProvisioningCreates or updates the user record in the store.-
Attribute CollectorCollects additional user attributes defined in the user type.-
Validate Attribute UniquenessChecks that unique attributes are not already registered.User Type Resolver must have run
Set CredentialsSets credentials (e.g., password) for an existing user.-
OpenID4VP VerifyInitiates an OpenID4VP credential presentation request, returns a QR code and deep link for the user's wallet, and polls until the credential is verified.OpenID4VP service configured in ThunderID
AuthorizationEvaluates authorization policies for the current user.-
User ConsentRecords explicit user consent for defined scopes or terms.-
Validate PermissionChecks that the request has required scope/permissions.-
OU CreationCreates an organizational unit for the user.-
Resolve OUResolves or selects an organizational unit based on strategy.Varies by strategy
Generate InviteGenerates an invite link for user registration.-
Verify InviteVerifies an invite token before completing registration.Generate Invite must have run
Send EmailSends email using configured templates.Email service configured
Send SMSSends SMS using configured templates and sender.SMS sender configured
Auth Assertion GeneratorGenerates the final authentication assertion on successful flow completion.User authenticated; assertion settings configured
HTTP RequestMakes HTTP requests to external endpoints.-
tip

Executor Reference

Providing Inputs to Executor

Each executor expects certain inputs required for its execution. These inputs can be configured as node inputs of the associated TASK EXECUTION node. If no node inputs are configured, the executor falls back to any default inputs defined in the executor itself.

Executors do not collect inputs directly. All required inputs must already be available when the executor runs. Inputs can come from:

  • A View earlier in the flow: user-provided data such as an email address or username.
  • A previous executor's output: data produced by an earlier step, such as a resolved user ID.

What happens when an input is missing: If a required input is missing, the executor may either trigger an OnIncomplete handler node or prompt the user for missing inputs.

Declaring inputs in the Flow Builder

Use the Add Input option in the executor's properties to specify which inputs the executor expects. The execution validates that each declared input is available from a preceding View or executor output.

View and Executor Pairings

Each View node (Step or Widget) collects user input that must be read by a specific Executor. If you place a View on the canvas without connecting its required Executor, the flow will stall at the yellow incomplete dot. The table below shows which Executor is required or recommended for each View, and any prerequisites that must be satisfied first.

View (Step or Widget)Required Executor(s)PrerequisitesNotes
Username + Password (Step or Widget)Identifier + Password-The executor reads the username and password fields submitted by the View.
SMS OTP View (Step or Widget)Generate OTP → Send SMS → Verify OTPUser's mobile number must be on recordPlace Generate OTP and Send SMS before the View (no user input needed); place Verify OTP after to validate the code the user enters.
Passkey View (Step or Widget)Request Passkey → Verify Passkey-Both executors are required as a pair. Request Passkey issues the challenge; Verify Passkey validates the response.
Consent View (Step)User ConsentUser must be authenticatedThe executor reads the consent decisions submitted by the View. The user's ID must already be in the flow context.
Continue with Google (Widget)Google-Redirect-based. The executor handles the OAuth redirect and reads the authorization code returned by Google.
Continue with GitHub (Widget)GitHub-Redirect-based. The executor handles the OAuth redirect and reads the authorization code returned by GitHub.
Blank View (with attribute fields)Attribute CollectorUser must be authenticatedUse when collecting profile attributes required by the user type. The executor checks the user's existing attributes and prompts only for what is missing.
EUDI Wallet View (Widget)OpenID4VP VerifyOpenID4VP service configuredThe executor initiates the request and surfaces QR code data. The view renders the QR code and polls until the wallet responds.

Some Executors run entirely in the background and do not need a preceding View to gather user input.

ExecutorPlacementPrerequisites
Auth Assertion GeneratorLast Executor before the END node in every authentication flowUser must be authenticated by prior Executors
AuthorizationAfter authentication Executors, before Auth Assertion GeneratorUser must be authenticated
Generate OTPBefore the SMS OTP View-
Send SMSAfter Generate OTP, before the SMS OTP ViewUser's mobile number must be on record
Send EmailAfter the flow collects the recipient emailRecipient email input; inviteLink for invite and self-registration templates
ProvisioningAfter identity collection in registration flows-
Identify UserEarly in the flow, after the user submits an identifier-
User Type ResolverAfter Identify User-
OU CreationAfter Provisioning in registration flowsOU name and handle inputs must be present in the flow context. Accepts an optional parentOuId property (see below).

Executor Details and Configuration

Expand each executor below for a full reference, including when to use it, prerequisites, inputs, failure conditions, and node property configuration.

Credential Verification and Management

Identifier + Password

Verifies a provided identifier and a password against stored values. This is the standard executor for username-password based authentication.

When to use: Any flow that collects a user identifier (username, email, etc.) and password from the user. Pair with the Username + Password View or Widget, and configure the identifier field to match your chosen identifier type.

Prerequisites: None.

View pairing: Requires the Username + Password View (Step or Widget). Configure the identifier field in the View to match your chosen identifier type (e.g., username or email).

How it works in registration flows: When used in a registration flow, the executor checks whether the user already exists. If found, it fails with "User already exists." If not found, it returns complete without authenticating, allowing the flow to proceed to provisioning. If a user ID was pre-resolved earlier in the flow (e.g., via the Resolve User executor), the executor skips the identifier prompt and only requests the password.

Input Configuration:

  • User identifier (required): a field or attribute that can identify a user. Default: username. Can be overridden with any supported identifier (e.g., email). Can be collected from the user via an identifier View. Skipped automatically if userID is already resolved in runtime data from a prior executor.
  • User credential (required): a field or attribute that represents the user's credential. Default: password. Can be overridden to use a different credential attribute. Can be collected from the user via a credential View.

Example:

{
"id": "credentials_auth",
"type": "TASK_EXECUTION",
"executor": {
"name": "CredentialsAuthExecutor",
"inputs": [
{
"identifier": "username",
"type": "TEXT_INPUT",
"required": true
},
{
"identifier": "password",
"type": "PASSWORD",
"required": true
}
]
}
}

Failure conditions:

  • Invalid credentials
  • User not found
  • Authentication service error
Set Credentials

Sets credentials (e.g., password) for an existing user. Typically used in registration flows after Provisioning to let the new user set their own password, or in account management flows where an authenticated user updates their credentials.

When to use: Registration flows after user account creation (after Provisioning), or account management flows where an authenticated user is changing their credentials.

Prerequisites:

  • userID (required): must be present in runtime data before this executor runs. Not a configurable node input. Populated by:
    • In registration flows: the Provisioning executor, which creates the user and sets userID
    • In account management flows: any authentication executor (Identifier + Password, OAuth, SMS OTP, etc.) or user resolution executor

View pairing: Pair with a Blank View configured with credential input fields (e.g., a Password field). The View collects the new credential value and passes it to this executor.

Input Configuration:

  • Credential value (required): a field or attribute that represents the new credential to set for the user. Default: password. Can be collected from the user via a credential View.

Example:

{
"id": "set_credentials",
"type": "TASK_EXECUTION",
"executor": {
"name": "CredentialSetter",
"inputs": [
{
"identifier": "password",
"type": "PASSWORD",
"required": true
}
]
}
}

Failure conditions:

  • userID not present in runtime data
  • Credential value not provided
  • User not found in the user store
  • Credential update failed

Federated Authentication

After a successful federated authentication, these executors set the entityState runtime key from the account-linking result: exists when the federated identity resolves to a unique existing local user. A later node can branch on {{ctx(entityState)}}.

OAuth

Authenticates users via a generic OAuth 2.0 provider. After the OAuth redirect, ThunderID exchanges the authorization code for an access token and user profile information.

When to use: Any flow offering single sign-on via a custom OAuth provider not covered by pre-built executors.

Prerequisites: An OAuth identity provider must be configured in ThunderID with a valid client ID, client secret, and redirect URI.

View pairing: Pair with a Continue with [Provider Name] Widget. No user input is needed beyond the redirect: the Widget initiates the redirect and the executor reads the returned code.

Executor properties:

PropertyUI LabelRequiredDescription
idpIdConnectionYesThe identity provider ID configured in ThunderID
allowAuthenticationWithoutLocalUserAllow Authentication Without Local UserNoAllow authentication when no local user exists (creates federated link)
allowRegistrationWithExistingUserAllow Registration With Existing UserNoAllow registration if the account already exists
allowCrossOUProvisioningAllow Cross-OU ProvisioningNoAllow creating user in a different OU from the default

Input Configuration:

  • code (required): the authorization code returned by the OAuth provider after redirect. Default: code. If not provided, the executor redirects to the OAuth provider to start the authorization flow and get a code.

Example:

{
"id": "oauth_login",
"type": "TASK_EXECUTION",
"properties": {
"idpId": "<idp-id>",
"allowAuthenticationWithoutLocalUser": false,
"allowRegistrationWithExistingUser": false,
"allowCrossOUProvisioning": false
},
"executor": {
"name": "OAuthExecutor"
},
"onSuccess": "auth_assert",
"onFailure": "end"
}

Failure conditions:

  • User denied consent on the OAuth provider's authorization screen
  • Authorization code expired or already used
  • Client credentials or redirect URI mismatch
  • OAuth identity provider not configured in ThunderID
OIDC

Authenticates users via a generic OIDC provider. After the OIDC redirect, ThunderID exchanges the authorization code for an ID token and extracts identity claims.

When to use: Any flow offering single sign-on via a custom OIDC provider (Enterprise IdP, custom authorization server, etc.).

Prerequisites: An OIDC identity provider must be configured in ThunderID with a valid client ID, client secret, and redirect URI.

View pairing: Pair with a Continue with [Provider Name] Widget. No user input is needed beyond the redirect: the Widget initiates the redirect and the executor reads the returned code.

Executor properties:

PropertyUI LabelRequiredDescription
idpIdConnectionYesThe identity provider ID configured in ThunderID
allowAuthenticationWithoutLocalUserAllow Authentication Without Local UserNoAllow authentication when no local user exists (creates federated link)
allowRegistrationWithExistingUserAllow Registration With Existing UserNoAllow registration if the account already exists
allowCrossOUProvisioningAllow Cross-OU ProvisioningNoAllow creating user in a different OU from the default

Input Configuration:

  • code (required): the authorization code returned by the OIDC provider after redirect. Default: code. If not provided, the executor redirects to the OIDC provider to start the authorization flow and get a code.

Example:

{
"id": "oidc_login",
"type": "TASK_EXECUTION",
"properties": {
"idpId": "<idp-id>",
"allowAuthenticationWithoutLocalUser": false,
"allowRegistrationWithExistingUser": false,
"allowCrossOUProvisioning": false
},
"executor": {
"name": "OIDCAuthExecutor"
},
"onSuccess": "auth_assert",
"onFailure": "end"
}

Failure conditions:

  • User denied consent on the provider's authorization screen
  • Authorization code expired or already used
  • Client credentials or redirect URI mismatch
  • OIDC identity provider not configured in ThunderID
Google

Authenticates users via Google OIDC. After the OAuth redirect, ThunderID exchanges the authorization code for an ID token and extracts identity claims.

When to use: Any flow offering "Sign in with Google."

Prerequisites: A Google identity provider must be configured in ThunderID with a valid client ID, client secret, and redirect URI.

View pairing: Pair with the Continue with Google Widget. No user input is needed beyond the redirect: the Widget initiates the redirect and the executor reads the returned code.

Executor properties:

PropertyUI LabelRequiredDescription
idpIdConnectionYesThe identity provider ID configured in ThunderID
allowAuthenticationWithoutLocalUserAllow Authentication Without Local UserNoAllow authentication when no local user exists (creates federated link)
allowRegistrationWithExistingUserAllow Registration With Existing UserNoAllow registration if the account already exists
allowCrossOUProvisioningAllow Cross-OU ProvisioningNoAllow creating user in a different OU from the default

Input Configuration:

  • code (required): the authorization code returned by Google after user consent. Default: code

Example:

{
"id": "google_login",
"type": "TASK_EXECUTION",
"properties": {
"idpId": "<idp-id>",
"allowAuthenticationWithoutLocalUser": false,
"allowRegistrationWithExistingUser": false,
"allowCrossOUProvisioning": false
},
"executor": {
"name": "GoogleOIDCAuthExecutor"
},
"onSuccess": "auth_assert",
"onFailure": "end"
}

Failure conditions:

  • User denied consent on Google's authorization screen
  • Authorization code expired or already used
  • Client credentials or redirect URI mismatch in the Google OAuth application
  • Google identity provider not configured in ThunderID
GitHub

Authenticates users via GitHub OAuth 2.0. After the redirect, ThunderID exchanges the authorization code for an access token and fetches the user's profile.

When to use: Developer-facing applications where GitHub is the preferred sign-in option.

Prerequisites: A GitHub identity provider must be configured in ThunderID with a valid client ID, client secret, and redirect URI.

View pairing: Pair with the Continue with GitHub Widget. No user input is needed beyond the redirect: the Widget initiates the redirect and the executor reads the returned code.

Executor properties:

PropertyUI LabelRequiredDescription
idpIdConnectionYesThe identity provider ID configured in ThunderID
allowAuthenticationWithoutLocalUserAllow Authentication Without Local UserNoAllow authentication when no local user exists (creates federated link)
allowRegistrationWithExistingUserAllow Registration With Existing UserNoAllow registration if the account already exists
allowCrossOUProvisioningAllow Cross-OU ProvisioningNoAllow creating user in a different OU from the default

Input Configuration:

  • code (required): the authorization code returned by GitHub after user authorization. Default: code

Example:

{
"id": "github_login",
"type": "TASK_EXECUTION",
"properties": {
"idpId": "<idp-id>",
"allowAuthenticationWithoutLocalUser": false,
"allowRegistrationWithExistingUser": false,
"allowCrossOUProvisioning": false
},
"executor": {
"name": "GithubOAuthExecutor"
},
"onSuccess": "auth_assert",
"onFailure": "end"
}

Failure conditions:

  • User denied authorization on GitHub
  • Redirect URI not registered in the GitHub OAuth application
  • Authorization code expired
  • GitHub identity provider not configured in ThunderID

Passkeys (WebAuthn)

Request Passkey

Issues a WebAuthn authentication challenge. The browser forwards it to the user's authenticator (device biometric, hardware key, etc.), which signs the challenge.

When to use: First executor in a passkey sign-in step, immediately before the Passkey View. Always paired with Verify Passkey.

Prerequisites:

  • userID (optional): if present in runtime data, the challenge targets only passkeys registered for that user. If absent, a usernameless challenge is issued and any registered passkey can respond. Populated by any prior authentication executor.
  • relyingPartyId node property must be set to the domain where the passkey is used (e.g., app.example.com). The executor hard-fails if missing.

View pairing: Must be followed by the Passkey View, which collects the signed authenticator response.

Executor properties:

PropertyUI LabelRequiredDescription
relyingPartyIdRelying Party IDYesThe relying party domain (e.g. app.example.com)
relyingPartyNameRelying Party NameNoDisplay name of the relying party (e.g. My App)
authenticatorSelection-NoAuthenticator selection criteria object with fields: authenticatorAttachment, requireResidentKey, residentKey, userVerification
attestation-NoAttestation conveyance preference (e.g. none, direct, indirect). Defaults to none.

Input Configuration: None. This executor generates a challenge; it does not collect inputs from the user.

Example:

{
"id": "request_passkey",
"type": "TASK_EXECUTION",
"properties": {
"relyingPartyId": "app.example.com",
"relyingPartyName": "My App"
},
"executor": {
"name": "PasskeyAuthExecutor",
"mode": "challenge"
},
"onSuccess": "passkey_view",
"onFailure": "end"
}

Failure conditions:

  • relyingPartyId node property missing or empty
  • No passkeys registered for the user (when userID is provided)
  • Passkey service error
Verify Passkey

Validates the cryptographic response returned by the user's authenticator against the session token issued by Request Passkey.

When to use: Immediately after the Passkey View, always paired with Request Passkey.

Prerequisites:

  • passkeySessionToken (required): set in runtime data by Request Passkey. Not a configurable node input. The executor fails if absent.
  • userID (optional): used in usernameless flows when the authenticator returns a user handle that needs to be resolved.

View pairing: Must be immediately preceded by the Passkey View, which collects the signed response from the authenticator.

Input Configuration:

  • credentialId (required): the credential ID returned by the authenticator. Default: credentialId
  • clientDataJSON (required): the client data from the authenticator response. Default: clientDataJSON
  • authenticatorData (required): the authenticator data from the response. Default: authenticatorData
  • signature (required): the cryptographic signature from the response. Default: signature
  • userHandle (optional): the user handle from the authenticator; used in usernameless flows. Default: userHandle

Example:

{
"id": "verify_passkey",
"type": "TASK_EXECUTION",
"executor": {
"name": "PasskeyAuthExecutor",
"mode": "verify"
},
"onSuccess": "auth_assert",
"onFailure": "end"
}

Failure conditions:

  • Signature verification failed (invalid or tampered response)
  • Session token missing (Request Passkey did not run)
  • User not found after successful verification

Retry behavior: If the passkey response is invalid (client error), the executor returns INCOMPLETE instead of failing the flow, allowing the user to retry by providing the required inputs again.

Start Passkey Registration

Begins the WebAuthn credential registration ceremony and returns PublicKeyCredentialCreationOptions to the browser.

When to use: Registration flows that include passkey enrollment, or within an authenticated session when adding a passkey to an existing account. Always paired with Finish Passkey Registration.

Prerequisites:

  • userID (required): must be present in runtime data. The executor hard-fails if missing. Populated by Provisioning (for new users during registration) or any authentication executor (for existing users adding a passkey).
  • relyingPartyId node property must be set to the domain where passkeys will be registered (e.g., app.example.com). The executor hard-fails if missing.

Executor properties:

PropertyUI LabelRequiredDescription
relyingPartyIdRelying Party IDYesThe relying party domain (e.g. app.example.com)
relyingPartyNameRelying Party NameNoDisplay name of the relying party (e.g. My App)
authenticatorSelection-NoAuthenticator selection criteria object with fields: authenticatorAttachment, requireResidentKey, residentKey, userVerification
attestation-NoAttestation conveyance preference (e.g. none, direct, indirect). Defaults to none.

Input Configuration: None. This executor generates the registration ceremony options and does not collect user inputs.

Example:

{
"id": "start_passkey_reg",
"type": "TASK_EXECUTION",
"properties": {
"relyingPartyId": "app.example.com",
"relyingPartyName": "My App"
},
"executor": {
"name": "PasskeyAuthExecutor",
"mode": "register_start"
},
"onSuccess": "finish_passkey_reg",
"onFailure": "end"
}

Failure conditions:

  • userID not available
  • relyingPartyId node property missing or empty
  • Passkey service error
Finish Passkey Registration

Completes the WebAuthn registration ceremony and stores the new credential in the user's passkey list.

When to use: Immediately after the user's device returns the newly created credential. Always follows Start Passkey Registration.

Prerequisites:

  • passkeySessionToken (required): set in runtime data by Start Passkey Registration. Not a configurable node input. The executor fails if absent.

View pairing: Must be immediately preceded by the Passkey View, which collects the user's newly created credential.

Input Configuration:

  • credentialId (required): the credential ID of the newly created passkey. Default: credentialId
  • clientDataJSON (required): the client data from the registration response. Default: clientDataJSON
  • attestationObject (required): the attestation object from the registration response. Default: attestationObject
  • credentialName (optional): a user-friendly label for the passkey. Default: credentialName (falls back to "Passkey" if empty)

Example:

{
"id": "finish_passkey_reg",
"type": "TASK_EXECUTION",
"executor": {
"name": "PasskeyAuthExecutor",
"mode": "register_finish"
},
"onSuccess": "auth_assert",
"onFailure": "end"
}

Failure conditions:

  • Session token missing (Start Passkey Registration did not run)
  • Credential already registered
  • Invalid credential: allow the user to retry by providing the required inputs

OTP

Generate OTP

Generates a time-limited OTP, stores the session token in runtime data, and forwards the OTP code to downstream delivery executors via ForwardedData. Runs in the background: no user interaction at this step.

When to use: Any step requiring OTP-based verification. Place this executor before the delivery executor (e.g., Send SMS) and the OTP input View. Always paired with Verify OTP.

How it identifies the user:

The executor resolves the OTP recipient in this order:

  1. UserID from authenticated context: if a prior executor (e.g., Identifier + Password) has already authenticated the user, the userID is read directly and the OTP is keyed to that user.
  2. UserID from declared inputs: if the user is not yet authenticated, the executor uses the declared node inputs (e.g., mobile_number) to look up the user in the user store.
  3. Registration flows: if no user is found and the flow type is registration, the first declared phone or email input value is used directly as the OTP recipient (since no user exists yet).

Input Configuration:

  • mobile_number (optional): used to look up the user in the user store by their phone number. Required in non-authenticated flows where phone number is the user identifier.

Executor properties:

PropertyUI LabelRequiredDescription
maxAttempts-NoMaximum OTP generation attempts before the flow fails. Default: 3

Example:

{
"id": "generate_otp",
"type": "TASK_EXECUTION",
"executor": {
"name": "OTPExecutor",
"mode": "generate"
},
"onSuccess": "send_sms"
}

Failure conditions:

  • User not found for the provided phone number
  • User not identified and no identifying input declared
  • Maximum OTP generation attempts exceeded (default: 3)
  • OTP service error
Verify OTP

Validates the OTP code the user entered against the session token stored by Generate OTP.

When to use: Immediately after the OTP input View. Always follows Generate OTP in the same flow.

Prerequisites:

  • otpSessionToken (required): set in runtime data by Generate OTP. Not a configurable node input. The executor fails if absent.

View pairing: Must be immediately preceded by the OTP input View, which collects the OTP code the user received.

Input Configuration:

  • otp (required): the OTP code entered by the user. Default: otp

Executor properties: None.

Example:

{
"id": "verify_otp",
"type": "TASK_EXECUTION",
"executor": {
"name": "OTPExecutor",
"mode": "verify"
},
"onSuccess": "auth_assert",
"onFailure": "end"
}

Failure conditions:

  • OTP code incorrect: allow the user to retry (limit: 3 attempts)
  • OTP session token missing (Generate OTP did not run)
  • OTP expired (user waited too long to enter it)
  • Authentication service error

Retry limit: The maximum number of incorrect OTP attempts before the flow fails is 3 (fixed).

Generate Magic Link

Generates a time-limited magic link token and forwards it to a delivery executor (Send Email or Send SMS). Runs in the background: no user interaction at this step.

When to use: Passwordless authentication or registration flows where users sign in by clicking an emailed or SMS-delivered link. Always paired with Verify Magic Link in a separate verify flow.

Prerequisites: None.

Input Configuration: None. The destination email or phone is resolved from prior user inputs or user store. This executor has no registered node input default. A Send Email or Send SMS executor must follow this one to deliver the generated link.

Executor properties:

PropertyUI LabelRequiredDescription
tokenExpiry-NoToken validity period in seconds. Defaults to the magic link service default if absent.
magicLinkURL-NoURL prefix for the generated magic link. Defaults to the service-configured URL if absent.

Example:

{
"id": "generate_magic_link",
"type": "TASK_EXECUTION",
"executor": {
"name": "MagicLinkExecutor",
"mode": "generate"
},
"onSuccess": "send_email",
"onFailure": "end"
}

Failure conditions:

  • In registration flows, if the user already exists (token generation skipped silently to prevent enumeration)
  • Invalid destination attribute configuration
Verify Magic Link

Validates the magic link token the user received and authenticates them. Runs in a separate flow from Generate Magic Link, triggered when the user clicks the link.

When to use: After a user clicks the magic link from their email/SMS. Always follows Generate Magic Link (in a prior flow).

Prerequisites:

  • A magic link token must have been generated and delivered by Generate Magic Link in a prior flow execution. The token is typically passed as a URL query parameter when the user clicks the link.

Input Configuration:

  • token (required): the magic link token from the URL. Default: token

Example:

{
"id": "verify_magic_link",
"type": "TASK_EXECUTION",
"executor": {
"name": "MagicLinkExecutor",
"mode": "verify"
},
"onSuccess": "auth_assert",
"onFailure": "end"
}

Failure conditions:

  • Token mismatch (wrong token provided)
  • Token expired (user waited too long to click the link)
  • No token provided
  • Token invalid or not found (Generate Magic Link did not run)
  • User lookup failed after token validation

User Provisioning and Attributes

Provisioning

Creates a new user record in the user store. This executor persists the user's identity during registration flows. The executor follows the user type schema: required attributes are determined by the user type resolved earlier in the flow.

When to use: Registration flows, after the User Type Resolver has run.

Prerequisites:

  • userType (required): must be present in runtime data. Set by User Type Resolver or by a previous operation. Defines which attributes are required for the new user.
  • defaultOUID or ouId (required): must be present in runtime data. Set by User Type Resolver or OU Resolver. Determines which OU the new user belongs to.

How attribute collection works: The executor fetches required and optional attributes from the user type schema. Attributes already present in user inputs or federated accounts are considered satisfied. Only missing required attributes are prompted. Optional attributes are only prompted if includeOptional is enabled in node properties.

Prompt batching: If maxPerPrompt is set, the executor forwards only that many missing inputs per prompt cycle, cycling through them across multiple flow iterations.

Cross-OU provisioning: When allowCrossOUProvisioning is set and the user exists in a different OU, the executor creates the user in the target OU instead of failing.

Executor properties:

PropertyUI LabelRequiredDefaultDescription
allowCrossOUProvisioningAllow Cross-OU ProvisioningNofalseAllow creating the user in a different OU from the default when the user already exists in another OU
assignGroupAssign GroupNo-Group ID to automatically assign the new user to
assignRoleAssign RoleNo-Role ID to automatically assign the new user to
includeOptional-NofalseWhether to prompt for optional (non-credential) schema attributes
includeOptionalCredentialsInclude Optional CredentialsNofalseWhether to prompt for optional credential attributes
maxPerPrompt-No0 (all at once)Maximum number of missing inputs to forward per prompt cycle

Input Configuration: None. Required user attributes are dynamically determined from the user type schema, not registered as fixed node input defaults.

Example:

{
"id": "provisioning",
"type": "TASK_EXECUTION",
"properties": {
"allowCrossOUProvisioning": false,
"assignGroup": "",
"assignRole": ""
},
"executor": {
"name": "ProvisioningExecutor"
},
"onSuccess": "set_credentials",
"onFailure": "error_prompt",
"onIncomplete": "attr_form"
}

Failure conditions:

  • userType or OU ID not available (User Type Resolver did not run)
  • User already exists with a conflicting unique attribute (in registration flows, prompts for a different value)
  • Required attribute validation failure
  • User store error
Attribute Collector

Checks the authenticated user's existing profile attributes against the node's configured required inputs, then prompts only for what is missing. Skips automatically in registration flows.

When to use: Authentication flows where you want to progressively collect profile attributes from returning users without re-prompting for data they have already provided.

Prerequisites:

  • userID (required): must be present in runtime data. Set by any authentication executor. The executor fails immediately if missing.
  • Only runs in authentication flows, but automatically skips (returns complete immediately) in registration flows.

View pairing: Connect a Blank View (with the required attribute fields) to receive the incomplete path. The executor routes back to the View only for attributes the user has not yet provided.

How missing-input resolution works:

  1. Checks the user's authenticated attributes.
  2. Falls back to the user's stored profile in the user store.
  3. Only attributes absent from both sources are added to the prompt.

Input Configuration: No registered defaults. Configure any attribute identifiers as node inputs; the executor checks those attributes and prompts only for missing ones.

Example:

{
"id": "attr_collector",
"type": "TASK_EXECUTION",
"executor": {
"name": "AttributeCollector",
"inputs": [
{
"identifier": "email",
"type": "TEXT_INPUT",
"required": true
}
]
},
"onSuccess": "next_step",
"onFailure": "end",
"onIncomplete": "attr_form"
}

Failure conditions:

  • User not authenticated
  • userID missing from context
  • User not found in the user store
  • User store update error

Identity and User Resolution

Identify User

Looks up an existing user in the store by a submitted identifier (email, username, phone, etc.). Operates in strict single-match mode: fails if multiple users match the identifier.

When to use:

  • Early in authentication flows to resolve the user before credential verification
  • In social login flows to check whether a federated account is already linked
  • In registration flows to detect duplicate identifiers

Prerequisites: None.

View pairing: Typically follows a View that collects an identifier (email, username, etc.). In federated flows, no View is needed: the identifier comes from the social executor's output.

Input Configuration: No registered defaults. Configure the identifier attribute(s) (e.g., email, username, mobile_number) as node inputs to specify which fields are used for the user store lookup.

Example:

{
"id": "identify_user",
"type": "TASK_EXECUTION",
"executor": {
"name": "IdentifyingExecutor",
"mode": "identify",
"inputs": [
{
"identifier": "username",
"type": "TEXT_INPUT",
"required": true
}
]
},
"onSuccess": "credentials_auth",
"onFailure": "end",
"onIncomplete": "prompt_identifier"
}

Failure conditions:

  • No user found matching the submitted identifier (routes to incomplete: use this path to branch to registration)
  • Multiple users match (ambiguous match: hard failure, routes to failure)
  • User store lookup error
Resolve User

Resolves an ambiguous user match by presenting disambiguation options to the user, then re-filtering until exactly one user is identified. Operates in resolve mode with support for multi-step disambiguation.

When to use:

  • When Identify User found multiple candidates and the flow needs to disambiguate
  • When different OUs, user types, or other attributes need to be selected to narrow down matches
  • In multi-tenant scenarios where the same email may exist under different organizations

Prerequisites:

  • Candidate users (optional): if stored in runtime data from a prior Identify User execution that returned an ambiguous match, the executor filters from those candidates in-memory. Otherwise, it performs a fresh search against the user store. Not a configurable node input.

View pairing: Pair with a Blank View configured with disambiguation inputs (e.g., OU handle selector, user type selector).

How disambiguation works:

  1. Checks for stored candidate users from a previous Identify User execution. If none exist, performs a fresh search using the provided attributes.
  2. Builds a filter from the user's selected attributes (from View inputs)
  3. Filters candidates against the selected attributes
  4. If multiple candidates remain, extracts new disambiguation options and prompts again
  5. If exactly one candidate matches, returns that user as authenticated

Input Configuration: No registered defaults. Configure disambiguation attribute identifiers (e.g., ouHandle, userType) as node inputs; the executor filters candidates by whatever is provided.

Example:

{
"id": "resolve_user",
"type": "TASK_EXECUTION",
"executor": {
"name": "IdentifyingExecutor",
"mode": "resolve",
"inputs": [
{
"identifier": "ouHandle",
"type": "TEXT_INPUT",
"required": true
}
]
},
"onSuccess": "prompt_password",
"onFailure": "end",
"onIncomplete": "prompt_disambiguate"
}

Failure conditions:

  • No candidates available (Identify User did not find multiple matches)
  • User selection does not match any candidate
  • Candidates are indistinguishable (no further disambiguation options available)
Resolve Federated User

Resolves an ambiguous user after federated authentication (OAuth/OIDC). Reads candidate users stored by Identify User and filters them based on user-selected attributes, then authenticates the matched user.

When to use: After federated auth (Google, GitHub, OAuth, OIDC) when Identify User found multiple candidates. This executor reuses the federated identity verified by the auth step rather than re-authenticating.

Prerequisites:

  • sub (required): the federated subject verified by a prior OAuth/OIDC executor (Google, GitHub, OAuth, OIDC). Set in runtime data by the federated auth executor; not a configurable node input.
  • Candidate users (required): must be stored in runtime data from a prior Identify User execution that returned an ambiguous match.

View pairing: Pair with a Blank View configured with disambiguation inputs (e.g., OU handle, user type).

How it works:

  1. Reads the federated subject set by the OAuth/OIDC executor
  2. Reads candidate users from the prior Identify User execution
  3. Filters candidates using user-provided selection criteria (from View)
  4. On successful match, links the federated identity to the resolved user without re-authenticating

Input Configuration: No registered defaults. Configure disambiguation attribute identifiers (e.g., ouHandle, userType) as node inputs.

Example:

{
"id": "resolve_federated_user",
"type": "TASK_EXECUTION",
"executor": {
"name": "FederatedAuthResolverExecutor",
"inputs": [
{
"identifier": "ouHandle",
"type": "TEXT_INPUT",
"required": true
}
]
},
"onSuccess": "auth_assert",
"onFailure": "end",
"onIncomplete": "prompt_disambiguate"
}

Failure conditions:

  • No federated subject found (OAuth/OIDC executor did not run)
  • No candidate users available (Identify User did not find multiple matches)
  • User selection does not match any candidate
  • User found but federated identity cannot be linked
User Type Resolver

Determines the user type and default OU for the flow. The resolved values are used by Provisioning and other downstream executors. Behavior differs by flow type.

When to use: At the start of registration and user onboarding flows, before Provisioning. Also used in authentication flows to validate that the application allows the resolved user type.

Prerequisites:

  • The application must have at least one user type configured in ThunderID Console (Applications → allowed user types).
  • In registration flows, at least one user type must have self-registration enabled.

Behavior by flow type:

Flow typeBehavior
RegistrationResolves from the application's allowed user types, checking self-registration eligibility. Auto-selects if only one eligible type; prompts with a SELECT input if multiple are available.
AuthenticationValidates that the application has at least one allowed user type configured; completes without further action.
User OnboardingLists all user types in the system, optionally filtered by node properties or by a prior resolved OU.

Executor properties:

PropertyUI LabelRequiredDescription
allowedUserTypesAllowed User TypesNoArray of user type names to restrict the eligible set to a subset of the application's allowed types

Input Configuration:

  • userType (required): the user type selection. Default: userType. If only one eligible user type exists, the executor resolves it automatically without prompting.

Example:

{
"id": "user_type_resolver",
"type": "TASK_EXECUTION",
"properties": {
"allowedUserTypes": []
},
"executor": {
"name": "UserTypeResolver"
},
"onSuccess": "provisioning",
"onFailure": "end",
"onIncomplete": "user_type_form"
}

Failure conditions:

  • No allowed user types configured for the application
  • No user types with self-registration enabled (registration flows)
  • User-selected type is not in the allowed list
  • User type not found in the system
Resolve OU

Resolves or selects an organizational unit based on a configured strategy. Overwrites the default OU set by User Type Resolver when applicable.

When to use: User onboarding flows where the target OU must be determined dynamically based on the caller, administrative selection, or system hierarchy.

Prerequisites: Depends on the resolution strategy (see table below).

Executor properties:

PropertyUI LabelRequiredDefaultBehavior
resolveFromResolve FromYes-Strategy for OU resolution: caller, prompt, or promptAll

Resolution Strategies:

caller strategy:

  • Extracts the caller's OU from the security context
  • Overwrites the default OU with the caller's OU
  • When to use: B2B flows where the administrator's OU determines where the new user is created
  • Prerequisites: The caller must have an OU in their security context (automatically extracted from the incoming request)
  • Setup: No special setup needed. The OU is read from the calling account's security context
  • Failure conditions: Caller's OU not found in security context

prompt strategy:

  • Prompts the user to select a child OU if the default OU has children
  • When to use: Delegated onboarding where an admin selects which sub-organization to create the user in
  • Prerequisites:
    • User Type Resolver must have run first: resolves defaultOUID
    • A Blank View with OU selection input must follow this executor, to collect the user's OU selection
  • Setup:
    1. Place User Type Resolver before this executor in registration flows
    2. Add a Blank View immediately after this executor with an OU selection component
    3. This executor will prompt only if the default OU has child OUs. Otherwise, it completes silently
  • Failure conditions: Default OU not found; selected OU is not a child of the default

promptAll strategy:

  • Shows the full OU tree from root, independent of User Type Resolver
  • When to use: Advanced onboarding with full OU tree visibility
  • Prerequisites: A Blank View with OU selection component must follow this executor
  • Setup:
    1. No need to run User Type Resolver first (unlike prompt strategy)
    2. Add a Blank View immediately after this executor with an OU selection component
    3. The executor will display the complete OU tree starting from root
  • Failure conditions: OU service error

Input Configuration:

  • ouId (required): the user's selected organizational unit. Used for prompt and promptAll strategies. Default: ouId

Example:

{
"id": "resolve_ou",
"type": "TASK_EXECUTION",
"properties": {
"resolveFrom": "caller"
},
"executor": {
"name": "OUResolverExecutor"
},
"onSuccess": "provisioning",
"onFailure": "end",
"onIncomplete": "ou_selector"
}

Failure conditions:

  • Caller's OU not found in security context (for caller strategy)
  • Selected OU does not exist
  • OU service error
Authorization

Evaluates whether the authenticated user holds the required permissions. The authorized set is used by Auth Assertion Generator to include permissions in the token.

When to use: After authentication and before Auth Assertion Generator, when the application restricts access based on permissions or roles.

Prerequisites: None. This executor has no registered prerequisites. In registration flows, it completes immediately without checks.

How permission resolution works:

  1. Reads requested_permissions from user inputs or the OIDC scope parameter (space-separated permission strings).
  2. Resolves the user's group memberships from their profile or by fetching transitive groups.
  3. Calls the authorization service with the user ID, group IDs, and requested permissions.
  4. Passes the authorized permission set to Auth Assertion Generator.

If no permissions are configured, the executor completes immediately with an empty permission set.

Input Configuration: None. The executor reads requested_permissions from user inputs or the OIDC scope; it has no registered node input defaults.

Example:

{
"id": "authorization_check",
"type": "TASK_EXECUTION",
"executor": {
"name": "AuthorizationExecutor"
},
"onSuccess": "auth_assert"
}

Failure conditions:

  • User not authenticated (in non-registration flows)
  • Authorization service error
User Consent

Checks whether the authenticated user has active consent records for the required attributes, prompts if any are missing, and records the user's decisions. Auth Assertion Generator automatically filters token claims to consented attributes when this executor has run.

When to use: OAuth flows where the application requests access to specific user data. Required for privacy regulation compliance in many jurisdictions.

Prerequisites:

  • userID (required): must be present in runtime data. Set by any authentication executor. The executor fails if missing.
  • The application must have consent scopes or claims configured in OAuth/OIDC settings.

View pairing: Pair with the Consent View (Step). The executor forwards consent prompt data to the View via ForwardedData; the View returns consent_decisions as a JSON payload. The executor completes immediately if the user already has active consent records.

How consent resolution works:

  1. Reads required_essential_attributes and required_optional_attributes from the application's assertion configuration.
  2. Calls the consent enforcer to check existing consent records.
  3. If all consents are active, completes immediately: no prompt is needed.
  4. If any consents are missing, forwards the prompt data to the Consent View and awaits the user's decisions.
  5. After decisions are received, records them and makes consentId and consented_attributes available for Auth Assertion Generator.

Executor properties:

PropertyUI LabelRequiredDescription
timeoutConsent Timeout (seconds)NoSeconds before the consent prompt expires. If the user does not respond in time, the executor fails.

Input Configuration:

  • consent_decisions (required): JSON-encoded consent decisions returned by the Consent View. Default: consent_decisions

Example:

{
"id": "user_consent",
"type": "TASK_EXECUTION",
"properties": {
"timeout": "0"
},
"executor": {
"name": "ConsentExecutor"
},
"onSuccess": "auth_assert",
"onFailure": "end",
"onIncomplete": "consent_view"
}

Failure conditions:

  • userID not available
  • User denied one or more essential (required) attributes: hard failure
  • Consent prompt timed out (if timeout is configured)
  • Consent service error
Validate Permission

Validates that the incoming API request has the required permission/scope to proceed. This is a request-level guard distinct from user-level authorization. Used to enforce scope-based access control on administrative or sensitive operations.

When to use: At the start of user onboarding or administrative flows, to prevent unauthorized callers from executing sensitive operations. For example, only admins with user:write scope can start user registration flows.

Prerequisites: None. Permissions are extracted automatically from the incoming request's security context (OAuth token or identity context header).

How it works:

  1. Extracts required scopes from the requiredScopes node property (if configured)
  2. Reads permissions from the incoming request's security context
  3. Checks if the request has at least one of the required permissions
  4. Returns COMPLETE if authorized, FAILURE if not

Executor properties:

PropertyUI LabelRequiredDefaultDescription
requiredScopesRequired ScopesNoSystem rootArray of permission/scope strings required for access. If omitted, defaults to system root scope. Set to [] to allow any authenticated caller.

Input Configuration: None. This executor operates on the request security context and has no configurable node input defaults.

Example:

{
"id": "validate_permission",
"type": "TASK_EXECUTION",
"properties": {
"requiredScopes": ["system"]
},
"executor": {
"name": "PermissionValidator"
},
"onSuccess": "next_step",
"onFailure": "end"
}

Failure conditions:

  • Request lacks one of the required permissions: returns FAILURE with "Insufficient permissions"
  • Request context unavailable: returns FAILURE

Organization Management

OU Creation

Creates a new Organizational Unit. The created OU is available for use by downstream executors.

When to use: B2B self-registration flows, place after Provisioning, once the user account exists.

Prerequisites:

  • None. If parentOuId is not set as a node property, the executor falls back to the default OU ID from runtime data (set by User Type Resolver or OAuth executor). If neither is available, no parent is assigned.

Input Configuration:

  • ouName (required): the display name of the new OU.
  • ouHandle (required): the unique URL-safe identifier for the OU.

Executor properties:

PropertyUI LabelRequiredDefaultDescription
parentOuIdParent OU IDNo-Controls where the new OU is created. A valid UUID creates the OU as a child of that parent; "" (empty string) creates at root level; omitted falls back to the default OU resolved by User Type Resolver.

Example:

{
"id": "ou_creation",
"type": "TASK_EXECUTION",
"properties": {
"parentOuId": "019d94ff-072c-7d7f-a5c5-0a638c1f62bc"
},
"executor": {
"name": "OUExecutor"
},
"onSuccess": "next_node",
"onFailure": "error_prompt"
}

Failure conditions:

  • An OU with the same name already exists at the target parent: re-prompts the user to change the name
  • An OU with the same handle already exists: re-prompts the user to change the handle
  • Specified parentOuId does not exist
  • OU service error

Notifications

Send Email

Sends a transactional email using a configured template. Runs in the background: no user interaction. Supports invite, registration, and recovery scenarios.

When to use:

  • Invite flows: Send an invitation link to a user being added by an administrator.
  • Registration flows: Send a welcome or email-verification message after account creation.
  • Recovery flows: Send a password reset link to the user's registered email.

Prerequisites:

  • email (required): the recipient email address must be resolvable before this executor runs. Not a configurable node input. Resolved in priority order: forwarded data from prior executors (e.g., Generate Invite, Magic Link) → user inputs → user store (looked up via userID). The executor fails if none of these resolve to a non-empty address.
  • An email service must be configured in ThunderID. See SMTP Server Configuration. The executor returns failure status if no email client is configured.

Skip delivery: If skipDelivery is set to true, the executor returns complete immediately without sending anything. Use this in test environments.

Executor properties:

PropertyUI LabelRequiredDefaultDescription
emailTemplateEmail TemplateNouser_inviteThe template scenario to use. If absent or empty, defaults to the user invite template.

Input Configuration: None. The email executor resolves the recipient and template data from runtime context; it has no configurable node input defaults.

Example:

{
"id": "send_email",
"type": "TASK_EXECUTION",
"properties": {
"emailTemplate": "UserInvite"
},
"executor": {
"name": "EmailExecutor",
"mode": "send"
},
"onSuccess": "next_step",
"onFailure": "end"
}

Failure conditions:

  • No email client configured
  • Recipient email address not resolvable from context or user entity
  • Template rendering error
  • SMTP or mail provider error
Send SMS

Sends a transactional SMS using a configured template and sender. Runs in the background: no user interaction. Uses template-based message body rendering.

When to use:

  • Notification flows: Send an SMS reminder, confirmation, or one-time code to the user.
  • Alert flows: Notify the user of account activity or status changes.
  • Registration flows: Send welcome or verification SMS after account creation.

Prerequisites:

  • An SMS sender must be configured in ThunderID. See SMS Providers.
  • senderId node property must be set to the configured sender ID

Recipient resolution order:

  1. A phone-type input from the node's configured inputs (from ForwardedData)
  2. mobile_number from user inputs
  3. Phone attribute from user store (looked up by userID if available)

Phone number validation: Accepts phone numbers in E.164 format and common variations with parentheses, spaces, dashes.

Executor properties:

PropertyUI LabelRequiredDescription
senderIdNotification SenderYesThe notification sender ID configured in ThunderID
smsTemplateSMS TemplateYesThe template name to use for rendering the SMS body

Input Configuration:

  • mobile_number (required): the recipient phone number. Default: mobile_number

Example:

{
"id": "send_sms",
"type": "TASK_EXECUTION",
"properties": {
"smsTemplate": "OTPVerification",
"senderId": "<sender-id>"
},
"executor": {
"name": "SMSExecutor"
},
"onSuccess": "next_step",
"onFailure": "end"
}

Failure conditions:

  • Phone number not found in context or user store
  • senderId not configured in node properties
  • Phone number invalid or undeliverable format
  • SMS service error

Invitations and User Onboarding

Generate Invite

Generates an invite token and link for user registration. Typically paired with Email or SMS Executor to deliver the invite link.

When to use:

  • Administrative onboarding: Admin invites external users to sign up
  • Self-service with email confirmation: User provides email, receives invite link
  • B2B workflows: Partner organization users invited to create accounts

Prerequisites: None.

How it works:

  1. Generates a cryptographically secure invite token
  2. Stores the token for Verify Invite to consume
  3. Generates a full invite link (with token embedded)
  4. Forwards the link to downstream Email or SMS executors via ForwardedData

Input Configuration: None. The recipient email or phone is resolved from prior user inputs or user store. A Send Email or Send SMS executor must follow this one to deliver the invite link.

Example:

{
"id": "invite_generate",
"type": "TASK_EXECUTION",
"executor": {
"name": "InviteExecutor",
"mode": "generate"
},
"onSuccess": "send_invite_email",
"onFailure": "end"
}

Failure conditions:

  • Token generation service error
Verify Invite

Validates an invite token provided by the user and completes the invite verification step.

When to use: After a user receives an invite link and clicks it, verifying the invite token before allowing registration to proceed.

Prerequisites:

  • An invite token must have been generated and delivered by Generate Invite in a prior flow execution. The token is typically passed as a URL query parameter when the user clicks the invite link.

View pairing: The invite link includes the token as a query parameter; the View (if present) extracts it and passes it to the executor.

How it works:

  1. Reads the user-provided invite token from user inputs
  2. Compares it against the stored token from Generate Invite
  3. Returns COMPLETE if tokens match, FAILURE if they do not

Input Configuration:

  • inviteToken (required): the token from the invite link URL. Default: inviteToken

Example:

{
"id": "invite_verify",
"type": "TASK_EXECUTION",
"executor": {
"name": "InviteExecutor",
"mode": "verify",
"inputs": [
{
"identifier": "inviteToken",
"type": "HIDDEN",
"required": true
}
]
},
"onSuccess": "provisioning",
"onFailure": "end"
}

Failure conditions:

  • Stored invite token not found (Generate Invite did not run)
  • User-provided token does not match stored token
  • Token validation error

Advanced Integration

HTTP Request

Executes an HTTP request to an external endpoint, optionally retries on failure, maps response data for downstream executors, and continues the flow.

When to use:

  • Webhook integration: Notify external systems when a user registers
  • Data enrichment: Call an external service to fetch additional user attributes
  • Policy evaluation: Check an external policy engine before allowing flow continuation
  • Custom business logic: Delegate decision-making to a specialized service

Prerequisites: None. Any context data referenced via {{ctx(fieldName)}} placeholders in the URL, headers, or body must be available from prior executors at runtime.

Input Configuration: This executor is entirely configuration-driven; all settings are node properties.

How it works:

  1. Parses HTTP configuration from node properties (URL, method, headers, body, timeout)
  2. Resolves {{ctx(fieldName)}} placeholders in the URL, headers, and body using runtime data and user inputs from prior executors
  3. Sends the HTTP request with optional retry logic based on errorHandling config
  4. Maps response fields for downstream use based on responseMapping configuration
  5. Continues the flow (or fails based on error handling configuration)

Executor properties:

PropertyUI LabelRequiredDefaultDescription
urlURLYes-The endpoint URL (supports placeholders like {{ctx(userId)}})
methodMethodNoGETHTTP method: GET, POST, PUT, DELETE, or PATCH
headersHeadersNo-HTTP headers as a map of strings
bodyRequest BodyNo-Request body (JSON object, only for POST/PUT/PATCH)
timeoutTimeout (seconds)No10sRequest timeout in seconds (max 20s)
responseMappingResponse MappingNo-Map response JSON fields for downstream executor use
errorHandling.failOnErrorFail on ErrorNofalseWhether HTTP error status codes cause flow failure
errorHandling.retryCountRetry CountNo0Number of retries on failure (max 5)
errorHandling.retryDelayRetry Delay (ms)No0Delay between retries in milliseconds (max 5000ms)

Placeholder syntax: Use {{ctx(fieldName)}} to reference data from the flow context. Placeholders are resolved from runtime data first, then user inputs. If a placeholder cannot be resolved, it is kept as-is in the request.

The following fields are always available when the corresponding data exists in the flow context:

PlaceholderSourceDescription
{{ctx(userId)}}Authenticated user or runtime dataThe authenticated user's ID. Available after any authentication or provisioning executor has run.
{{ctx(ouId)}}Authenticated user or runtime dataThe organizational unit ID of the current user. Available after OU resolution or authentication.
{{ctx(ouHandle)}}OU service lookupThe handle of the resolved OU. Fetched automatically when referenced.
{{ctx(ouName)}}OU service lookupThe display name of the resolved OU. Fetched automatically when referenced.
{{ctx(ouDescription)}}OU service lookupThe description of the resolved OU. Fetched automatically when referenced.
{{ctx(clientId)}}OAuth flow contextThe OAuth client ID of the application initiating the flow. Available in OAuth/OIDC authorization flows.

In addition to these fields, any value present in runtime data (set by prior executors) or user inputs (collected from prior Views) can be referenced using the same {{ctx(fieldName)}} syntax (e.g., {{ctx(email)}}, {{ctx(username)}}).

Failure conditions:

  • URL not configured or invalid
  • HTTP request timeout
  • HTTP error status code (4xx, 5xx) when failOnError: true
  • Retries exhausted
  • Response body cannot be parsed as JSON
  • Response mapping references non-existent fields

Example:

{
"id": "publish_ou_event",
"type": "TASK_EXECUTION",
"properties": {
"url": "<webhook-url>",
"method": "POST",
"headers": {
"Content-Type": "application/json"
},
"body": {
"event": "OU_CREATED",
"ouName": "{{ctx(ouName)}}",
"ouId": "{{ctx(ouId)}}",
"ouHandle": "{{ctx(ouHandle)}}"
},
"timeout": 5,
"responseMapping": {
"externalRef": "response.data.referenceId",
"statusCode": "response.status"
},
"errorHandling": {
"failOnError": false,
"retryCount": 2,
"retryDelay": 1000
}
},
"executor": {
"name": "HTTPRequestExecutor"
},
"onSuccess": "next_step",
"onFailure": "end"
}

Verifiable Credentials

OpenID4VP Verify

Initiates an OpenID4VP presentation request and, on each subsequent poll, checks whether the user's wallet has responded with a verified credential. The executor handles the full initiate-and-poll lifecycle: on first execution it generates a QR code and deep link; on each re-entry it checks the request state until the credential is verified or the request expires.

When to use: Any authentication flow that accepts a credential from a digital wallet (for example, an EUDI Wallet Personal Identification Data presentation). Place this executor in a TASK EXECUTION node and route its onIncomplete path to a View that renders the QR code and polls back.

Prerequisites:

  • The OpenID4VP service must be configured in ThunderID with at least one presentation definition (see the OpenID4VP protocol guide).
  • A certificate-backed signing key and an ephemeral encryption key must be registered in the key store.

View pairing: Use the EUDI Wallet widget (or manually pair with a custom View that renders openid4vpWalletUri as a QR code). The View must post back to the same TASK EXECUTION node to trigger the poll step.

Executor properties:

PropertyUI LabelRequiredDefaultDescription
presentation_definition_idPresentation DefinitionNoeudi-pidThe identifier of the presentation definition to request. Must match a definition registered in the server configuration.
allowAuthenticationWithoutLocalUserAllow Auth Without Local UserNofalseWhen true in an authentication flow, sets the userEligibleForProvisioning runtime flag on completion so a downstream Provisioning executor can create the account.

Input Configuration: None. The executor generates the request internally; no user inputs are required.

How it works:

  1. On first execution (no state in runtime data): calls the OpenID4VP service to initiate a new request. Sets runtime data with the request state and surfaces the following additional data:

    • openid4vpWalletUri: the openid4vp:// authorization URI for the wallet (QR code target or deep link)
    • openid4vpClientId: the verifier's client_id
    • openid4vpRequestUri: the signed request object URI Returns INCOMPLETE so the flow pauses at the View node.
  2. On subsequent executions (state present in runtime data): polls the request state. Returns INCOMPLETE again (re-emitting the QR data) if the wallet has not yet responded; completes the flow if the credential is verified; fails the flow if verification fails or the request expires.

  3. On completion: sets AuthenticatedUser with the verified holder's subject and all selectively disclosed credential claims. If an existing local account matches the disclosed attributes, AuthenticatedUser.UserID is populated automatically. When allowAuthenticationWithoutLocalUser is true, the userEligibleForProvisioning runtime flag is set so a downstream Provisioning executor can create the account.

Example:

{
"id": "eudi_verify",
"type": "TASK_EXECUTION",
"properties": {
"presentation_definition_id": "eudi-pid"
},
"executor": {
"name": "OpenID4VPVerifyExecutor"
},
"onSuccess": "auth_assert",
"onFailure": "end",
"onIncomplete": "eudi_wait_view"
}

Failure conditions:

  • OpenID4VP service not configured in the server
  • Presentation definition ID not registered
  • Request expired before the wallet responded (state TTL elapsed)
  • Wallet presented an untrusted credential (issuer not in the trust store)
  • Credential signature or key binding verification failed
  • Mandatory claim missing from the presentation
  • Wallet disclosed a claim that was not in the requested claims list

Token Issuance

Auth Assertion Generator

Generates the final authentication assertion, a signed JWT, that ThunderID returns to the client on successful flow completion. This must be the last executor before every END node in an authentication flow.

When to use: Every authentication flow, immediately before the END node. This is the final step that returns the signed JWT to the client. Do not place in registration-only or recovery-only flows unless those flows also complete with a sign-in.

Prerequisites:

  • An authenticated user must be present in the flow context: at least one authentication executor (Identifier + Password, OAuth, OIDC, SMS OTP, Passkey, Magic Link, etc.) must have set isAuthenticated: true. The executor hard-fails if no authenticated user is present.
  • Application assertion settings must be configured in Console (assertion lifetime, user attributes to include, etc.).

How the assertion is built:

  • Subject (sub): Set to the authenticated user's ID.
  • Audience (aud): Set to the application (entity) ID.
  • Assurance: Extracted from the flow execution history. Lists which authenticators ran, in which step, and at what time.
  • Authorized permissions: Included if the Authorization executor ran.
  • User attributes: Resolved from the following sources in priority order: consented attributes (if User Consent ran) → essential/optional attribute lists from application configuration → application assertion configuration. Computed attributes (groups, roles, userType, ouId, ouName, ouHandle) are derived at assertion time and included only if configured.
  • Token lifetime: Uses the application's assertion validity period; falls back to the system JWT default.

Input Configuration: None. This executor operates entirely on runtime context; it has no configurable node input defaults.

Outputs:

  • Signed JWT assertion returned to the client

Example:

{
"id": "auth_assert",
"type": "TASK_EXECUTION",
"executor": {
"name": "AuthAssertExecutor"
},
"onSuccess": "end"
}

Failure conditions:

  • No authenticated user present (no authentication executor ran)
  • JWT signing failure (key configuration error)
  • User attribute or group resolution error

Utility

Validate Attribute Uniqueness

Checks that unique schema attributes supplied in user inputs are not already held by an existing user. If a conflict is detected, the user is prompted to change the value.

When to use: After a form View that collects unique attributes (email, username, phone, etc.), before any creation executor. Early detection prevents conflicts at provisioning time.

Prerequisites:

  • userType (required): must be present in runtime data. Set by User Type Resolver. The executor reads the unique attribute schema from this user type.

View pairing: Pair with a Blank View that collects the unique attributes (email, username, phone, etc.).

How it works:

  1. Reads the user type resolved by User Type Resolver
  2. Fetches the unique attributes defined in that user type's schema
  3. Checks each unique attribute value provided in user inputs
  4. Queries the user store to see if any other user already holds that value
  5. Prompts the user to correct the conflicting attribute if a conflict is found
  6. Returns COMPLETE if all values are free to use

Input Configuration: None. This executor validates the unique-attribute values that are already present in user inputs; it has no registered node input defaults.

Example:

{
"id": "check_uniqueness",
"type": "TASK_EXECUTION",
"executor": {
"name": "AttributeUniquenessValidator"
},
"onSuccess": "provisioning",
"onIncomplete": "attr_form"
}

Failure conditions:

  • userType not available (User Type Resolver did not run)
  • User type schema not found
  • User store lookup error

Executor Validation Rules

When you create or update a flow definition, ThunderID validates every TASK_EXECUTION node against the executor it references. These checks run before the flow is persisted and prevent misconfigurations from reaching runtime. A validation failure returns an error describing the node and the rule that was violated.

The following rules are enforced for each executor node:

Mode validation: If the node specifies an executor mode, the server checks that the mode is one of the values the executor supports. For example, OTPExecutor supports "generate" and "verify"; configuring "mode": "send" on an OTPExecutor node fails validation. When the node omits the mode field or the executor does not restrict modes, no check is performed.

Flow type validation: Some executors are restricted to specific flow types. For example, an executor that is only valid in registration flows will cause a validation error if referenced in an authentication flow definition. When the executor does not restrict flow types, the node is accepted in any flow type.

Required properties validation: Certain executors require specific keys in the node's properties map. For example, federated authentication executors require an idpId property. If a required property is missing or empty, the server rejects the flow definition with an error identifying the missing property.

tip

Each executor's supported modes, flow type restrictions, and required properties are listed in the Executor Details and Configuration section above. Refer to the individual executor entries for the exact values.

Input Validation Rules

Input components can declare a validation array that the server enforces when the user submits the form. The server evaluates these rules after the required-input presence check and before the View advances to the next node.

The validation array attaches to two places in the flow definition:

  • meta.components[].validation - returned in the verbose flow execution response embedded inside meta so that clients can optionally pre-validate values before submission.
  • prompts[].inputs[].validation - the authoritative copy that the server evaluates on each submission and that API-only clients receive in the prompt response.

Each rule is a single-constraint object with a discriminated type field:

typevalue TypeBehavior
regexstringThe submitted value must match the regex pattern. The server uses a regex engine that guarantees linear-time matching.
minLengthintegerThe submitted value must contain at least value Unicode code points. The server counts code points rather than bytes so multibyte characters count as one.
maxLengthintegerThe submitted value must contain at most value Unicode code points.

Each rule may set a message field that the server returns as is in data.fieldErrors when the rule fails. The message field accepts a literal string or an i18n key such as {{i18n(validation:email.invalid)}}. The server does not resolve i18n keys; the client renders them. When message is absent, the server returns a default key for the rule type: validation.pattern.invalid, validation.minLength.invalid, or validation.maxLength.invalid.

Example: Phone Input with validation rules
{
"id": "input_phone",
"type": "PHONE_INPUT",
"category": "FIELD",
"required": true,
"validation": [
{
"type": "regex",
"value": "^[0-9]{10}$",
"message": "{{i18n(validation:phone.pattern.invalid)}}"
},
{
"type": "maxLength",
"value": 10,
"message": "{{i18n(validation:phone.maxLength.invalid)}}"
}
],
"resourceType": "ELEMENT"
}

When one or more rules fail on a submission, the server returns flowStatus: INCOMPLETE with a data.fieldErrors array. Each failing rule produces a separate entry that pairs the input identifier with the rule's message. The server clears the failing inputs from the submission and re-presents the same step. The response returns the full inputs and actions arrays so the client can re-render the original form alongside the errors. The user then resubmits corrected values.

Example: Validation failure response (abbreviated)
{
"flowStatus": "INCOMPLETE",
"type": "VIEW",
"data": {
"inputs": [
{ "ref": "input_username", "identifier": "username", "type": "TEXT_INPUT", "required": true, "validation": [ /* ... */ ] },
{ "ref": "input_password", "identifier": "password", "type": "PASSWORD_INPUT", "required": true, "validation": [ /* ... */ ] }
],
"actions": [ { "ref": "action_submit", "nextNode": "node_bs12" } ],
"fieldErrors": [
{ "identifier": "username", "message": "{{i18n(validation:email.invalid)}}" },
{ "identifier": "password", "message": "{{i18n(validation:password.minLength.invalid)}}" }
]
}
}

ACR Values in Flows

Use the acr_values parameter in an authorization request to specify which authentication methods are acceptable for the session.

GET /oauth2/authorize
?response_type=code
&client_id=<client-id>
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&scope=openid%20profile
&acr_values=urn%3Athunder%3Aacr%3Agold
&state=abc123
&nonce=xyz789

ACR filtering only takes effect when the flow contains a LOGIN_OPTIONS PROMPT node with authMethodMapping configured. Without it, acr_values have no effect on the flow.

Using acr_values with the LOGIN_OPTIONS node

Map each ACR string to an action ref in the authMethodMapping property. When the authorization request includes acr_values, ThunderID uses those values as the resolved keys to filter the node's actions:

"authMethodMapping": {
"urn:thunder:acr:silver": "action-password",
"urn:thunder:acr:gold": "action-otp"
}

When the user selects a gated action, the matched ACR string is included as the acr claim in the issued ID token.

Default Behavior

  • acr_values sent: Only actions whose mapped key matches a requested ACR value are shown. Unmapped actions are always shown. If there is no match, all actions are shown as a fallback.
  • acr_values not sent: All actions are shown.

Interceptors

Interceptors are cross-cutting operations that execute at defined lifecycle points around each flow request. They are declared at the flow level, not on individual nodes, and run automatically based on their mode and scope.

Declaration

Add an interceptors array to the flow definition body when creating or updating a flow:

{
"handle": "my-login-flow",
"name": "My Login Flow",
"flowType": "AUTHENTICATION",
"interceptors": [
{
"name": "CaptchaInterceptor",
"mode": "PRE_REQUEST",
"scope": "SELECTED",
"applyTo": ["login-screen"],
"properties": {
"siteKey": "abc123"
}
}
],
"nodes": [...]
}

The interceptors array is included in create, update, get, and version payloads. Omit it or pass an empty array if you do not need configurable interceptors.

Fields

FieldRequiredDescription
nameYesName of the interceptor as registered in the interceptor registry.
modeYesLifecycle point at which the interceptor runs. See Modes.
scopeNoDetermines which nodes the interceptor applies to for per-node modes. Defaults to ALL.
applyToNoList of node IDs the interceptor applies to. Only used when scope is SELECTED.
propertiesNoInterceptor-specific configuration as a key-value map.

Interceptor Modes

ModeWhen it runs
PRE_REQUESTBefore the flow processes the incoming request, once per request cycle.
PRE_NODEBefore a specific node executes.
POST_NODEAfter a specific node executes.
POST_REQUESTAfter the flow has produced its response, once per request cycle.

Scope

scope controls which nodes a PRE_NODE or POST_NODE interceptor applies to. It has no effect on PRE_REQUEST and POST_REQUEST interceptors.

ScopeBehavior
ALLRuns for every node, unless the node's skipInterceptors property lists this interceptor by name.
SELECTEDRuns only for the node IDs listed in applyTo.

To exclude an ALL-scoped interceptor from a specific node, add a skipInterceptors entry in that node's properties:

{
"id": "skip-node",
"type": "TASK_EXECUTION",
"properties": {
"skipInterceptors": ["CaptchaInterceptor"]
},
...
}

Built-in Interceptors

Some interceptors are always active and cannot be declared in the flow definition. They run at system-level priority before any configurable interceptors.

Challenge Token: Active on every flow. On POST_REQUEST, generates a per-step challenge token and includes it in the flow step response under the challengeToken field. On PRE_REQUEST, validates the token submitted with the incoming request to prevent replay and out-of-order submissions. Validation is skipped on the first request of a new flow instance (no prior token issued yet) and when the engine permits a segment restart. The ThunderID JavaScript SDK reads and forwards the challenge token transparently: no client-side handling is required.

Try Out

  • Build a Flow: Step-by-step guide to creating a flow in the ThunderID Console.

Explore with AI

ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© Copyright Linux Foundation Europe.For web site terms of use, trademark policy and other project policies please see https://linuxfoundation.eu/en/policies.