Error Handling

The KeylessAuth and KeylessEnroll classes emit 2 kind of error events:

  • The all purpose "error" event

  • The WebSocket specific "ws-error" event

Here's an example of how to handle these errors:

import { KeylessAuth } from '@keyless/sdk-web'

const auth = new KeylessAuth()

auth.on('error', (error) => {
  // handle error
})

auth.on('ws-error', (event, error) => {
  // handle error
  // the event argument is the one emitted by the native WebSocket instance
})

// connecting without a WebSocket URL and username will of course emit an error
auth.connect()

Of course this example also applies to the KeylessEnroll class, each error contains the error code in the message, this is the enum which is also exported by the @keyless/sdk-web package:

enum KeylessError {
  WEB_SOCKET_UNKNOWN = 0,
  WEB_SOCKET_OPEN,
  WEB_SOCKET_UNEXPECTED_CLOSE,
  WEB_SOCKET_TIMEOUT,

  KEY_UNSET = 100,
  KEY_ID_UNSET,
  USERNAME_UNSET,
  ERROR,
  LIVENESS_FAILED,
  USERNAME_LOCKED_OUT,

  IMAGE_STREAM_FAILED_TO_DRAW_FRAME = 200,

  ENROLL_USER_EXISTS = 300,

  AUTH_USER_DOES_NOT_EXIST = 400,
  AUTH_RECOGNITION_FAILED
}

Errors Explanation

Error

Triggers

WEB_SOCKET_UNKNOWN

Forwarded from the native WebSocket instance “error” event.

WEB_SOCKET_OPEN

Triggered by creating a new native WebSocket instance, could also be caused by a timeout during the connection to the WebSocket.

WEB_SOCKET_UNEXPECTED_CLOSE

Triggered after the “ws-close” event, it is triggered when the WebSocket closes in a state where it is not supposed to be closing.

  • BeginImageStream

  • ImageAccepted

  • StopSending

This is the list of events which are considered as unexpected if the WebSocket closes.

WEB_SOCKET_TIMEOUT

Triggered after the WebSocket connection is open for the specified amount of time, by default this timeout is 1 minute.

KEY_UNSET

Triggered when the key.value has not been provided to the KeylessAuth connect options.

KEY_ID_UNSET

Triggered when the key.id has not been provided to the KeylessAuth connect options.

USERNAME_UNSET

Triggered when the username has not been provided to the KeylessAuth connect options.

ERROR

Triggered by the WebSocket Error incoming message, the full error can be found inside the event argument of the "error" event.

LIVENESS_FAILED

Triggered by the WebSocket LivenessFailed incoming message, as the name implies it means the frames sent by the user did not pass our liveness.

USERNAME_LOCKED_OUT

Triggered when the lockout policy is enabled and the user has failed the specified amount of times in a row the authentication or enrollment.

The user will be locked out for the specified amount of time, which by default is 5 minutes.

The user will be locked out after the specified amount of times in a row, which by default is 5 attempts.

IMAGE_STREAM_FAILED_TO_DRAW_FRAME

Triggered when it is impossibile to draw the video stream frame, this could happen for various reasons:

  • The user did not grant camera permissions, so there is no stream to draw from.

  • The browser does not support Canvas or ImageCapture APIs, should be unlikely.

  • The video stream is absent or undefined for unknown reasons.

ENROLL_USER_EXISTS

Triggered by the WebSocket UserDoesExist incoming message, as the name implies it means that the user is trying to enroll but they are already enrolled with that username.

AUTH_USER_DOES_NOT_EXIST

Triggered by the WebSocket UserDoesNotExist incoming message, as the name implies it means that the user is trying to authenticate with a username that has not been enrolled first.

AUTH_RECOGNITION_FAILED

Triggered by the WebSocket FinishedAuthentication incoming message when the result is FAILURE, as the name implies it means that the user face did not match with the face that was enrolled.

Web Components Error Handling

Handling errors in the web components is very similar to how they are handled in the @keyless/sdk-web library, so check out that part first because those errors are inherited by the web components: Errors Explanation

Of course the web components naturally have more complexity, let’s go in order with some examples on how to handle all the errors emitted by the web components.

Here's an example of handling the "error" event:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Auth</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        align-items: center;
        display: flex;
        justify-content: center;
        margin: 0;
        min-height: 100vh;
        padding: 8px;
      }

      kl-auth {
        border: 1px solid lightgray;
      }
    </style>
  </head>
  <body>
    <kl-auth
      enable-camera-instructions
      key="IMAGE_ENCRYPTION_PUBLIC_KEY"
      key-id="IMAGE_ENCRYPTION_KEY_ID"
      lang="en"
      size="375"
      theme="light"
      username="USERNAME"
      ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
    ></kl-auth>
    <script src="@keyless/sdk-web-components/elements/auth/auth-element.iife.js"></script>
    <script>
      const auth = document.querySelector('kl-auth')

      auth.addEventListener('error', (event) => {
        // will print the error code
        console.error(event.message)
      })
    </script>
  </body>
</html>

We extend the native ErrorEvent browser implementation here, so you can find the error code quite easily inside the event message.

The error code can be anything from the @keyless/sdk-web KeylessError enum, with on top the errors of the web components, here’s their enums:

enum KeylessCameraError {
  NO_MEDIA_DEVICES = 0,
  NO_MEDIA_STREAM
}

enum KeylessComponentsError {
  CAMERA_CHECK_FAILED = 0
}

Web Components Errors Explanation

Error

Triggers

NO_MEDIA_DEVICES

Triggered when there are no media devices capable of video output, this means that the user either does not have a camera or they did not grant the camera permissions.

NO_MEDIA_STREAM

Triggered when there is no media stream available, explanation is the same as NO_MEDIA_DEVICES error.

CAMERA_CHECK_FAILED

Triggered when camera checks are enabled and the user failed to meet the requested conditions.

The "ws-error" event is emitted when the WebSocket native instance emits an error, here's an example:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Auth</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        align-items: center;
        display: flex;
        justify-content: center;
        margin: 0;
        min-height: 100vh;
        padding: 8px;
      }

      kl-auth {
        border: 1px solid lightgray;
      }
    </style>
  </head>
  <body>
    <kl-auth
      enable-camera-instructions
      key="IMAGE_ENCRYPTION_PUBLIC_KEY"
      key-id="IMAGE_ENCRYPTION_KEY_ID"
      lang="en"
      size="375"
      theme="light"
      username="USERNAME"
      ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
    ></kl-auth>
    <script src="@keyless/sdk-web-components/elements/auth/auth-element.iife.js"></script>
    <script>
      const auth = document.querySelector('kl-auth')

      auth.addEventListener('ws-error', (event) => {
        // will print the native WebSocket error event
        console.error(event.detail.event)
        
        // will print the error code
        console.error(event.detail.error.message)
      })
    </script>
  </body>
</html>

We use a custom error here called WebSocketErrorEvent which contains both the native WebSocket event and the error generated by us with a predictable error code.

The "finished" event is a bit special, meaning that in the authentication flow the integrator must check whether the authentication result was successful or not, here's an example:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Auth</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        align-items: center;
        display: flex;
        justify-content: center;
        margin: 0;
        min-height: 100vh;
        padding: 8px;
      }

      kl-auth {
        border: 1px solid lightgray;
      }
    </style>
  </head>
  <body>
    <kl-auth
      enable-camera-instructions
      key="IMAGE_ENCRYPTION_PUBLIC_KEY"
      key-id="IMAGE_ENCRYPTION_KEY_ID"
      lang="en"
      size="375"
      theme="light"
      username="USERNAME"
      ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
    ></kl-auth>
    <script src="@keyless/sdk-web-components/elements/auth/auth-element.iife.js"></script>
    <script>
      const auth = document.querySelector('kl-auth')

      auth.addEventListener('finished', (event, error) => {
        if (error) {
          return console.error('authentication failed', error)
        }

        if (event.detail.result === 'SUCCESS') {
          return console.info('authentication successful')
        }

        console.error('authentication failed', event.detail)
      })
    </script>
  </body>
</html>

The "liveness-failed" event is emitted when the liveness fails, might be useful to handle in order to give a more accurate error feedback to the user.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Auth</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        align-items: center;
        display: flex;
        justify-content: center;
        margin: 0;
        min-height: 100vh;
        padding: 8px;
      }

      kl-auth {
        border: 1px solid lightgray;
      }
    </style>
  </head>
  <body>
    <kl-auth
      enable-camera-instructions
      key="IMAGE_ENCRYPTION_PUBLIC_KEY"
      key-id="IMAGE_ENCRYPTION_KEY_ID"
      lang="en"
      size="375"
      theme="light"
      username="USERNAME"
      ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
    ></kl-auth>
    <script src="@keyless/sdk-web-components/elements/auth/auth-element.iife.js"></script>
    <script>
      const auth = document.querySelector('kl-auth')

      auth.addEventListener('liveness-failed', (event) => {
        // handle user failing liveness
      })
    </script>
  </body>
</html>

The "user-does-not-exist" event is only emitted by the authentication web components, it’s a straight failure due to the user not existing on the server.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Auth</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        align-items: center;
        display: flex;
        justify-content: center;
        margin: 0;
        min-height: 100vh;
        padding: 8px;
      }

      kl-auth {
        border: 1px solid lightgray;
      }
    </style>
  </head>
  <body>
    <kl-auth
      enable-camera-instructions
      key="IMAGE_ENCRYPTION_PUBLIC_KEY"
      key-id="IMAGE_ENCRYPTION_KEY_ID"
      lang="en"
      size="375"
      theme="light"
      username="USERNAME"
      ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
    ></kl-auth>
    <script src="@keyless/sdk-web-components/elements/auth/auth-element.iife.js"></script>
    <script>
      const auth = document.querySelector('kl-auth')

      auth.addEventListener('user-does-not-exist', (event) => {
        // handle feedback to the user about his username being wrong or
        // his account not potentially existing
      })
    </script>
  </body>
</html>

The "user-exists" event is only emitted by the enrollment components, if a user attempts to enroll with a username that is already enrolled on the server, this event will be emitted.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Auth</title>
    <style>
      * {
        box-sizing: border-box;
      }

      body {
        align-items: center;
        display: flex;
        justify-content: center;
        margin: 0;
        min-height: 100vh;
        padding: 8px;
      }

      kl-auth {
        border: 1px solid lightgray;
      }
    </style>
  </head>
  <body>
    <kl-auth
      enable-camera-instructions
      key="IMAGE_ENCRYPTION_PUBLIC_KEY"
      key-id="IMAGE_ENCRYPTION_KEY_ID"
      lang="en"
      size="375"
      theme="light"
      username="USERNAME"
      ws-url="KEYLESS_AUTHENTICATION_SERVICE_URL"
    ></kl-auth>
    <script src="@keyless/sdk-web-components/elements/auth/auth-element.iife.js"></script>
    <script>
      const auth = document.querySelector('kl-auth')

      auth.addEventListener('user-exists', (event) => {
        // handle feedback to the user about the username being already enrolled
      })
    </script>
  </body>
</html>

Last updated