---
title: IDV Bridge SaaS
description: Enroll user images through an API to facilitate ongoing authentication through our Mobile or Web SDK.
component: recognize
page_id: recognize:idv-bridge:idv-bridge-saas
canonical_url: https://docs.pingidentity.com/recognize/idv-bridge/idv-bridge-saas.html
llms_txt: https://docs.pingidentity.com/recognize/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
section_ids:
  integration-guide: Integration guide
  performing-the-enrollment: Performing the enrollment
  setting-headers: Setting headers
  optional-headers: Optional headers
  scenarios: Scenarios
  encrypting-the-image: Encrypting the image
  create-an-aes-gcm-or-aes-gcm-siv-key: Create an AES-GCM or AES-GCM-SIV key
  encrypt-aes-gcm-or-aes-gcm-siv-key: Encrypt image with an AES-GCM or AES-GCM-SIV key
  encrypt-the-aes-gcm-or-aes-gcm-siv-key: Encrypt the AES-GCM or AES-GCM-SIV key
  exporting-the-client-state: Exporting the client state to authenticate a user through Mobile SDK
---

# IDV Bridge SaaS

## Integration guide

Enrolling a user with a "selfie" through the authentication service is a straightforward process, but clients must apply appropriate security measures during implementation.

There are three fundamental steps to the process:

1. **Performing the enrollment** - Send the image to our APIs to register the user's face in a privacy-preserving manner.

2. **Encrypting the image** - Ensure the image is encrypted.

3. **Exporting the client state** - Enable users to authenticate successfully by exporting client state, then using it to bind a new device with the [Mobile SDK](../mobile-sdk/mobile-sdk-account-recovery.html) (this isn't required when you authenticate users only with the Web SDK).

### Performing the enrollment

To perform an enrollment, make a POST request to one of these URLs:

* **Sandbox**: `https://authentication-service-sandbox.eks.core-production.keyless.technology//v1/users/{customer}/{username}`

* **Europe**: `https://authentication-service.eks.core-production.keyless.technology//v1/users/{customer}/{username}`

* **Latam**: `https://authentication-service.eks.core-production.latam.keyless.technology/v1/users/{customer}/{username}`

* **US (East Coast)**: `https://authentication-service.eks.core-production.saas-us-east.keyless.technology/v1/users/{customer}/{username}`

#### Setting headers

These endpoints require authorization through the `kl-api-key: API_KEY` header. You can find this on the [PingOne Recognize dashboard](https://dash.keyless.io/) (Access Control tab), then create a Secret API Key.

The request body is the image, sent as binary data. The following encryption headers are required.

| Key (case-insensitive) | Key value (examples or details)                                        | Description                                                                                                                         |
| ---------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `kl-key-id`            | `KEYLESS_KEY_ID` Example: `alias/kl-core-staging-remote-sdk-image-key` | Identifier of the key used to encrypt the image encryption key. This ID is provided by PingOne Recognize along with the public key. |
| `kl-key-algorithm`     | `RSAES-OAEP-SHA-256`                                                   | Algorithm used to encrypt the image encryption key. Currently, only `RSAES-OAEP-SHA-256` is supported.                              |
| `kl-Image-Key`         | Raw key that must be at least 128 or 256 bits long                     | Encrypted key used to encrypt the image. Must be hex encoded. [Further details](#encrypt-aes-gcm-or-aes-gcm-siv-key).               |
| `kl-Image-Algorithm`   | Either `AES-GCM` or `AES-GCM-SIV`                                      | Defines the image algorithm used to encrypt the image.                                                                              |
| `kl-Scenario`          | `SELFIE`, `DOCUMENT`, `TRUSTED_SOURCE`                                 | Defines the source from which the image was captured, allowing IDV Bridge to adapt accordingly.                                     |

#### Optional headers

| Key (case-insensitive) | Key value (examples or details) | Description                                                                                                                                                                                                                |
| ---------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kl-transaction-data`  | `enrollment-123456`             | Supports creation of an additional JWT signature from a string, signed by PingOne Recognize if enrollment succeeds. The signed data is returned as `transaction_jwt` and can be verified later using your public key.      |
| `kl-exif-transpose`    | `true` or `false` (default)     | If `true`, instructs IDV Bridge to rotate the image according to available EXIF metadata. Useful when submitted selfies are rotated by 90 degrees. Can adversely impact trusted images, so the default is `false`.         |
| `kl-seed-entropy`      | `true` or `false` (default)     | Seed entropy is a user-unique value used to generate cryptographic keys or other unique values. If `true`, the user's seed entropy is returned in the response and encrypted with the same key and algorithm as the image. |

#### Scenarios

PingOne Recognize has optimized this service for three primary use cases. Set this using `kl-scenario`.

| Scenario         | Description                                                                                                                        | Example                                                                                                                                                  |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TRUSTED_SOURCE` | For user faces captured with a consistent quality control process. Some quality checks are removed to minimize false rejections.   | \* Extracted from a passport RFID chip through NFC. \* Captured through a third-party process with quality checks (face clearly visible, good lighting). |
| `SELFIE`         | For user faces captured by a process with no quality checks. User is asked specifically to capture a selfie instead of a document. | \* Selfies with no additional artifacts, though quality can vary.                                                                                        |
| `DOCUMENT`       | For identity document images only. Tuned to ignore additional faces that might be present (watermarks, holograms).                 | Images of identity documents that contain a portrait, such as passports, driver's licenses, or identity cards.                                           |

## Encrypting the image

The image must be sent encrypted. Use `AES-GCM` or `AES-GCM-SIV`. `AES-GCM-SIV` is recommended where possible.

The encryption flow is:

### Create an AES-GCM or AES-GCM-SIV key

Generate a new `AES-GCM` or `AES-GCM-SIV` key on your server. Key length can be 128, 192, or 256 bits.

The important requirement is access to the raw key bytes.

```typescript
import { siv } from '@noble/ciphers/aes'
import { getRandomValues } from 'crypto'

// fill a 128 bits Uint8Array with cryptographically secure random values
const key = getRandomValues(new Uint8Array(16))

// fill a 96 bits Uint8Array with cryptographically secure random values
const nonce = getRandomValues(new Uint8Array(12))

// create the AES-GCM-SIV cipher with key and nonce
const cipher = siv(key, nonce)
```

Using `@noble/ciphers` in Node.js, generate the nonce beforehand.

|   |                                                                                                                                                                                                         |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | This key is required to symmetrically encrypt the image and transmit it safely over the internet. This is in addition to TLS and makes access to clear image data significantly harder for an attacker. |

### Encrypt image with an AES-GCM or AES-GCM-SIV key

After the `AES-GCM` or `AES-GCM-SIV` key is ready, generate 96 random bits for the nonce and encrypt the image.

Prepend nonce bytes to the encrypted image bytes.

```typescript
import { siv } from '@noble/ciphers/aes'
import { getRandomValues } from 'crypto'

// read real image here
const face = new Uint8Array()

const key = getRandomValues(new Uint8Array(16))
const nonce = getRandomValues(new Uint8Array(12))
const cipher = siv(key, nonce)

// encrypt the face Uint8Array
const encryptedFace = cipher.encrypt(face)

// create a new Uint8Array with enough bytes to contain both nonce and encrypted face bytes
const encryptedFaceWithNonce = new Uint8Array(nonce.length + encryptedFace.length)

// set the nonce bytes from the first position
encryptedFaceWithNonce.set(nonce, 0)

// set the encrypted face bytes after the nonce bytes
encryptedFaceWithNonce.set(encryptedFace, nonce.length)
```

|   |                                                                                                                                                            |
| - | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | Sending the nonce separately isn't best practice, so prepend it to the encrypted face bytes. This is standard and allows the Web SDK server to decrypt it. |

### Encrypt the AES-GCM or AES-GCM-SIV key

For the Web SDK server to decrypt the image, pass the `AES-GCM` or `AES-GCM-SIV` key in encrypted form using an `RSAES-OAEP-SHA-256` public key.

PingOne Recognize provides the `RSAES-OAEP-SHA-256` public key in SPKI format.

```typescript
import { getRandomValues, publicEncrypt } from 'crypto'

const key = getRandomValues(new Uint8Array(16))

// put complete public key here
const publicKey = '-----BEGIN PUBLIC KEY-----...'

// encrypt the key
const encryptedKey = publicEncrypt(publicKey, key)
```

This is the final encryption step before sending the request.

|   |                                                                                                                                                                                                                                                                                                                                                                                                                   |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | The key used to encrypt the image must be sent to the Web SDK server so it can decrypt the image, but it cannot be sent in clear text. Encrypt it first using RSA asymmetric encryption.Only the Web SDK server can use the RSA private key counterpart to decrypt data encrypted with the RSA public key.The Web SDK server doesn't directly know the RSA private key at any time. Decryption is handled by KMS. |

## Exporting the client state to authenticate a user through Mobile SDK

After successful enrollment through IDV Bridge SaaS, integrators should export client state if they plan to run ongoing authentication through the [Mobile SDK](../mobile-sdk/mobile-sdk-introduction.html). Client state is required to execute [Account Recovery](../mobile-sdk/mobile-sdk-account-recovery.html) and new device activation flows.
