Build a Multi-Factor Sign-In Flow
By the end of this guide, you will have a sign-in flow that offers two first-factor options: username + password, or Google social login. Users who choose username + password are prompted for a second factor via SMS OTP. Users who sign in with Google skip the second factor and go straight to login success.
Flow Structure
The two paths diverge at the login screen and both end at login success, the password path with an extra OTP step.
Prerequisites
- ThunderID is running. See Get Started.
- You have created an application. See Register an Application.
- A Google identity provider is configured in ThunderID. See Add Google.
- An SMS notification sender is configured. See SMS Sender.
- Each user's profile in ThunderID includes a mobile number. The SMS OTP executor reads the number from the user store.
Create the Flow
- Sign in to the ThunderID Console at https://localhost:8090/console.
- Go to Flows and click + Create New Flow.
- Select Sign-in as the flow type and click Continue.
- Select + Start from scratch and enter a flow name, for example
MFA Sign In. - Click Create.
The canvas opens with a START node, a blank View node, a Auth Assertion Generator, and an END node.
Add the Username + Password Option
- Delete Blank View.
- From Steps, click + next to Username + Password. This replaces the default blank View with the login form View node.
- From Executors, click + next to Identifier + Password.
- On the Username + Password View node, click the Sign In button component and set its Type property to
SUBMIT. - Connect START → Username + Password View → Identifier + Password Executor.
Add the Google Social Login Option
From the left panel under Widgets, click + next to Continue with Google.
This adds three elements to the canvas:
- A Continue with Google button action on the Username + Password View node
- A Google Executor node
- An Auth Assertion Generator Executor node (pre-wired to END)
The login screen now has two actions: the Sign In button (routes to the Identifier + Password executor) and the Continue with Google button (routes to the Google executor).
Configure the Google Executor
The Google executor manages the OAuth redirect to Google and the callback. It requires a configured Google identity provider. See Add Google if you haven't set one up yet.
- Click configure icon on the Google Executor node.
- Set the
Connectionproperty to the connection name of your configured Google identity provider. - Connect the Google Executor's incomplete output to the Username + Password View node input.
Add SMS OTP as the Second Factor
After a successful username + password check, the flow sends a one-time password (OTP) to the user's registered mobile number and asks them to verify it. This second factor is built from four nodes:
| Node | Role |
|---|---|
| Generate OTP Executor | Generates the OTP and stores it for delivery. Runs in the background, no user interaction. |
| Send SMS Executor | Delivers the generated OTP to the user's mobile number via SMS. Runs in the background, no user interaction. |
| SMS OTP View | Presents the OTP entry screen to the user. |
| Verify OTP Executor | Validates the code the user entered. |
Add the Generate OTP and Send SMS Executors
- From the left panel under Executors, click + next to Generate OTP.
- Connect the Identifier + Password Executor's success output to the Generate OTP Executor's input.
- From the left panel under Executors, click + next to Send SMS.
- Click the configure icon on the Send SMS Executor node.
- Set the Notification Sender property to the sender name of your configured SMS sender.
- Set the SMS Template property to the scenario name of the SMS template to use (e.g.,
OTPfor the built-in OTP template). - Connect the Generate OTP Executor's success output to the Send SMS Executor's input.
Add the OTP View and Verify OTP Executor
- From Steps, click + next to SMS OTP View. Click the Verify button component and set its Type property to
SUBMIT. - From Executors, click + next to Verify OTP.
- Connect Send SMS success → SMS OTP View.
- Connect SMS OTP View action output → Verify OTP Executor.
Configure the Verify OTP Executor
- Connect the Verify OTP Executor's incomplete output back to the SMS OTP View node so users can retry an incorrect code.
- Connect the Verify OTP Executor's success output to the Auth Assertion Generator Executor's input.
Verify the Wiring
Before saving, confirm these connections:
| From | Port | To |
|---|---|---|
| START | - | Username + Password View |
| Username + Password View (Sign In) | action | Identifier + Password Executor |
| Username + Password View (Continue with Google) | action | Google Executor |
| Identifier + Password | success | Generate OTP Executor |
| Identifier + Password | incomplete | Username + Password View |
| success | Auth Assertion Generator Executor | |
| Generate OTP | success | Send SMS Executor |
| Send SMS | success | SMS OTP View |
| SMS OTP View (Verify) | action | Verify OTP Executor |
| Verify OTP | success | Auth Assertion Generator Executor |
| Verify OTP | incomplete | SMS OTP View |
| Verify OTP | failure | (unconnected, flow terminates; wire to error View in production) |
| Auth Assertion Generator | success | END |
Every executor has a failure output (red dot). An unconnected failure output causes the flow to terminate with an unhandled error and no feedback to the user. This guide leaves failure outputs unconnected for simplicity. See Always Wire Failure Outputs.
Save the Flow
Click Save in the top-right corner. ThunderID saves the flow and assigns it a version.
Assign the Flow to Your Application
A flow has no effect until it is assigned to an application.
- In the Console, go to Applications and open the application you created.
- Open the Flows tab.
- Under Authentication Flow, select the flow you just saved from the dropdown.
- Click Save.
ThunderID now runs this flow for every sign-in to your application. Password-based users are challenged with an SMS OTP; Google users complete sign-in in a single step.
Go Further
Build and configure a registration flow
A registration flow controls how new users sign up for your application. To create one:
- In the Console, go to Flows → + Create New Flow.
- Select Self Sign-up as the flow type and click Continue.
- Choose a template, for example, Basic ("Username, password, and profile info registration"), or Blank to start from scratch.
- Give the flow a name and click Create.
- The canvas opens with the selected template pre-wired. Customize the steps as needed, then click Save.
To assign the registration flow to your application:
- Go to Applications and open the application.
- Open the Flows tab and find the Registration Flow section.
- Enable the toggle to activate self sign-up for the application.
- Select the flow you created from the dropdown.
- Click Save.
ThunderID runs the selected flow when a user initiates sign-up for your application.
Build and configure a recovery flow
A recovery flow lets users regain access to their account, for example, via email-based password reset. To create one:
- In the Console, go to Flows → + Create New Flow.
- Select Password Recovery as the flow type and click Continue.
- Choose a template, for example, Email Link ("Email-based password recovery with email link") or SMS OTP, or Blank to start from scratch.
- Give the flow a name and click Create.
- Review and customize the canvas, then click Save.
To assign the recovery flow to your application:
- Go to Applications and open the application.
- Open the Flows tab and find the Recovery Flow section.
- Enable the toggle to activate recovery for the application.
- Select the flow you created from the dropdown.
- Click Save.
ThunderID runs the selected flow when a user requests password or account recovery.
Build and configure a user onboarding flow
A user onboarding flow controls how administrators create and onboard new users. Unlike authentication, registration, and recovery flows, which are assigned per application, the user onboarding flow is configured system-wide. It runs whenever an admin creates a user through the ThunderID Console, regardless of which application they are working in. The same flow also governs admin-initiated user creation via the API.
The default built-in flow walks the admin through the following steps:
- Permission check: validates that the caller holds the required
systemscope. - User type selection: if multiple user types are configured, prompts the admin to select one.
- Organisation unit (OU) selection: prompts the admin to choose the target OU from the OU tree.
- Onboarding mode: the admin chooses between two paths:
- Create now: the admin fills in the user's profile details immediately and the user is provisioned on the spot.
- Invite: the admin enters the user's email address and selects a delivery method: send an invitation email, or copy a shareable link. The invited user follows the link to complete their own registration (the invite link is valid for 24 hours).
To customize the user onboarding flow, you can either edit the default flow directly in the Console's Flow Builder, or create a new one via the /flows API. See API Reference.
To set the flow as the active system-wide user onboarding flow, update deployment.yaml:
flow:
user_onboarding_flow_handle: <your-flow-handle>
Restart ThunderID for the change to take effect. All admin user creation actions, from the Console and the API, use the new flow.
Development Tips
Always Wire Failure Outputs
Every executor has a failure output (red dot). Leaving it unconnected does not mean the flow ignores failures: it means the flow terminates with an unhandled error and the user sees nothing. Connect every failure output to either:
- An error View that displays a descriptive message
- The END node to terminate the flow explicitly
For retry behavior, such as letting a user re-enter an incorrect OTP, use the incomplete output, not the failure output. Wire the incomplete output back to the input View.
Pass the user Object Between Executors
Most executors read from and write to the user context variable. The typical chain is:
Identify User → sets user
↓
Identifier + Password → confirms user is authenticated
↓
Authorization → enriches with granted scopes
↓
Auth Assertion Generator → issues token for user
Ensure executors earlier in the flow populate user before executors that depend on it.
Use Attribute Collector for Progressive Profiling
The Attribute Collector's incomplete path lets you collect profile data gradually across multiple sign-ins rather than demanding everything at registration. Wire a View after the incomplete path, collect the missing attributes, and the next execution of Attribute Collector will not re-prompt for fields already provided.
Reference
- Flow Concepts: Node types, left panel sections, and canvas connection rules.
- Advanced Configurations: Every executor, its required inputs, and its configurations.