# Cross-device face authentication

Cross-device face authentication extends the standard [face authentication](/guides/user/auth_face) flow to support scenarios where users begin the login process on a desktop or laptop but perform the biometric capture on a personal mobile device. This is especially useful for users who cannot access an internal enterprise mobile application—such as contractors, interview candidates, or external partners—and must authenticate using a personal phone instead.

Important
This guide builds on the [Face authentication](/guides/user/auth_face) guide. Make sure you are familiar with the base flow and requirements, have installed required SDKs, and have checked prerequisites before proceeding. This guide introduces modifications to the existing journeys to enable the cross-device capability.

## How it works

Below are examples of registration and authentication flows.

### Face registration

Face registration relies on the Identity Verification and works the same way as in the [base guide](/guides/user/auth_face#face-registration). If the user starts the flow in the web app, the hosted Identity Verification performs the handoff to mobile automatically.

### Cross-device face authentication

**Initiating a face authentication on a desktop:**

When a user requests to log in from a desktop or laptop with face authentication as a second factor, the authentication journey detects that the client is running on a non-mobile device. Instead of requesting a selfie directly, the journey creates a cross-session message and displays a QR code on the desktop screen. The journey running on a desktop waits for the mobile journey to communicate back the result. Once received, the web journey processes the message from the mobile journey and continues.

```mermaid
sequenceDiagram
    participant User
    participant Desktop as Desktop client
    participant IDO as IDO SDK
    participant M as Mosaic

    User-->>Desktop: Log me in
    Desktop->>Desktop: Init IDO SDK
    Desktop->>IDO: Start journey
    IDO-->>IDO: Match case: detect desktop
    IDO-->>M: Create cross-session message
    M-->>IDO: Message ID (ticket)
    IDO->>Desktop: QR code (with ticket)
    Desktop-->>User: Display QR code
    note over User, M: Handoff to a mobile device
    M-->>IDO: Desktop journey resumes
    IDO-->>IDO: Authentication succeeded?
    IDO->>Desktop: Journey complete
    Desktop-->>User: Logged in
```

**Completing face authentication on mobile:**

The user scans the QR code with their personal mobile device, which initiates a new instance of the journey. It detects the client and cross-session message, and performs the selfie capture. Once the selfie is acquired and validated on the mobile device, the result is communicated via the cross-session message.

```mermaid
sequenceDiagram
    participant User
    participant Mobile as Mobile client
    participant IDO as IDO SDK
    participant IDV as IDV SDK
    participant M as Mosaic

    User-->>Mobile: Scans QR code
    Mobile->>Mobile: Init SDKs
    Mobile->>IDO: Start journey (with ticket)
    IDO-->>IDO: Match case: detect mobile + cross-session
    IDO->>Mobile: Selfie acquisition step
    IDV->>User:Capture selfie
    User->>IDV: Selfie
    IDV-->>IDO: Complete
    IDO-->>M: Selfie data
    M-->>M: Face authentication
    M-->>IDO: Complete
    IDO-->>M: Process cross-session message
```

## Before you start

Complete all prerequisite steps described in the [Face authentication](/guides/user/auth_face#before-you-start) guide, including creating an application, installing SDKs, and implementing a login method for existing users.

## Support cross-device handoff in the journey

div
div
Admin Portal
The cross-device flow requires modifying the authentication journey from the [base guide](/guides/user/auth_face#authentication-journey) to detect the client platform and route the flow accordingly.

figure
a
img
figcaption
Click to open the image in a dedicated tab.
  
Example
This is an example journey that represents a basic use case that uses face authentication as a second factor after email OTP. Adjust your journey as needed. The numbered callouts in the example journey above highlight the areas that differ from the basic use case and require configuration for cross-device face authentication.

### 1. Match case branching

Add the [Match case](/guides/orchestration/journeys/match_case) step as an initial step in the journey. The source expression should detect whether the journey is running on a mobile or desktop device and whether a cross-session message is present before branching accordingly. For example, you can use the code below to evaluate the user agent and check for a cross-session message.

```bash
let userAgent = @policy.request().headers["user-agent"] return
let commonMobileKeys = ["Mobi","Android","iPhone","iPad","iPod","Windows Phone","Opera Mini","IEMobile"] return
let isMobile = @std.containsIf(commonMobileKeys, (item) => @strings.refind(userAgent,item)) return
let isCross = @policy.getInteractionParameters().interactionParams.claims.csm != null return
let result = isMobile ? (isCross ? "Mobile - Cross" : "Mobile - Main") : (isCross ? "Desktop - Cross" : "Desktop - Main")
return result
```

This routes the journey into one of the following branches:

| Branch | Description |
|  --- | --- |
| **Desktop - Main** | The journey runs on a desktop without a cross-session context. This is the primary entry point: collects the user identifier and initiates the cross-device handoff. |
| **Mobile - Main** | The journey runs on a mobile device without a cross-session context. This is the primary entry point for the standard mobile face authentication (same as the [base guide](/guides/user/auth_face)). |
| **Mobile - Cross** | The journey runs on a mobile device with a cross-session context. This is the branch triggered when the user scans the QR code—it performs the selfie capture and communicates the result back. |
| **Desktop - Cross** | Not a valid scenario. Routes to [Reject access](/guides/orchestration/journeys/reject_access). |
| **Otherwise** | Fallback for unrecognized contexts. Routes to [Reject access](/guides/orchestration/journeys/reject_access). |


### 2. Desktop - Main branch

This branch represents the primary entry point for the cross-device use case. The user starts the face authentication on the desktop, and the journey initiates the cross-device handoff to a mobile device. For example, it can include the following actions:

figure
a
img
figcaption
Click to open the image in a dedicated tab.
  
1. [Login form](/guides/orchestration/journeys/login_form) and [Email OTP authentication](/guides/orchestration/journeys/authenticate_email_otp) steps: Displays the login form and performs email OTP authentication as a first factor.
2. [Create cross-session message](/guides/orchestration/journeys/create_cross-session_message) step: Creates a cross-session message containing the user context so it can be shared with the mobile session. Configure the step as follows:
  - **Cross-session message request fields**: add `user_context` field with `@policy.userContext()` value
  - **Output variable**: set to `csm`
3. [Wait for cross-session message](/guides/orchestration/journeys/wait_for_cross-session_message) step: Waits for the mobile device to process the cross-session message, displaying a QR code that the user scans with their phone. Configure the step as follows:
  - **Message id**: set to `csm`
  - **Message id format**: set to "Display as QR code"
  - **Message id display mode**: set to "Display Custom Expression"
  - **Message id to display**: set to expression below. Client ID can be found in your app settings in the Admin Portal. Redirect URL can be any page the client redirects to upon completion, such as SSO hosted domain URL or your custom domain. Base URL depends on your region or custom domain (api.transmitsecurity.io, api.sbx.transmitsecurity.io, etc.),

```bash
   let clientId = "<YOUR_CLIENT_ID>" return
   let redirectUrl = `https://<YOUR_REDIRECT_URL>/cross-device-complete/?clientId=${clientId}` return
   let claims = @strings.tojson({csm: csm, id_token:{}}) return
  `https://<YOUR_BASE_URL>/cis/oidc/auth?client_id=${clientId}&redirect_uri=${@strings.urlEncode(redirectUrl)}&response_type=code&prompt=login&scope=openid&claims=${@strings.urlEncode(claims)}`
```
4. [Condition](/guides/orchestration/journeys/condition) step: Once the mobile session completes, checks whether the face authentication succeeded.
5. [Complete journey](/guides/orchestration/journeys/complete_journey) or [Reject access](/guides/orchestration/journeys/reject_access) steps: Depending on the result, successfully completes the journey or rejects access.


### 3. Mobile - Cross branch

This branch runs on the mobile device after the user scans the QR code displayed on the desktop. It performs the selfie capture and face authentication, then communicates the result back to the desktop session. For example, it can include the following actions:

figure
a
img
figcaption
Click to open the image in a dedicated tab.
  
1. [Set temporary variables](/guides/orchestration/journeys/set_temporary_variables) step: Extracts cross-session message values and sets temporary variables for use later in the flow. Configure this step as follows:
  - Variable `csm`: set to `@policy.getInteractionParameters().interactionParams.claims.csm`
  - Variable `original_user_context`: set to `@policy.readCrossSessionMessage(@policy.getInteractionParameters().interactionParams.claims.csm).user_params.user_context`
2. [Restore user context](/guides/orchestration/journeys/restore_user_context) step: Restores the user context from the cross-session message so the journey can act on behalf of the authenticated user. Configure this step as follows:
  - **Saved user context**: set to `original_user_context`
  - If you expect multiple users to use the same device for authentication (meaning somebody could have already authenticated on this mobile device), consider clearing user context first by setting **Saved user context** to `null`
3. [Selfie acquisition](/guides/orchestration/journeys/selfie_acquisition) step: Obtains a selfie image from the user on the mobile device.
4. [Face authentication](/guides/orchestration/journeys/authenticate_face) step: Verifies the selfie against the stored reference vector. Configure this step as follows:
  - **Identifiers**: set to "Email"
  - **Identifier value**: set to `@policy.userContext().user_identifiers.email`
5. Depending on the result:
  - **Success**: [Process cross-session message](/guides/orchestration/journeys/process_cross-session_message) and [Complete journey](/guides/orchestration/journeys/complete_journey) steps: Processes the cross-session message to signal success back to the desktop session, then completes the journey. Configure the Process cross-session message step as follows:
    - **Cross-session message reply fields**: add `access_token` field with `@policy.userContext().access_token` value
    - **Output variable**: set to `cross_session_message`
    - **Message id**: set to `csm`
  - **Failure**: [Process cross-session message](/guides/orchestration/journeys/process_cross-session_message) and [Reject access](/guides/orchestration/journeys/reject_access) steps: Processes the cross-session message to signal failure back to the desktop session, then rejects access. Configure the Process cross-session message step as follows:
    - **Cross-session message reply fields**: add `error` field with `error` value
    - **Output variable**: set to `cross_session_message`
    - **Message id**: set to `csm`