IDV Bridge SaaS

Enroll user images via an API in order to facilitate on-going authentication via our Mobile or Web SDK.

Integration Guide

Enrolling a user with a "selfie" via our Authentication Service is a straightforward process, but please note it's imperative that clients consider the appropriate security measures that need to be followed 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 while doing so.

  3. Exporting the client state - enable users to successfully authenticate, by exporting the client state which can then be leveraged to bind a new device via the Mobile SDK (note this won't be required if you are authenticating users via the WebSDK only).

Performing the enrollment

In order to perform an enrollment you must make a post request with the following URLs:

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

Setting Headers

These endpoints sit behind authorization, which must be provided through the kl-api-key: API_KEY header, this can be found on the Keyless dashboard (Access Control tab) and create a Secret API Key from this page.

The body of this request is going to be the image, which must be sent as a binary. The following encryption headers are also then required.

Key (non case sensitive)
Key value (examples or details)
Description

kl-key-id

KEYLESS_KEY_ID

Example: alias/kl-core-staging-remote-sdk-image-key

The identifier of key that is used to encrypt image encryption key.

This ID is provided by Keyless along with public key.

kl-key-algorithm

RSAES-OAEP-SHA-256

The 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

Is the encrypted key that you used to encrypt the image and must be hex encoded. Further details here.

kl-Image-Algorithm

Can be 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 captured, allowing IDV Bridge to adapt accordingly. Further details here.

Optional headers

Key (non case sensitive)
Key value (examples or details)
Description

kl-transaction-data

enrollment-123456

Supports the creation of an additional JWT signature from a string, which can be signed by Keyless if the enrollment is successful. The signed data is returned in the response as transaction_jwt and can then later be verified using your public key.

kl-exif-transpose

true

false (default)

If set to true instructs IDV Bridge to rotate the image according to any available EXIF metadata. This is of course useful if users have submitted selfies that have been incorrectly rotated 90 degrees. It may have an adverse impact on trusted images hence the default value is set to false.

kl-seed-entropy

true

false (default)

Seed entropy is a value that is unique to a user and can be used to generate cryptographic keys or other values that are unique to the user. When set to true this returns the user's seed entropy in the response and is encrypted by the same key and algorithm as the image.

Scenarios

Keyless has optimized this service based on the three primary Use Cases that we typically see and this must be defined via kl-scenario.

Scenario
Description
Example

TRUSTED_SOURCE

For user faces captured with quality control process consistently in place. Note this will remove some quality checks to minimize the risk of false rejections.

  • Extracted from a Passport RFID chip via NFC.

  • Captured via a third party process with quality checks in place to assist the user (face is clearly visible, good lighting)

SELFIE

For user faces captured by a process with no quality checks in place. The user has been asked specifically to capture a selfie rather than a document.

  • Selfies with no additional artefacts, though quality may be variable.

DOCUMENT

For Identity Document images only. This setting has been tuned to ignore the additional faces that may 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, the encryption can be done with AES-GCM or AES-GCM-SIV, we recommend using AES-GCM-SIV if possible since it’s more secure.

Let’s explain the encryption flow in steps.

Create AES-GCM or AES-GCM-SIV Key

Generate a new AES-GCM or AES-GCM-SIV key on your server, length of the key can be 128, 192 or 256 bits. The important bit here is to have access to the raw bytes of the key.

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 the nonce must be generated beforehand.

This key is required to symmetrically encrypt the image and transmit it safely over the internet, this is on top of TLS making it very hard for an attacker to access the image in clear.

Encrypt Image with AES-GCM or AES-GCM-SIV Key

Now that you have the AES-GCM or AES-GCM-SIV key ready to use, generate 96 random bits to use as the nonce and encrypt the image.

The nonce bytes must be prepended 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 is not a best practice, so it must be prepended to the encrypted face bytes. This is a standard and will make it possible for the Web SDK server to decrypt it.

Encrypt AES-GCM or AES-GCM-SIV Key

In order for the Web SDK server to decrypt the image, the AES-GCM or AES-GCM-SIV key must be passed, but it will also need to be encrypted, this time with a RSAES-OAEP-SHA-256 public key.

The RSAES-OAEP-SHA-256 public key will be given to you in SPKI format by Keyless.

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 marks the last bit of encryption that must be done before sending the request, let’s look at the headers that must be specified.

The key that is used to encrypt the image must be sent to the Web SDK server in order for it to decrypt the image again, but it cannot of course be sent in clear. This is why it must be encrypted first by leveraging RSA asymmetric encryption capabilities.

The Web SDK server has exclusive access to the RSA private key counterpart, meaning that it’s the only entity with the permissions to decrypt anything that is encrypted with the RSA public key.

Furthermore, the Web SDK server does not know the RSA private key at any point in time, decryption is handled by KMS.

Exporting the client state

Having successfully enrolled a user via IDV Bridge SaaS, it is critical that integrators export the client state if they are planning to conduct any on-going authentication via our Mobile SDK, which will then allow them to authenticate on that specific device on an on-going basis. Please follow this link for the documentation on how to export the client state.

Last updated

Was this helpful?