# Identity Verification

div
div
Client SDK
div
Mobile approve
div
SSO
div
Sub-journey
> Verifies the identity of the user by comparing their selfie to a validated legal document like a driver's license or ID card


## Description

This step uses Mosaic [Identity Verification](/guides/verify/identity_verification_overview) service to validate the user's identity using documents they provide (see [Supported documents](/guides/verify/global_coverage)) and matching their documents to a live selfie. For example, a banking app can validate a user's name and ID for a new account opening, or validate the identity of a first-time app user before sending them their login credentials.

Once invoked, the step proceeds to the Mosaic verification experience.

- **In web apps**, a user is forwarded to the hosted page. This page will perform a verification process that collects user consent, captures document images and a selfie of the user, and redirects back to your configured callback URL once all the verification checks are processed.
- **In mobile apps**, Mosaic UI within the app will take over and perform a verification process that collects user consent, captures document images and a selfie of the user.


Having compeleted the identity verification part, the step will proceed to obtain the verification result. Journey execution will resume with the relevant branch based on the recommendation returned in the result (either allow, challenge, or deny).

If defined, an output variable will store the verification result (including document details and personal user info - see example [here](/guides/verify/quick_start_web/#step-6-handle-verification-result)). This data can be used in subsequent journey steps in the expression fields (e.g., `identity_result.verified_info.document.type`) or as a part of an interpolated string (e.g., `Hi ${identity_result.verified_info.person.given_name}`).

If needed, you can test the journey by configuring mock behavior for the step. For example, you can simulate a flow that results in a specific recommendation without performing an actual verification.

This step is powered by Mosaic Identity Verification. For Web, no additional configuration is needed. For mobile apps, use a dedicated SDK to invoke identity verification: [Identity Verification (iOS)](https://transmitsecurity.github.io/identityVerification-ios-sdk-docs/documentation/identityverification/) or [Identity Verification (Android)](/sdk-ref/coming_soon3).

Note for mobile developers
- Make sure the Identity Verification SDK is configured to work in the same region as other Mosaic services.
- For details on how to set up and initialize the Identity Verification SDK for mobile apps, see Steps 2 & 3 of [Android quickstart](/guides/verify/quick_start_mosaic_ui_android) or [iOS quickstart](/guides/verify/quick_start_mosaic_ui_ios).


## Configuration

div
| Field | Description |
|  --- | --- |
| **Time to live** | Time in seconds after which the verification session data will be deleted. Default is 90 days (in seconds). |
| **Callback URL*** | **(Only for web apps)** The URL that the user will be redirected to once the verification process completes.  **For web applications**, this page needs to reinitialize the SDK and resume the journey using the SDK function that restores from state (see [Orchestration SDK for Web](/sdk-ref/idosdk/interfaces/idosdk/#restorefromserializedstate)). **For web apps running SSO journeys**, this URI must match the URI configured for the SSO service (**SSO and Federation** > **Configuration**). Note: for SSO with hosted login UI, use `https://sso-app.transmitsecurity.io` or its regional variant (e.g., `https://sso-app.eu.transmitsecurity.io` for EU or `https://sso-app.ca.transmitsecurity.io`) for CA. **For mobile apps**, this URL won't be used but you have to provide any value there. |
| **Output Variable*** | Name of the variable used to store the verification result data, which can be used in subsequent journey steps. Default is **identity_result**. This corresponds to an object with the structure described [here](/openapi/verify/verifications.openapi/other/getresult#other/getresult/response&c=200) and includes document details and personal user info. |
| **Session Error Behavior** | Determines the behavior in case an unexpected error occurs during the verification process. You can either choose to abort the journey (default) or proceed to a dedicated error branch. |
| **Mocking Behavior Enabled** | If enabled, the step will be performed using mock behavior for testing purposes only. For example, you can simulate a flow that results in a specific recommendation without performing an actual verification. |
| **Recommendation** | The desired mock recommendation for this session (see [recommendations](/guides/verify/identity_verification_result/#recommendations)) |
| **Processing Time** | Desired processing time of the mock verification (in seconds) once all the images are captured. |
| **Force Recapture** | Allows simulating a recapture status for the mock flow. If **Yes**, a recapture status will be returned for the first attempt to verify the session |


## Example

In our example, the user tries to get their identity verified by submitting their documents adn selfie for verification. This step collects images and obtains a recommendation. For example, proceed normally if 'allow', terminate if 'deny', or enforce a manual review procedure if 'challenge'.

figure
a
img
figcaption
Click to open the image in a dedicated tab.
When executed, this step sends a callback to the client with the IDO service response object. It will have the `journeyStepId` set to `identityVerification` and the data will include the endpoint to redirect to.


```json
{
 "data": {
   "payload": {
     "endpoint": "<endpoint to redirect>",
     "base_endpoint": "<base endpoint>",
     "start_token": "<start token>",
     "state": "<state>",
     "session": "<session>"
     },
   }
}
```

Upon extracting the values from `data`. Web clients need to save the state of the Orchestration SDK by calling the serialize state function (see [Orchestration SDK for Web](/sdk-ref/idosdk/interfaces/idosdk/#serializestate)) and, for example, saving the state in the local storage. Once the user completes verification, the Mosaic verification experience redirects back to the web client that loads the state in order to resume the journey (see [Orchestration SDK for Web](/sdk-ref/idosdk/interfaces/idosdk/#restorefromserializedstate)). The client then processes incoming query parameters and submits the response to the journey using the Orchestration SDK call.

JavaScript

```js
// On page load
const params = Object.fromEntries(new URLSearchParams(window.location.search).entries());
const sdkState = loadFromLocalStorage(); // Implementation of this function is up to the developer
if (params.state && params.sessionId && sdkState) {
  // Indicates the journey resumes after redirection
  // If SDK was loaded via script tag, invoke functions inside 'window.tsPlatform'
  const idoResponse = ido.restoreFromSerializedState(sdkState);
  if (idoResponse.journeyStepId === IdoJourneyActionType.IdentityVerification) {
    handleIdentityVerification(idoResponse);
  } else { /* Handle this */ }
} else { // Indicates a regular journey flow
  // TODO: Init IDO DK, start journey, and loop+switch on IdoJourneyActionType
  //
}

// Handle the Identity verification step
async function IdentityVerification(idoResponse) {
  const params = Object.fromEntries(new URLSearchParams(window.location.search).entries());

  if (params.state && params.sessionId) {
    // Mosaic verification experience returns params
    // Submit the data to the SDK
    const data = {
      payload: {
        state: params.state,
        sessionId: params.sessionId,
      },
    };
    // If SDK was loaded via script tag, invoke functions inside 'window.tsPlatform'
    await ido.submitClientResponse(ClientResponseOptionType.ClientInput, data);
  } else {
    // If the sessionID and state are not in the URL, redirect the user to the Mosaic verification experience
    // If SDK was loaded via script tag, invoke functions inside 'window.tsPlatform'
    const sdkState = ido.serializeState();
    await saveToLocalStorage(sdkState); // implementation of this function is up to the developer

    window.location.href = idoResponse.data?.payload.endpoint;
  }
}
```

Swift

```swift
class MyViewController: UIViewController {
    private func handleIdoResponse(_ response: TSIdoServiceResponse) {
        guard let stepId = response.journeyStepId else {
            debugPrint("[DEBUG] Missing step id in the journey response")
            return
        }

        if stepId == .identityVerification {
            guard let payload = response.data["payload"] as? [String: Any],
                  let startToken = payload["start_token"] as? String {
                      debugPrint("[DEBUG] Start token is missing")
                      return
                  }

            initializeAndStartIDVMosaicUI(startToken: startToken)
        }
    }


    func initializeAndStartIDVMosaicUI(startToken: String) {
        // Initialize Identity Verification SDK
        TSIdentityVerification.initialize(baseUrl: "[BASE_URL]", clientId: "[CLIENT_ID]")
        TSIdentityVerification.mosaicUIDelegate = self
        // Start Identity Verification with Mosaic UI
        TSIdentityVerification.startMosaicUI(startToken: startToken)
    }
}

extension MyViewController: TSIdentityVerificationMosaicUIDelegate {
    func mosaicUIVerificationDidComplete() {
        debugPrint("[DEBUG]: verification process did complete")

        // Submit parameters to Orchestration SDK
        do {
            try TSIdo.submitClientResponse(clientResponseOptionId: .clientInput)
        } catch {
            debugPrint("[DEBUG] Failed to submit verification completion: \(error)")
        }
    }

    func mosaicUIVerificationDidCancel() {
        // Handle cancellation
    }

    func mosaicUIVerificationDidFail(with error: TSIdentityVerificationError) {
        debugPrint("[DEBUG]: Verification process did fail with error: \(error)")

        // Submit failure to Orchestration SDK
        do {
            try TSIdo.submitClientResponse(clientResponseOptionId: .fail)
        } catch {
            debugPrint("[DEBUG] Failed to submit verification failure: \(error)")
        }
    }
}
```

Kotlin

```kotlin
private fun processServiceResponse(idoResponse: TSIdoServiceResponse) {
    when (idoResponse.journeyStepId) {
        ...
        TSIdoJourneyActionType.IdentityVerification.type -> handleIdentityVerification(idoResponse)
        ...
    }
}

private fun handleIdentityVerification(
    result: TSIdoServiceResponse
) {
    // Check if the camera permission is granted
    if (ContextCompat.checkSelfPermission(context, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
        // Extract the start_token and base_endpoint from IDO response
        val idvStartToken = (result.data as JSONObject).optJSONObject("payload").optString("start_token")
        val idvBaseEndpoint = (result.data as JSONObject).optJSONObject("payload").optString("base_endpoint")

        // Start the identity verification process using the IDV SDK
        TSIdentityVerification.initialize(context, CLIENT_ID, idvBaseEndpoint)
        TSIdentityVerification.registerForStatusMosaicUI(this) // "this" implements ITSIdentityVerificationMosaicUIStatus interface
        TSIdentityVerification.startWithSmartUI(context, idvStartToken)
    } else {
        // Request camera permission
        ActivityCompat.requestPermissions(
            activity,
            arrayOf(android.Manifest.permission.CAMERA),
            101
        )
    }
}

// This method is implemented by the listener that implements the ITSIdentityVerificationMosaicUIStatus interface
override fun mosaicUIVerificationCompleted() {
    val session = (idoResponse.data as JSONObject).optJSONObject("payload").optString("session")
    val state = (idoResponse.data as JSONObject).optJSONObject("payload").optString("state")
    val payload = IDVResponsePayload(session, state)

    // Submit response to Orchestration SDK
    TSIdo.submitClientResponse(TSIdoClientResponseOptionType.ClientInput.type, IDVResult(payload), callback)
}
...
data class IDVResponsePayload(val sessionId: String, val state: String)
data class IDVResult(val payload: IDVResponsePayload)
```