PingOne Recognize

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 (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:

Setting headers

These endpoints require authorization through the kl-api-key: API_KEY header. You can find this on the PingOne Recognize dashboard (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.

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.

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.

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.

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. Client state is required to execute Account Recovery and new device activation flows.