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 anextNodereference, 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
| Field | Required | Description |
|---|---|---|
identifier | Yes | Unique key used to store the submitted value. |
ref | No | UI binding reference that links to a meta component by id. |
type | No | Field type: text (default), password, or select. |
required | No | When true, the field must be filled before the flow can advance. |
Action
| Field | Required | Description |
|---|---|---|
ref | Yes | Unique identifier for the action. |
nextNode | Yes | ID of the node to advance to when this action is selected. |
type | No | SUBMIT 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
completestatus. - Terminal error: Ends the flow with a
failurestatus. - 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.
| Property | Required | Description |
|---|---|---|
next | Yes | ID of the next node to advance to automatically. Mutually exclusive with prompts. |
message | No | A 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
| Type | Role |
|---|---|
BLOCK | Container that groups inputs and actions. Input and action components must live inside a BLOCK. |
ACTION | A button or link. Its id must match the ref of a prompt action. |
DYNAMIC_INPUT_PLACEHOLDER | Marks where dynamically derived inputs (those arriving via ForwardedData, not declared in the node's prompts) should be inserted. |
| Any other type | Treated 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
| Property | Required | Description |
|---|---|---|
authMethodMapping | No | Object 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 keys | Actions shown |
|---|---|
password | action-password, action-passkey |
otp | action-otp, action-passkey |
password, otp | action-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
| Field | Required | Description |
|---|---|---|
executor | Yes | Name of the executor to run. |
mode | No | Operational mode passed to the executor. Use when an executor supports multiple steps or variants (e.g. send, verify). |
inputs | No | Override the executor's default inputs for this node. When omitted, the executor's own default inputs are used. |
onSuccess | No | ID of the node to advance to when the executor completes successfully. |
onFailure | No | ID 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. |
onIncomplete | No | ID 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
| Field | Required | Description |
|---|---|---|
flow.ref | Yes | ID of the flow to invoke. |
onSuccess | Yes | ID of the node to advance to when the callee flow completes successfully. |
onFailure | No | ID 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.
| Widget | Description |
|---|---|
| Username + Password | A login form wired to the Identifier + Password executor. |
| Continue with Google | Social login using Google. |
| Continue with GitHub | Social login using GitHub. |
| Continue with SMS OTP | An OTP input view wired to the Verify OTP executor. |
| Sign In with Passkey | A passkey prompt wired to the passkey verification executors. |
| Provisioning | A Provisioning executor wired to a dynamic input view that prompts for missing schema attributes. |
| Self Sign Up Link | A rich text link wired to a CALL node that invokes a registration flow. |
| Sign In Link | A rich text link wired to a CALL node that invokes an authentication flow. |
| Recovery Link | A 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.
| Step | Description |
|---|---|
| Blank View | An empty View node. Add Components from the left panel to design the screen. |
| SMS OTP View | A pre-designed OTP input screen for SMS verification. |
| Passkey View | A pre-designed screen for passkey authentication. |
| Username + Password | A pre-designed login form with username and password fields. |
| Consent View | A 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.
| Component | Description |
|---|---|
| Form | A container that groups input fields and a submit button. |
| Text Input | A single-line text field. |
| Password Input | A password field with masked input. |
| Email Input | An email address field. |
| Phone Input | A phone number field. |
| Number Input | A numeric field. |
| Date Input | A date picker field. |
| OTP Input | A one-time password input field. |
| Checkbox | A checkbox for consent or toggling an option. |
| Button | A clickable button that triggers a transition to the next node. |
| Resend | A control that lets the user request a new OTP or code. |
| Text | A read-only label or paragraph. |
| Rich Text | A 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. |
| Divider | A horizontal rule that visually separates sections of a screen. |
| Image | An image element such as a logo or illustration. |
| Stack | A layout container that groups other components. |
| Icon | A small icon element. |
| Timer | A countdown timer, used on consent screen. |
| Dynamic Input Placeholder | Marks 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.
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.
| Executor | Description | Configuration Prerequisites |
|---|---|---|
| Identifier + Password | Verifies a username (or email) and password against the user store. | - |
| OAuth | Authenticates via generic OAuth 2.0 provider. | OAuth identity provider configured |
| OIDC Auth | Authenticates via generic OIDC provider. | OIDC identity provider configured |
| Authenticates the user via Google sign-in. | Google identity provider configured | |
| GitHub | Authenticates the user via GitHub sign-in. | GitHub identity provider configured |
| Request Passkey | Initiates a passkey authentication challenge. | Relying party configured |
| Verify Passkey | Verifies the user's passkey response. | Request Passkey must have run |
| Start Passkey Registration | Begins the passkey registration ceremony. | Relying party configured |
| Finish Passkey Registration | Completes the passkey registration ceremony. | Start Passkey Registration must have run |
| Generate OTP | Generates a time-limited OTP and forwards it for delivery. | - |
| Verify OTP | Verifies the OTP code the user entered. | Generate OTP must have run |
| Generate Magic Link | Generates a magic link authentication token and sends it to the user. | - |
| Verify Magic Link | Verifies the magic link token submitted by the user. | Generate Magic Link must have run |
| Identify User | Looks up a user by identifier in the user store. | - |
| Resolve User | Handles disambiguation when multiple users match an identifier. | - |
| Resolve Federated User | Resolves ambiguous federated user after social login. | OAuth/OIDC executor must have run; Identify User must have run |
| User Type Resolver | Resolves the user type based on configured rules. | User type configured on the application |
| Provisioning | Creates or updates the user record in the store. | - |
| Attribute Collector | Collects additional user attributes defined in the user type. | - |
| Validate Attribute Uniqueness | Checks that unique attributes are not already registered. | User Type Resolver must have run |
| Set Credentials | Sets credentials (e.g., password) for an existing user. | - |
| OpenID4VP Verify | Initiates 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 |
| Authorization | Evaluates authorization policies for the current user. | - |
| User Consent | Records explicit user consent for defined scopes or terms. | - |
| Validate Permission | Checks that the request has required scope/permissions. | - |
| OU Creation | Creates an organizational unit for the user. | - |
| Resolve OU | Resolves or selects an organizational unit based on strategy. | Varies by strategy |
| Generate Invite | Generates an invite link for user registration. | - |
| Verify Invite | Verifies an invite token before completing registration. | Generate Invite must have run |
| Send Email | Sends email using configured templates. | Email service configured |
| Send SMS | Sends SMS using configured templates and sender. | SMS sender configured |
| Auth Assertion Generator | Generates the final authentication assertion on successful flow completion. | User authenticated; assertion settings configured |
| HTTP Request | Makes HTTP requests to external endpoints. | - |
- See View and Executor Pairings for more details on combining Views with executors.
- See Executor Details and Configuration for more details on prerequisites and inputs.
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.
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) | Prerequisites | Notes |
|---|---|---|---|
| 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 OTP | User's mobile number must be on record | Place 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 Consent | User must be authenticated | The executor reads the consent decisions submitted by the View. The user's ID must already be in the flow context. |
| Continue with Google (Widget) | - | 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 Collector | User must be authenticated | Use 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 Verify | OpenID4VP service configured | The 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.
| Executor | Placement | Prerequisites |
|---|---|---|
| Auth Assertion Generator | Last Executor before the END node in every authentication flow | User must be authenticated by prior Executors |
| Authorization | After authentication Executors, before Auth Assertion Generator | User must be authenticated |
| Generate OTP | Before the SMS OTP View | - |
| Send SMS | After Generate OTP, before the SMS OTP View | User's mobile number must be on record |
| Send Email | After the flow collects the recipient email | Recipient email input; inviteLink for invite and self-registration templates |
| Provisioning | After identity collection in registration flows | - |
| Identify User | Early in the flow, after the user submits an identifier | - |
| User Type Resolver | After Identify User | - |
| OU Creation | After Provisioning in registration flows | OU 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 ifuserIDis 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
- In registration flows: the Provisioning executor, which creates the user and sets
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:
userIDnot 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:
| Property | UI Label | Required | Description |
|---|---|---|---|
idpId | Connection | Yes | The identity provider ID configured in ThunderID |
allowAuthenticationWithoutLocalUser | Allow Authentication Without Local User | No | Allow authentication when no local user exists (creates federated link) |
allowRegistrationWithExistingUser | Allow Registration With Existing User | No | Allow registration if the account already exists |
allowCrossOUProvisioning | Allow Cross-OU Provisioning | No | Allow 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:
| Property | UI Label | Required | Description |
|---|---|---|---|
idpId | Connection | Yes | The identity provider ID configured in ThunderID |
allowAuthenticationWithoutLocalUser | Allow Authentication Without Local User | No | Allow authentication when no local user exists (creates federated link) |
allowRegistrationWithExistingUser | Allow Registration With Existing User | No | Allow registration if the account already exists |
allowCrossOUProvisioning | Allow Cross-OU Provisioning | No | Allow 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
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:
| Property | UI Label | Required | Description |
|---|---|---|---|
idpId | Connection | Yes | The identity provider ID configured in ThunderID |
allowAuthenticationWithoutLocalUser | Allow Authentication Without Local User | No | Allow authentication when no local user exists (creates federated link) |
allowRegistrationWithExistingUser | Allow Registration With Existing User | No | Allow registration if the account already exists |
allowCrossOUProvisioning | Allow Cross-OU Provisioning | No | Allow 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:
| Property | UI Label | Required | Description |
|---|---|---|---|
idpId | Connection | Yes | The identity provider ID configured in ThunderID |
allowAuthenticationWithoutLocalUser | Allow Authentication Without Local User | No | Allow authentication when no local user exists (creates federated link) |
allowRegistrationWithExistingUser | Allow Registration With Existing User | No | Allow registration if the account already exists |
allowCrossOUProvisioning | Allow Cross-OU Provisioning | No | Allow 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.relyingPartyIdnode 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:
| Property | UI Label | Required | Description |
|---|---|---|---|
relyingPartyId | Relying Party ID | Yes | The relying party domain (e.g. app.example.com) |
relyingPartyName | Relying Party Name | No | Display name of the relying party (e.g. My App) |
authenticatorSelection | - | No | Authenticator selection criteria object with fields: authenticatorAttachment, requireResidentKey, residentKey, userVerification |
attestation | - | No | Attestation 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:
relyingPartyIdnode 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:credentialIdclientDataJSON(required): the client data from the authenticator response. Default:clientDataJSONauthenticatorData(required): the authenticator data from the response. Default:authenticatorDatasignature(required): the cryptographic signature from the response. Default:signatureuserHandle(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).relyingPartyIdnode 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:
| Property | UI Label | Required | Description |
|---|---|---|---|
relyingPartyId | Relying Party ID | Yes | The relying party domain (e.g. app.example.com) |
relyingPartyName | Relying Party Name | No | Display name of the relying party (e.g. My App) |
authenticatorSelection | - | No | Authenticator selection criteria object with fields: authenticatorAttachment, requireResidentKey, residentKey, userVerification |
attestation | - | No | Attestation 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:
userIDnot availablerelyingPartyIdnode 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:credentialIdclientDataJSON(required): the client data from the registration response. Default:clientDataJSONattestationObject(required): the attestation object from the registration response. Default:attestationObjectcredentialName(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:
- 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.
- 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. - 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:
| Property | UI Label | Required | Description |
|---|---|---|---|
maxAttempts | - | No | Maximum 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).
Magic Link
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:
| Property | UI Label | Required | Description |
|---|---|---|---|
tokenExpiry | - | No | Token validity period in seconds. Defaults to the magic link service default if absent. |
magicLinkURL | - | No | URL 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.defaultOUIDorouId(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:
| Property | UI Label | Required | Default | Description |
|---|---|---|---|---|
allowCrossOUProvisioning | Allow Cross-OU Provisioning | No | false | Allow creating the user in a different OU from the default when the user already exists in another OU |
assignGroup | Assign Group | No | - | Group ID to automatically assign the new user to |
assignRole | Assign Role | No | - | Role ID to automatically assign the new user to |
includeOptional | - | No | false | Whether to prompt for optional (non-credential) schema attributes |
includeOptionalCredentials | Include Optional Credentials | No | false | Whether to prompt for optional credential attributes |
maxPerPrompt | - | No | 0 (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:
userTypeor 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:
- Checks the user's authenticated attributes.
- Falls back to the user's stored profile in the user store.
- 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
userIDmissing 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:
- Checks for stored candidate users from a previous Identify User execution. If none exist, performs a fresh search using the provided attributes.
- Builds a filter from the user's selected attributes (from View inputs)
- Filters candidates against the selected attributes
- If multiple candidates remain, extracts new disambiguation options and prompts again
- 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:
- Reads the federated subject set by the OAuth/OIDC executor
- Reads candidate users from the prior Identify User execution
- Filters candidates using user-provided selection criteria (from View)
- 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 type | Behavior |
|---|---|
| Registration | Resolves 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. |
| Authentication | Validates that the application has at least one allowed user type configured; completes without further action. |
| User Onboarding | Lists all user types in the system, optionally filtered by node properties or by a prior resolved OU. |
Executor properties:
| Property | UI Label | Required | Description |
|---|---|---|---|
allowedUserTypes | Allowed User Types | No | Array 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:
| Property | UI Label | Required | Default | Behavior |
|---|---|---|---|---|
resolveFrom | Resolve From | Yes | - | 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
- User Type Resolver must have run first: resolves
- Setup:
- Place User Type Resolver before this executor in registration flows
- Add a Blank View immediately after this executor with an OU selection component
- 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:
- No need to run User Type Resolver first (unlike
promptstrategy) - Add a Blank View immediately after this executor with an OU selection component
- The executor will display the complete OU tree starting from root
- No need to run User Type Resolver first (unlike
- Failure conditions: OU service error
Input Configuration:
ouId(required): the user's selected organizational unit. Used forpromptandpromptAllstrategies. 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
callerstrategy) - Selected OU does not exist
- OU service error
Authorization and Consent
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:
- Reads
requested_permissionsfrom user inputs or the OIDC scope parameter (space-separated permission strings). - Resolves the user's group memberships from their profile or by fetching transitive groups.
- Calls the authorization service with the user ID, group IDs, and requested permissions.
- 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:
- Reads
required_essential_attributesandrequired_optional_attributesfrom the application's assertion configuration. - Calls the consent enforcer to check existing consent records.
- If all consents are active, completes immediately: no prompt is needed.
- If any consents are missing, forwards the prompt data to the Consent View and awaits the user's decisions.
- After decisions are received, records them and makes
consentIdandconsented_attributesavailable for Auth Assertion Generator.
Executor properties:
| Property | UI Label | Required | Description |
|---|---|---|---|
timeout | Consent Timeout (seconds) | No | Seconds 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:
userIDnot available- User denied one or more essential (required) attributes: hard failure
- Consent prompt timed out (if
timeoutis 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:
- Extracts required scopes from the
requiredScopesnode property (if configured) - Reads permissions from the incoming request's security context
- Checks if the request has at least one of the required permissions
- Returns
COMPLETEif authorized,FAILUREif not
Executor properties:
| Property | UI Label | Required | Default | Description |
|---|---|---|---|---|
requiredScopes | Required Scopes | No | System root | Array 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
FAILUREwith "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
parentOuIdis 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:
| Property | UI Label | Required | Default | Description |
|---|---|---|---|---|
parentOuId | Parent OU ID | No | - | 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
parentOuIddoes 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 viauserID). 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:
| Property | UI Label | Required | Default | Description |
|---|---|---|---|---|
emailTemplate | Email Template | No | user_invite | The 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.
senderIdnode property must be set to the configured sender ID
Recipient resolution order:
- A phone-type input from the node's configured inputs (from
ForwardedData) mobile_numberfrom user inputs- 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:
| Property | UI Label | Required | Description |
|---|---|---|---|
senderId | Notification Sender | Yes | The notification sender ID configured in ThunderID |
smsTemplate | SMS Template | Yes | The 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
senderIdnot 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:
- Generates a cryptographically secure invite token
- Stores the token for Verify Invite to consume
- Generates a full invite link (with token embedded)
- 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:
- Reads the user-provided invite token from user inputs
- Compares it against the stored token from Generate Invite
- Returns
COMPLETEif tokens match,FAILUREif 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:
- Parses HTTP configuration from node properties (URL, method, headers, body, timeout)
- Resolves
{{ctx(fieldName)}}placeholders in the URL, headers, and body using runtime data and user inputs from prior executors - Sends the HTTP request with optional retry logic based on
errorHandlingconfig - Maps response fields for downstream use based on
responseMappingconfiguration - Continues the flow (or fails based on error handling configuration)
Executor properties:
| Property | UI Label | Required | Default | Description |
|---|---|---|---|---|
url | URL | Yes | - | The endpoint URL (supports placeholders like {{ctx(userId)}}) |
method | Method | No | GET | HTTP method: GET, POST, PUT, DELETE, or PATCH |
headers | Headers | No | - | HTTP headers as a map of strings |
body | Request Body | No | - | Request body (JSON object, only for POST/PUT/PATCH) |
timeout | Timeout (seconds) | No | 10s | Request timeout in seconds (max 20s) |
responseMapping | Response Mapping | No | - | Map response JSON fields for downstream executor use |
errorHandling.failOnError | Fail on Error | No | false | Whether HTTP error status codes cause flow failure |
errorHandling.retryCount | Retry Count | No | 0 | Number of retries on failure (max 5) |
errorHandling.retryDelay | Retry Delay (ms) | No | 0 | Delay 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:
| Placeholder | Source | Description |
|---|---|---|
{{ctx(userId)}} | Authenticated user or runtime data | The authenticated user's ID. Available after any authentication or provisioning executor has run. |
{{ctx(ouId)}} | Authenticated user or runtime data | The organizational unit ID of the current user. Available after OU resolution or authentication. |
{{ctx(ouHandle)}} | OU service lookup | The handle of the resolved OU. Fetched automatically when referenced. |
{{ctx(ouName)}} | OU service lookup | The display name of the resolved OU. Fetched automatically when referenced. |
{{ctx(ouDescription)}} | OU service lookup | The description of the resolved OU. Fetched automatically when referenced. |
{{ctx(clientId)}} | OAuth flow context | The 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:
| Property | UI Label | Required | Default | Description |
|---|---|---|---|---|
presentation_definition_id | Presentation Definition | No | eudi-pid | The identifier of the presentation definition to request. Must match a definition registered in the server configuration. |
allowAuthenticationWithoutLocalUser | Allow Auth Without Local User | No | false | When 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:
-
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: theopenid4vp://authorization URI for the wallet (QR code target or deep link)openid4vpClientId: the verifier'sclient_idopenid4vpRequestUri: the signed request object URI ReturnsINCOMPLETEso the flow pauses at the View node.
-
On subsequent executions (state present in runtime data): polls the request state. Returns
INCOMPLETEagain (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. -
On completion: sets
AuthenticatedUserwith the verified holder'ssubjectand all selectively disclosed credential claims. If an existing local account matches the disclosed attributes,AuthenticatedUser.UserIDis populated automatically. WhenallowAuthenticationWithoutLocalUseristrue, theuserEligibleForProvisioningruntime 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:
- Reads the user type resolved by User Type Resolver
- Fetches the unique attributes defined in that user type's schema
- Checks each unique attribute value provided in user inputs
- Queries the user store to see if any other user already holds that value
- Prompts the user to correct the conflicting attribute if a conflict is found
- Returns
COMPLETEif 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:
userTypenot 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.
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 insidemetaso 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:
type | value Type | Behavior |
|---|---|---|
regex | string | The submitted value must match the regex pattern. The server uses a regex engine that guarantees linear-time matching. |
minLength | integer | The submitted value must contain at least value Unicode code points. The server counts code points rather than bytes so multibyte characters count as one. |
maxLength | integer | The 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.
{
"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.
{
"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_valuessent: 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_valuesnot 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
| Field | Required | Description |
|---|---|---|
name | Yes | Name of the interceptor as registered in the interceptor registry. |
mode | Yes | Lifecycle point at which the interceptor runs. See Modes. |
scope | No | Determines which nodes the interceptor applies to for per-node modes. Defaults to ALL. |
applyTo | No | List of node IDs the interceptor applies to. Only used when scope is SELECTED. |
properties | No | Interceptor-specific configuration as a key-value map. |
Interceptor Modes
| Mode | When it runs |
|---|---|
PRE_REQUEST | Before the flow processes the incoming request, once per request cycle. |
PRE_NODE | Before a specific node executes. |
POST_NODE | After a specific node executes. |
POST_REQUEST | After 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.
| Scope | Behavior |
|---|---|
ALL | Runs for every node, unless the node's skipInterceptors property lists this interceptor by name. |
SELECTED | Runs 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.