@quadible/web-sdk
Version:
The web sdk for Quadible's behavioral authentication service.
157 lines • 5.77 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const eventemitter2_1 = require("eventemitter2");
const FormData_1 = require("./FormData");
const sleep_1 = __importDefault(require("./sleep"));
class WebcamSession extends eventemitter2_1.EventEmitter2 {
api;
worker;
isStarted = false;
isAuthenticatingFrame = false;
isFaceAuthenticated = false;
lastAuthResult = null;
nextVideoFrame;
consecutiveNoFaceCount = 0;
consecutiveNoFaceThreshold = 2;
failedFaceAuthTries = 0;
failedFaceAuthRetryIntervalsMs = [0, 1e3, 1e3, 2e3, 2e3, 5e3, 5e3, 10e3];
lastTrackingUpdate = Date.now();
predictionHandler = ((newFrame) => {
this.nextVideoFrame = newFrame;
if (!this.isAuthenticatingFrame) {
this.processNextFrame();
}
}).bind(this);
constructor(api, worker) {
super();
this.api = api;
this.worker = worker;
}
async start() {
this.isStarted = true;
await this.worker.init();
this.worker.on('predictions', this.predictionHandler);
}
async stop() {
this.isStarted = false;
this.worker.off('predictions', this.predictionHandler);
}
async processNextFrame() {
try {
this.isAuthenticatingFrame = true;
while (true) {
const frame = this.nextVideoFrame;
this.nextVideoFrame = undefined;
if (!frame) {
break;
}
await this.processFrame(frame);
}
}
finally {
this.isAuthenticatingFrame = false;
}
}
async processFrame(frame) {
let hasFaces = Boolean(frame.predictions.length);
if (hasFaces) {
this.consecutiveNoFaceCount = 0;
}
else {
this.consecutiveNoFaceCount++;
if (this.consecutiveNoFaceCount <= this.consecutiveNoFaceThreshold) {
hasFaces = true;
}
}
if (!hasFaces) {
// If we lost face and lockScreenOnFaceLost = true, mark as unauthorized
this.sendTrackingEndIfNeeded();
this.setFaceAuthStatus(false);
}
else if (!this.isFaceAuthenticated) {
// If just gained or regained a face, authenticate
await (0, sleep_1.default)(this.getAuthRetryInterval());
const nextFrame = this.nextVideoFrame || frame;
this.lastAuthResult = await this.authenticateFrame(nextFrame.imageBase64);
this.setFaceAuthStatus(this.lastAuthResult.authenticated);
this.incrementOrResetFaceAuthTries();
}
else {
// If we found a face and we were already authenticated, send tracking
this.sendTrackingIfNeeded();
}
}
incrementOrResetFaceAuthTries() {
if (this.isFaceAuthenticated) {
this.failedFaceAuthTries = 0;
}
else {
this.failedFaceAuthTries++;
}
}
getAuthRetryInterval() {
let interval = this.failedFaceAuthRetryIntervalsMs[this.failedFaceAuthTries];
if (isNaN(interval)) {
interval = this.failedFaceAuthRetryIntervalsMs[this.failedFaceAuthRetryIntervalsMs.length - 1];
}
return interval;
}
async authenticateFrame(base64DataUrl) {
const result = await fetch(base64DataUrl);
const formData = (0, FormData_1.createFormData)();
const fileBlob = await result.blob();
formData.append('file', fileBlob);
const response = await (await this.api.postFile('/v1/face/authenticate', formData)).json();
this.emit("face-auth-response" /* WebcamSessionEvent.FaceAuthResponse */, response);
return response;
}
setFaceAuthStatus(status) {
const oldStatus = this.isFaceAuthenticated;
this.isFaceAuthenticated = status;
if (status && !oldStatus) {
this.emit("face-auth" /* WebcamSessionEvent.FaceAuthenticated */);
}
if (!status && oldStatus) {
this.emit("face-auth-lost" /* WebcamSessionEvent.FaceAuthenticationLost */);
}
if (status !== oldStatus) {
this.emit("face-auth-change" /* WebcamSessionEvent.FaceAuthenticationStatusChanged */, {
status,
lastAuthResult: this.lastAuthResult
});
}
}
sendTrackingEndIfNeeded() {
if (this.isFaceAuthenticated) {
this.api.pushEvents([{
kind: "facetracking" /* EventKind.FaceTracking */,
timestamp: Date.now(),
payload: {
trackingId: this.lastAuthResult.trackingId,
isTrackingActive: false
}
}]);
this.emit("tracking-end-sent" /* WebcamSessionEvent.TrackingEndSent */);
}
}
sendTrackingIfNeeded() {
const now = Date.now();
if (this.lastTrackingUpdate + 2e3 < now) {
this.lastTrackingUpdate = now;
this.api.pushEvents([{
kind: "facetracking" /* EventKind.FaceTracking */,
timestamp: Date.now(),
payload: {
trackingId: this.lastAuthResult.trackingId,
isTrackingActive: true
}
}]);
this.emit("tracking-sent" /* WebcamSessionEvent.TrackingSent */);
}
}
}
exports.default = WebcamSession;
//# sourceMappingURL=WebcamSession.js.map