PingOne Recognize

Enrollment

Enrollment is the process of registering a new user by connecting their facial biometrics to a PingOne Recognize account. During this process, a full and unobstructed view of the user’s face is required.

Enrollment with Web SDK can happen in two ways:

  • Live enrollment using the PingOne Recognize Web SDK JavaScript libraries.

  • IDV Bridge SaaS using the PingOne Recognize Authentication Service

This page explains how to perform interactive live enrollment on the front-end.

PingOne Recognize also supports component interoperability, so that users can be enrolled using IDV Bridge (on-premise) or the Mobile SDK. Authentication can be performed later using JavaScript and the Web SDK.

Before you begin

Review the prerequisites requirements before continuing.

Headless integration

The @keyless/sdk-web library lets you integrate the PingOne Recognize Web SDK without using a user interface. Here’s a simple enrollment:

Details
import {
  addKeylessEventListeners,
  createKeylessEnroll,
  createKeylessMediaStream,
  getKeylessCameraPermissionState,
  getKeylessVideoMediaDevices,
  getLastKeylessVideoFrameQuality,
  importKeylessWebAssemblyModuleOrThrow,
  isKeylessVideoMediaStreamAvailable,
  KeylessErrorCode,
  removeKeylessEventListeners,
  setKeylessDatadogOptions
} from '@keyless/sdk-web'
setKeylessDatadogOptions({ disable: true })
function requestKeylessJwtVerification(jwt) {}
function requestUserCameraPermission() {}
function handleCameraOperativityError(error) {}
function handleImportKeylessWebAssemblyModuleError(error) {}
function handleCreateKeylessMediaStreamError(error) {}
function handleOpenKeylessWebSocketConnectionError(error) {}
/**
 * This event is fired when an error occurs during the enrollment process.
 * The error object contains a `message` property that indicates the type of error.
 */
function onKeylessError(sym, error) {
  /**
   * Removing event listeners is advised on terminal events since
   * no more than one attempt is allowed per enrollment symbol.
   */
  removeKeylessEventListeners(sym)
  // will log the error code
  console.error(error.message)
}
/**
 * This event is fired when the enrollment process is complete.
 * It doesn't fire for failed attempts, only successful ones.
 */
function onKeylessSuccess(sym, message) {
  /**
   * Removing event listeners is advised on terminal events since
   * no more than one attempt is allowed per enrollment symbol.
   */
  removeKeylessEventListeners(sym)
  /**
   * The `jwt` is a JSON Web Token (JWT) that contains information
   * about the enrollment transaction.
   *
   * This token is signed by the Keyless Enrollment Service and can be used
   * to verify the authenticity of the transaction.
   *
   * This operation is strictly backend-to-backend and should never be performed
   * in client-side code.
   */
  requestKeylessJwtVerification(message.jwt)
}
/**
 * This event is useful for providing real-time feedback to users during
 * the enrollment process, such as prompting them to adjust their position
 * or lighting conditions to improve biometric recognition.
 *
 * The difference with "onKeylessFrameResults" is that this is from filters
 * running on the client.
 */
function onKeylessVideoFrameQuality(sym, event) {
  /**
   * Will log an array of filters that were triggered in this frame.
   * If no biometric filters were triggered, an empty array is returned.
   */
  console.log(event.filters)
  /**
   * Optionally, this function can be used to retrieve the quality of the last
   * video frame.
   *
   * This can be useful if you need to access the last video frame quality outside
   * of the video frame quality event.
   *
   * If this function is used then this event is useful for requesting an update to the UI.
   */
  console.log(getLastKeylessVideoFrameQuality(sym))
}
async function ensureCameraOperativity() {
  let devices, state
  devices = await getKeylessVideoMediaDevices()
  /**
   * If the error is MEDIA_DEVICES_NO_VIDEO_INPUTS, it means that
   * the user's device doesn't have a camera.
   */
  if (devices instanceof Error && devices.message === KeylessErrorCode.MEDIA_DEVICES_NO_VIDEO_INPUTS) throw devices
  state = await getKeylessCameraPermissionState()
  /**
   * If the camera permission state is not 'granted', request
   * the user to grant camera access.
   */
  if (state !== 'granted') {
    /**
     * Ideally this function should take the user to a UI prompt
     * where they can grant camera access to the website.
     *
     * The easiest way to trigger the browser's camera permission prompt
     * is to call isKeylessVideoMediaStreamAvailable(), which will return
     * a boolean indicating whether the user granted camera access or not.
     */
    requestUserCameraPermission()
    throw new Error('camera permission state is not granted')
  }
  devices = await getKeylessVideoMediaDevices()
  /**
   * If the error is MEDIA_DEVICES_EMPTY_VIDEO_INPUT_LABEL, it means that
   * even though the user has granted camera access, the browser requires
   * the user to start a video stream to be able to read the camera labels.
   *
   * In this case, we perform a throwaway getUserMedia() request with
   * isKeylessVideoMediaStreamAvailable() to start a video stream
   * to be able to read the camera labels.
   */
  if (devices instanceof Error && devices.message === KeylessErrorCode.MEDIA_DEVICES_EMPTY_VIDEO_INPUT_LABEL) {
    let available
    available = await isKeylessVideoMediaStreamAvailable()
    if (!available) throw new Error('video media stream is not available')
    devices = await getKeylessVideoMediaDevices()
  }
  /**
   * If we still have an error, throw an error to indicate that
   * the media devices still could not be read correctly.
   */
  if (devices instanceof Error) throw devices
}
async function enrollWithKeyless() {
  let options, enroll, stream, open
  options = {
    authorization: {
      token: 'USER_AUTHORIZATION_FROM_CUSTOMER'
    },
    customer: { name: 'CUSTOMER_NAME' },
    transaction: {
      data: 'DATA_FROM_CUSTOMER_SERVER_TO_BE_SIGNED'
    },
    service: { url: 'KEYLESS_AUTHENTICATION_SERVICE_URL' },
    username: 'USERNAME'
  }
  /**
   * Create a Keyless enrollment symbol.
   *
   * This symbol must be kept in memory for the duration of the enrollment process.
   * To perform multiple enrollments, a new symbol must be created for each enrollment.
   */
  enroll = createKeylessEnroll(options)
  /**
   * Add event listeners through the Keyless enrollment symbol.
   * These listeners will handle events during the enrollment process.
   */
  addKeylessEventListeners(enroll, [
    { name: 'error', callback: (error) => onKeylessError(enroll, error) },
    { name: 'success', callback: (message) => onKeylessSuccess(enroll, message) },
    { name: 'video-frame-quality', callback: (event) => onKeylessVideoFrameQuality(enroll, event) }
  ])
  /**
   * Create a media stream from the user's video input media device.
   * This stream will be used to capture video frames for biometric analysis.
   *
   * Note: The user must grant permission to access the media device.
   */
  stream = await createKeylessMediaStream()
  if (stream instanceof Error) return handleCreateKeylessMediaStreamError(stream)
}
importKeylessWebAssemblyModuleOrThrow()
  .then(() =>
    ensureCameraOperativity()
      .then(() => enrollWithKeyless())
      .catch(handleCameraOperativityError)
  )
  .catch(handleImportKeylessWebAssemblyModuleError)

Web component integration

This section shows how to integrate the PingOne Recognize Web SDK directly into a web application.

  • React

  • Vue

  • Embedded

import '@keyless/sdk-web-components'
export function KeylessEnroll() {
  function requestTransactionJwtVerification(jwt) {}
  onError = (event) => {
    // will log the error code
    console.log(event.message)
  }
  onSuccess = (event) => {
    /**
     * The `jwt` is a JSON Web Token (JWT) that contains information
     * about the enrollment transaction.
     *
     * This token is signed by the Keyless Authentication Service and can be used
     * to verify the authenticity of the transaction.
     *
     * This operation is strictly backend-to-backend and should never be performed
     * in client-side code.
     */
    requestTransactionJwtVerification(event.detail.jwt)
  }
  return (
    <kl-enroll
      authorization-token='USER_AUTHORIZATION_FROM_CUSTOMER'
      customer='CUSTOMER_NAME'
      enable-camera-instructions
      lang='en'
      onerror={onError}
      onsuccess={onSuccess}
      size='375'
      theme='light'
      transaction-data='DATA_FROM_CUSTOMER_SERVER_TO_BE_SIGNED'
      username='USERNAME'
      ws-url='KEYLESS_AUTHENTICATION_SERVICE_URL'
    />
  )
}
<script setup>
import '@keyless/sdk-web-components'
function requestTransactionJwtVerification(jwt) {}
function onError(event) {
// will log the error code
console.log(event.message)
}
function onSuccess(event) {
/**
* The `jwt` is a JSON Web Token (JWT) that contains information
* about the enrollment transaction.
*
* This token is signed by the Keyless Authentication Service and can be used
* to verify the authenticity of the transaction.
*
* This operation is strictly backend-to-backend and should never be performed
* in client-side code.
*/
requestTransactionJwtVerification(event.detail.jwt)
}
</script>
<template>
  <kl-enroll
    customer="CUSTOMER_NAME"
    enable-camera-instructions
    @error="onError"
    @success="onSuccess"
    lang="en"
    size="375"
    theme="light"
    transaction-data='DATA_FROM_CUSTOMER_SERVER_TO_BE_SIGNED'
    username="USERNAME"
    ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
  />
</template>
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Enroll</title>
    <style>
      * {
        box-sizing: border-box;
      }
      body {
        align-items: center;
        display: flex;
        justify-content: center;
        margin: 0;
        min-height: 100vh;
        padding: 8px;
      }
      kl-enroll {
        border: 1px solid lightgray;
      }
    </style>
  </head>
  <body>
    <kl-enroll
      customer="CUSTOMER_NAME"
      enable-camera-instructions
      lang="en"
      size="375"
      theme="light"
      transaction-data='DATA_FROM_CUSTOMER_SERVER_TO_BE_SIGNED'
      username="USERNAME"
      ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
    ></kl-enroll>
    <script src="./node_modules/@keyless/sdk-web-components/index.js" type="module"></script>
    <script>
      const enroll = document.querySelector('kl-enroll')
      function requestTransactionJwtVerification(jwt)
      enroll.addEventListener('error', (event) => {
        // will log the error code
        console.log(event.message)
      })
      enroll.addEventListener('success', (event) => {
        /**
         * The `jwt` is a JSON Web Token (JWT) that contains information
         * about the enrollment transaction.
         *
         * This token is signed by the Keyless Authentication Service and can be used
         * to verify the authenticity of the transaction.
         *
         * This operation is strictly backend-to-backend and should never be performed
         * in client-side code.
         */
        requestTransactionJwtVerification(event.detail.jwt)
      })
    </script>
  </body>
</html>