Enrollment

Enrollment is the process of registering a new user by connecting their facial biometrics to a Keyless 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, through the Keyless Web SDK JS libraries described below.

  • IDV Bridge SaaSarrow-up-right, through the Keyless Authentication Service

On this page we'll explain how to perform only interactive live enrollment on the front-end.

Note Keyless also supports component interoperabilityarrow-up-right, so that users could also be enrolled from IDV Bridge OnPremise or the Mobile SDK and still be authenticated via the Web SDK JS at a later date.

Prerequisites

Make sure you have fulfilled the prerequisites listed in the Prerequisites page before going further.

Headless Integration

The @keyless/sdk-web library lets you integrate the Keyless Web SDK however you want in terms of UI/UX, let's see a basic example of enrollment:

import {
  addKeylessEventListeners,
  createKeylessEnroll,
  createKeylessMediaStream,
  getKeylessCameraPermissionState,
  getKeylessVideoMediaDevices,
  getLastKeylessFrameTriggeredBiometricFilters,
  importKeylessWebAssemblyModuleOrThrow,
  isKeylessVideoMediaStreamAvailable,
  KeylessError,
  openKeylessWebSocketConnection,
  reduceKeylessBiometricFiltersToTriggered,
  removeKeylessEventListeners
} from '@keyless/sdk-web'

function requestTransactionJwtVerification(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 `code` 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 does not fire for failed attempts, only successful ones.
 */
function onKeylessFinished(sym, message) {
  /**
   * Removing event listeners is advised on terminal events since
   * no more than one attempt is allowed per enrollment symbol.
   */
  removeKeylessEventListeners(sym)

  /**
   * The `transactionJwt` 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(message.transactionJwt)
}

/**
 * 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.
 */
function onKeylessFrameResults(sym, message) {
  let filters

  /**
   * Returns an array of biometric filters that were triggered in the last frame.
   * If no biometric filters were triggered, an empty array is returned.
   */
  filters = reduceKeylessBiometricFiltersToTriggered(message.filters)

  /**
   * Optionally, this function can be used to retrieve the filters that were triggered
   * in the last frame.
   *
   * This can be useful if you need to access the last frame's triggered filters outside
   * of the frame results event.
   *
   * If this function is used then this event is useful for requesting an update to the UI.
   */
  filters = getLastKeylessFrameTriggeredBiometricFilters(sym)
}

async function ensureCameraOperativity() {
  let devices, state

  devices = await getKeylessVideoMediaDevices()

  /**
   * If the error is MEDIA_DEVICES_NO_VIDEO_INPUTS, it means that
   * the user does not have any camera available.
   */
  if (devices instanceof Error && devices.message === KeylessError.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 === KeylessError.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 enroll, options, stream, open

  /**
   * 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()

  /**
   * 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: 'finished', callback: (message) => onKeylessFinished(enroll, message) },
    { name: 'frame-results', callback: (message) => onKeylessFrameResults(enroll, message) }
  ])

  options = {
    customer: { name: 'CUSTOMER_NAME' },
    key: { id: 'IMAGE_ENCRYPTION_KEY_ID', value: 'IMAGE_ENCRYPTION_PUBLIC_KEY' },
    transaction: {
      data: 'DATA_FROM_CUSTOMER_SERVER_TO_BE_SIGNED'
    },
    username: 'USERNAME',
    ws: { url: 'KEYLESS_AUTHENTICATION_SERVICE_URL' }
  }

  /**
   * 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)

  /**
   * Open a WebSocket connection to the Keyless Authentication Service.
   * This connection will be used to process video frames and receive enrollment results.
   */
  open = await openKeylessWebSocketConnection(enroll, options)
  if (open instanceof Error) return handleOpenKeylessWebSocketConnectionError(open)
}

importKeylessWebAssemblyModuleOrThrow()
  .then(() =>
    ensureCameraOperativity()
      .then(() => enrollWithKeyless())
      .catch(handleCameraOperativityError)
  )
  .catch(handleImportKeylessWebAssemblyModuleError)

Web Component Integration

Now that we have explained how to perform enrollment in a headless way with the @keyless/sdk-web package, here is an example of doing so with common setups:

Last updated

Was this helpful?