Authentication is the biometric equivalent of "signing-in". During authentication Keyless compares the user's facial biometrics with the ones computed during .
If the biometrics match, Keyless authenticates the user.
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 authentication:
import { KeylessAuth } from '@keyless/sdk-web'
const auth = new KeylessAuth()
// in order to perform an authentication you need to pass a liveness check,
// this means that you need to have multiple natural different frames of the face,
// they should be captured from a live camera feed like a webcam
const frames = ['base16-frame-1', 'base16-frame-2', '...']
auth.on('begin-stream', async () => {
await auth.collectFrame(frames[0])
await auth.sendFrame()
// remove used frame
frames.shift()
})
auth.on('error', (event) => {
// will log the error code
console.error(event.message)
})
auth.on('frame-results', () => {
if (frames.length <= 0) {
throw new Error('No more frames')
}
// some time needs to pass between one frame and the other,
// this is handled gracefully in our web components by having a framerate of 4fps
setTimeout(async () => {
await auth.collectFrame(frames[0])
await auth.sendFrame()
// remove used frame
frames.shift()
}, 200)
})
auth.on('finished', (event) => {
// will log details about this authentication
console.log(event.data)
})
await auth.connect({
customer: { name: 'CUSTOMER_NAME' },
key: { id: 'IMAGE_ENCRYPTION_KEY_ID', value: 'IMAGE_ENCRYPTION_PUBLIC_KEY' },
username: 'USERNAME',
ws: { url: 'KEYLESS_AUTHENTICATION_SERVICE_URL' }
})
In this example we handle all the necessary events to perform an authentication with a continous stream of face frames, and if it fails the error code will be logged to the console.
Headless Web Integration
In the previous section we showed how to integrate @keyless/sdk-web in the simplest way without taking into account how to capture the frames from a webcam. Below we now expose a few utilities from the library that will simplify the integration, let's see them in action:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Auth</title>
</head>
<body>
<video autoplay></video>
<script src="./node_modules/@keyless/sdk-web/index.iife.js"></script>
<script></script>
<script>
let auth, video, stream, collectFrameIntervalID
const {
DEFAULT_KEYLESS_MEDIA_TRACK_FRAME_RATE,
getVideoMediaStream,
KeylessAuth
} = window.Keyless
auth = new KeylessAuth()
video = document.querySelector('video')
auth.on('begin-stream', async () => {
// will wait for at least a frame to be available
await auth.waitForFrames()
// will send a frame
await auth.sendFrame()
})
auth.on('error', async (error) => {
// will log the error code
console.error(error.message)
// will pause the video, stop the stream and clear the collect frame interval
video.pause()
})
auth.on('frame-results', async (event) => {
// will log the frame results
console.log(event.data)
// will wait for at least a frame to be available
await auth.waitForFrames()
// will send a frame
await auth.sendFrame()
})
auth.on('finished', async (event) => {
// will log details about this authentication
console.log(event.data)
// will pause the video, stop the stream and clear the collect frame interval
video.pause()
})
video.addEventListener('play', async () => {
// in case play fires multiple times in a row, we clear the interval
clearInterval(collectFrameIntervalID)
collectFrameIntervalID = setInterval(async () => {
// will collect a frame from the video or stream and store it in memory,
// this ensures a constant capture rate not affected by connection speed
await auth.collectFrame(video, stream)
}, 1000 / DEFAULT_KEYLESS_MEDIA_TRACK_FRAME_RATE)
auth.connect({
customer: { name: 'CUSTOMER_NAME' },
key: { id: 'IMAGE_ENCRYPTION_KEY_ID', value: 'IMAGE_ENCRYPTION_PUBLIC_KEY' },
username: 'USERNAME',
ws: { url: 'KEYLESS_AUTHENTICATION_SERVICE_URL' }
})
})
video.addEventListener('pause', async () => {
for (let track of stream.getTracks()) {
try {
track.stop()
} catch (e) {}
}
clearInterval(collectFrameIntervalID)
})
</script>
<script>
getVideoMediaStream().then((stream) => {
if (stream instanceof Error) {
// will log the error message
return console.error(stream.message)
}
video.srcObject = stream
})
</script>
</body>
</html>
This snippet will start a video media stream which will then be played through the video element. Once the video starts playing an interval is set which collects the frames from the video or stream based on browser compatibility with our capture algorithms.
These frames are encrypted and sent to the server when requested in the begin-stream and frame-results events, the flow then goes on as normal with authentication either succeeding or failing.
Web Component Integration
Previously we explained how to perform authentication in a headless way with the @keyless/sdk-web package, now we'll provide an example of doing so with raw HTML syntax.
That's it, everything is handled by the @keyless/sdk-web-components package, especially the more complex parts of capturing camera frames and sending them at a proper framerate to the Authentication Service.