# Risk-based login quickstart

This guide walks you through building a simplistic authentication flow that adapts based on real-time risk indicators using [Identity Threat Protection](/guides/user/identity_threat_protection) and journeys.

Note
This guide explains how to implement **risk-based login** using [Journeys](/guides/journeys_intro) and Orchestration SDKs.

## How it works

When a user requests to log in to the app, the client invokes the authentication journey (1) and obtains the user credentials (2). After authenticating the user, Mosaic's Identity Threat Protection (3) evaluates the login event and returns a set of discrete risk indicators (4). The risk signals are then evaluated against the rule engine, which maps them to an Allow, Deny, or Custom decision (5). The Risk Level Analysis step branches accordingly, letting you handle each outcome directly in the journey (6).

```mermaid
sequenceDiagram
    participant User
    participant App as Client App
    participant SDK as Orchestration SDK
    participant Mosaic

    User-->>App: Request login
    App->>SDK: startJourney()
    SDK-->>Mosaic: Start authentication journey
    Mosaic-->>SDK: Login Form step
    SDK->>App: journeyStepId: LoginForm
    App-->>User: Display login form
    User-->>App: Enter credentials
    App->>SDK: submitClientResponse("password", data)
    SDK-->>Mosaic: Submit credentials
    Mosaic-->>Mosaic: Authenticate user (Password Authentication)
    Mosaic-->>Mosaic: Evaluate risk (Identity Threat Protection)
    Mosaic-->>Mosaic: Evaluate against rules

    alt Allow (rule matched)
        Mosaic-->>SDK: Journey complete
        SDK->>App: Success + token
    else Deny (rule matched)
        Mosaic-->>SDK: Journey rejected
        SDK->>App: Rejection
    else Custom (no rule matched)
        Mosaic-->>Mosaic: Evaluate conditions on risk factors
        Mosaic-->>SDK: Journey complete or rejected
        SDK->>App: Success or Rejection
    end
```

Important
Identity Threat Protection returns risk indicators. The rule engine maps risk signals to Allow/Deny decisions. If no rule matches, the flow proceeds to the Custom branch where your journey defines how to act on the returned signals.

## Before you start

div
div
Admin Portal
1. If this is your first time integrating with Mosaic, create an application with OIDC client in the Admin Portal as described [here](/guides/user/create_new_application).
2. Ensure that **Identity Threat Protection** is enabled as the risk engine for your tenant. Identity Threat Protection and Fraud Prevention are mutually exclusive—contact your Customer Success Manager to enable the appropriate engine.


## Step 1: Configure rules

div
div
Admin Portal
Under **Journey Tools** > **Rules**, define allow and deny rules that determine risk decision logic:

- **Allow rules**: define trusted entities (e.g., known IP ranges, corporate devices) that should bypass risk-based restrictions.
- **Deny rules**: block access from specific origins or attributes (e.g., TOR exit nodes, flagged devices).


For example, you can create a rule that denies logins from specific locations and a rule that denies logins from devices you consider risky, such as devices with enabled VPN and TOR.

When the Risk Level Analysis step evaluates an interaction, it checks the detected risk signals against these rules. Rules are evaluated in order (1-100) and only the first rule to match will apply. If a rule matches, the step branches to the corresponding Allow or Deny branch. If no rule matches, the step proceeds to the Custom branch.

For details on how to create and manage rules, see [Enforce rules](/guides/risk/rules).

Note
Rules are optional. If no rules are defined, all interactions proceed to the Custom branch where you can use Condition steps to evaluate risk factors manually.

## Step 2: Build journey

div
div
Admin Portal
Journeys handle the business logic responsible for this flow. In the Admin Portal, go to **B2C Identity** or **B2B Identity** based on your setup, and open the **Journeys** section. Create a client SDK journey and store its ID. You'll need to implement some client-side code for this journey later.

Note
You can adapt the journey to match your authentication policy and use case by replacing or adding the authentication steps with the methods you prefer to use.

The example journey collects credentials, authenticates the user using their password, evaluates risk, and then branches based on the rule engine decision. It includes the following steps:

1. [Login form](/guides/orchestration/journeys/login_form): Enable password authentication as authentication option and ensure schema collects the user's username (`loginData1.username`) and password (`loginData1.password`).
2. [Password Authentication](/guides/orchestration/journeys/authenticate_password): Configure the step to use submitted user's credentials for authentication.
3. [Risk Level Analysis](/guides/orchestration/journeys/risk_level_analysis): Enable evaluation for `login` event. The step evaluates risk and branches based on the rule engine outcome:
  - **Allow** (rule-based): A rule matched and allows the interaction—proceeds to the [Complete Journey](/guides/orchestration/journeys/complete_journey) step.
  - **Deny** (rule-based): A rule matched and denies the interaction—proceeds to the [Reject Access](/guides/orchestration/journeys/reject_access) step.
  - **Custom**: No rule matched—use a [Condition](/guides/orchestration/journeys/condition) step to check for specific risk factors (e.g., `@std.contains(risk_analysis.risk_result.reasons.IP_RISKY_REPUTATION)`) and branch accordingly.


figure
a
img
figcaption
Click to open the image in a dedicated tab.
## Step 3: Add SDKs

div
div
client
To run this flow, your integration needs the Orchestration SDK.

Web
Install and initialize the [Platform Web SDK](/sdk-ref/platform/introduction). Make sure to initialize Orchestration with `collectRiskData: true` to enable Identity Threat Protection.

```js
import { ido, initialize } from '@transmitsecurity/platform-web-sdk';

initialize({
  clientId: 'your-client-id',
  ido: {
    serverPath: 'https://api.transmitsecurity.io/ido', // Required: Set serverPath based on your region or custom domain
    collectRiskData: true // Enables Identity Threat Protection
  }
});
```

iOS
Install and initialize the Orchestration SDK as described in the [iOS quickstart](/guides/orchestration/getting-started/quick_start_ios). Set `collectRiskData` to `true` during initialization:

```swift
do {
    try TSIdo.initialize(clientId: clientId,
                         options: .init(serverPath: "[BASE_URL]",
                                        locale: "[LOCALE]",
                                        collectRiskData: true))
} catch {
    // Handle error
}
```

Android
Install and initialize the Orchestration SDK as described in the [Android quickstart](/guides/orchestration/getting-started/quick_start_android). Set `collectRiskData` to `true` during initialization:

```kotlin
val collectRiskData = true
TSIdo.initializeSDK(
    context,
    clientID,
    TSIdoInitOptions(baseUrl, pollingTimeut, resourceUri, locale, collectRiskData)
)
```

Note
Configure the SDK to work with your region or [custom domain](/guides/deployment/custom_domains) by setting the appropriate `serverPath` / `baseUrl`.

## Step 4: Implement journey logic

div
div
client
In this step, you implement the client-side code needed to complete a risk-aware authentication journey created in [Step 3](#step-3-build-risk-aware-journey).

Implementation tips
You may implement a switch (or other routing mechanism) that invokes a handler responsible for a specific journey step (returned in `idoServiceResponse.journeyStepId` parameter). Each handler processes data, displays whatever UI is needed, and calls `submitClientResponse()` when the interaction is concluded.

The journey loops back to the switch unless Mosaic signals journey completion by setting the `journeyStepId` property to `Rejection.type` or `Success.type`. The `idoServiceResponse.token` property contains a JWT token as a proof of journey completion.

In a nutshell, you'll need to implement:

- [Starting the journey](#1-start-the-journey)
- [Handling the login form](#2-handle-login-form)


All steps except the Login Form step in this journey (password authentication, risk evaluation, branching) are handled server-side by Mosaic and do not require any client-side code.

### 1. Start the journey

Implement a handler that starts the journey by calling `startJourney()` with your journey ID.

Web
```js
const idoResponse = await ido.startJourney('YOUR_JOURNEY_ID');
```

iOS
```swift
do {
    try TSIdo.startJourney(journeyId: "YOUR_JOURNEY_ID")
} catch {
    debugPrint("[DEBUG] Failed to start journey: \(error)")
}
```

Android
```kotlin
TSIdo.startJourney(
    "YOUR_JOURNEY_ID",
    TSIdoStartJourneyOptions(),
    callback
)
```

### 2. Handle login form

The only client-facing step in this journey is the [Login Form](/guides/orchestration/journeys/login_form) step, which requests the user's username and password. Your client should present a login form and submit the collected credentials back to the journey using `"password"` as the branch ID.

Web
```js
function handleLoginForm(idoResponse) {
  const form = document.querySelector('#loginForm');
  const formData = new FormData(form);
  const data = Object.fromEntries(formData.entries());

  ido.submitClientResponse("password", data);
}
```

iOS
```swift
let data = [
    "username": collectedUsername,
    "password": collectedPassword
]
do {
    try TSIdo.submitClientResponse(
        clientResponseOptionId: "password",
        data: data
    )
} catch {
    debugPrint("[DEBUG] Failed to submit form data: \(error)")
}
```

Android
```kotlin
data class LoginData(val username: String, val password: String)

val responseData = LoginData(collectedUsername, collectedPassword)
TSIdo.submitClientResponse(
    "password",
    responseData,
    callback
)
```