Login Form
Provides the client with authentication options to choose from, and receives user input for the chosen option (such as username and password)
About client-facing steps
A journey is a sequence of steps that are executed when it's invoked by a client application (known as the "client"). Some steps require involvement from the client, such as to collect user input. Other steps, like validating a token, are executed in the backend by the Mosaic journey engine alone.
When invoked, the journey begins executing the steps while the client waits for further instructions. When a journey reaches a client-facing step, the journey asks the client for the required input and then waits for the client to respond. The client presents their own UI if user interaction is required, and returns input to the journey. The journey then proceeds in a similar manner until it’s completed.
Description
This client-facing step is used to obtain input from the client for user authentication. This includes which authentication method to use (if multiple methods are supported), and user input based on the chosen method. For example, a login form may collect a user's email to send an OTP, or collect a username and password before proceeding to password authentication. A single login form may also allow the user to choose whether to receive an OTP by email or SMS, and collect the user's email or phone number depending on which option they choose.
The step is configured with a set of authentication methods that will be presented to the client as options to choose from. When a method is toggled in the step, a branch is automatically created containing the related login method node. In this node, any login-related information (e.g., username, password, phone, etc.) is automatically populated.
Note
When the Login form step and its related login method nodes are inserted in the middle of an existing flow, the "tail" of the journey will be automatically linked to the first login method step. However, these nodes will not be automatically updated with information from the added nodes.
The schema represents the input expected back from the client if that method is chosen. The journey author can then add the appropriate authentication steps to the journey under the created branches.
Once the step is triggered, the journey asks the client for input and provides a step ID that uniquely identifies this particular step. The client knows which methods are enabled and what input is expected based on a JSON schema that was exchanged between the journey author and app developer during the integration process. The client is responsible for retrieving the input, including any supporting UI (e.g., HTML form).
Once the input is returned to the journey, it's stored in the configured output variable so it can be accessed by subsequent journey steps. If successful, the journey proceeds to the relevant branch. If the client cancels, the journey proceeds to a cancel branch if configured and is aborted otherwise.
As implied, this step requires coordination between the journey author and app developer, including:
- How the client should process the request when the journey returns a particular form ID
- Whether the client needs any context data like user-specific values to prefill a form. If so, this is configured in the step as application data as a free-form JSON and the schema is shared offline.
- What input the journey expects from the client for each branch. The schema is configured in the step.
- If the client cancels the step, whether it should fail the journey or proceed to another branch
- Whether the step supports additional branches that can be selected by the client (in addition to method branches)
Note
This step is the recommended way to collect user input for authentication scenarios.
For other scenarios (such as registration forms), use the Get Information From Client step.
Configuration
Field | Description |
---|---|
Step ID | Identifier of the step, used by the client to process the request for input. It can only contain letters, numbers, and underscores (no spaces), and it cannot begin with a number. Some examples of valid IDs are loginForm1 or login_form_1 |
Application Data | Data to send to the client, formatted as a free-form JSON. It may include expressions. For example, it can contain user-specific values to prefill a form, such as a masked phone number. |
Error Data | When this step is executed within a While Loop step, this represents errors to return to the client. For example, if an authentication error occurs, this can provide errors to display to the user. May be configured as a variable or JSON object, and contain expressions. |
Custom Branches | Additional journey branches supported for this step. For each branch, you define a branch ID and display name to label it in the editor, and a schema for the input expected back from the client. Note: The schema is used by the code generator and for autocompleting journey expressions, and isn't expected to be used by the client dynamically. The client can select a branch by returning the branch ID when calling submitClientResponse() . See Example 3: Forgot my password for implementation details. |
Branch Output Variable | Name of the variable used to store the data returned by the client, which can be used in subsequent journey steps. |
Cancel Behavior | Determines the behavior of client cancellation, which either aborts the journey (default) or proceeds to a cancel branch of the control flow. |
Password | If toggled, password authentication is a login option. Access and customize its schema via the gear icon. |
Passkeys | If toggled, WebAuthn biometric authentication is a login option. Access and customize its schema via the gear icon. |
TOTP | If toggled, TOTP authentication is a login option. Access and customize its schema via the gear icon. |
Email OTP | If toggled, email OTP authentication is a login option. Access and customize its schema via the gear icon. |
SMS OTP | If toggled, SMS OTP authentication is a login option. Access and customize its schema via the gear icon. |
Native biometrics | If toggled, mobile biometric authentication is a login option. Access and customize its schema via the gear icon. |
Examples
Below are some examples of implementing this step:
Example 1: Password login
Consider a simple login form that collects username and password for password authentication. In our example, the form ID is loginForm
, the input will be stored in a variable named loginData
, and only Password is enabled.
When executed, this step returns to the client the idoServiceResponse
object. The client then processes the request based on the form ID passed in journeyStepId
. For example:
function handleLoginForm(idoResponse) {
// Generate the form's HTML
const formHtml = `<form id="loginForm">
<input type="text" id="username" name="username" placeholder="username" />
<input type="text" id="password" name="password" placeholder="password" />
<button>Submit</button>
</form>`;
// Render the form
document.body.innerHTML = formHtml;
document.querySelector('#loginForm').addEventListener('submit', function(event) {
event.preventDefault();
// Collect the form data
const form = event.target;
const formData = new FormData(form);
// The data will look like this:
// {
// "username": "Johnny1990",
// "password": "some-password"
// }
const data = Object.fromEntries(formData.entries());
// Submit the data to the SDK
window.tsPlatform.ido.submitClientResponse("password", data);
});
}
// Data class to hold the user login credentials.
// Example data format:
// {
// "username": "Johnny1990",
// "password": "some-password"
// }
data class LoginCreds(
val username: String,
password: String
)
//...
// Collect the form data
val responseData = LoginCreds(
binding.etUserName.text.toString(),
binding.etPassword.text.toString()
)
// Submit the data to the SDK
TSIdo.submitClientResponse(
"password",
responseData,
callback
)
// Dictionary to hold the user login credentials
// Example data format:
// {
// "username": "Johnny1990",
// "password": "some-password"
// }
let passwordLoginData = ["username": "[username]",
"password": "[password]"]
// Submit the data to the SDK
TSIdo.submitClientResponse(
clientResponseOptionId: "password",
data: passwordLoginData
)
Example 2: Login options
Building on the previous example, consider a login form that supports both password and SMS OTP authentication. In this case, both password and SMS OTP methods are enabled in the step. The password branch expects username and password, while the SMS OTP branch only expects phone number in our example (since we're assuming phone number is also the username, and we updated the schema accordingly).
When executed, this step returns to the client the idoServiceResponse
object, which now includes the custom branches in the clientResponseOptions
. The client selects a branch using the branch ID (either password
or sms_otp
), and returns the relevant input. For example:
// Submits input for password authentication
window.tsPlatform.ido.submitClientResponse(
"password",
{
"username": "<username>",
"password": "<password>",
});
// Submits input for SMS OTP authentication
window.tsPlatform.ido.submitClientResponse(
"sms_otp",
{
"phone": "<phone_number>"
});
data class LoginPasswordCreds(val username: String, password: String)
data class LoginSMSOTPCreds(val phone: String)
...
val responsePasswordData = LoginPasswordCreds("[username]", "[password]")
// Submits input for password authentication
TSIdo.submitClientResponse("password", responsePasswordData, callback)
val responseSMSOTPData = LoginSMSOTPCreds("[phone]")
// Submits input for SMS OTP authentication
TSIdo.submitClientResponse("sms_otp", responseSMSOTPData, callback)
// Submits input for password authentication
let passwordLoginData = ["username": "[username]",
"password": "[password]"]
TSIdo.submitClientResponse(
clientResponseOptionId: .custom(id: "password"),
data: passwordLoginData
)
// Submits input for SMS OTP authentication
let smsOTPLoginData = ["phone": "[phone_number]"]
TSIdo.submitClientResponse(
clientResponseOptionId: .custom(id: "sms_otp"),
data: smsOTPLoginData
)
Example 3: Forgot my password
Building on the first example, suppose the login form for password authentication also includes an option the user can choose in case they forgot their password. In this example, a custom branch (forgot_password
) is added to the step, which automatically creates a new branch that will be used to execute the recovery flow. For this branch, the schema includes just the username field.
When executed, this step returns to the client the idoServiceResponse
object, which now includes the forgot password option in the clientResponseOptions
. For example, if the user clicks a "Forgot my password" link on the login form, the client can proceed to collect just the username and submit it to direct the journey to the relevant branch.
// Submits username to reset forgotten password
window.tsPlatform.ido.submitClientResponse(
"forgot_password",
{
"username": "<username>",
});
data class UsernameData(val username: String)
//...
val forgotPwdData = UsernameData("[username]")
// Submits username to reset forgotten password
TSIdo.submitClientResponse("forgot_password", forgotPwdData, callback)
// Submits username to reset forgotten password
TSIdo.submitClientResponse(clientResponseOptionId: .custom(id: "forgot_password"),
data: ["username": "[username]"])