@aws-amplify/ui-react-liveness 3.0.12 → 3.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/FaceLivenessDetector/LivenessCheck/LivenessCameraModule.mjs +3 -10
- package/dist/esm/components/FaceLivenessDetector/service/machine/index.mjs +144 -158
- package/dist/esm/components/FaceLivenessDetector/service/utils/CustomWebSocketFetchHandler.mjs +3 -4
- package/dist/esm/components/FaceLivenessDetector/service/utils/liveness.mjs +62 -67
- package/dist/esm/components/FaceLivenessDetector/service/utils/streamProvider.mjs +6 -5
- package/dist/esm/components/FaceLivenessDetector/service/utils/videoRecorder.mjs +1 -2
- package/dist/esm/components/FaceLivenessDetector/types/classNames.mjs +0 -5
- package/dist/esm/version.mjs +1 -1
- package/dist/index.js +218 -250
- package/dist/styles.css +6 -2
- package/dist/types/components/FaceLivenessDetector/hooks/useLivenessActor.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/service/machine/index.d.ts +3 -4
- package/dist/types/components/FaceLivenessDetector/service/types/machine.d.ts +17 -18
- package/dist/types/components/FaceLivenessDetector/service/utils/liveness.d.ts +7 -9
- package/dist/types/components/FaceLivenessDetector/service/utils/streamProvider.d.ts +3 -10
- package/dist/types/components/FaceLivenessDetector/service/utils/videoRecorder.d.ts +1 -7
- package/dist/types/components/FaceLivenessDetector/shared/index.d.ts +0 -3
- package/dist/types/components/FaceLivenessDetector/types/classNames.d.ts +0 -5
- package/dist/types/version.d.ts +1 -1
- package/package.json +3 -3
- package/dist/types/components/FaceLivenessDetector/shared/GoodFitIllustration.d.ts +0 -7
- package/dist/types/components/FaceLivenessDetector/shared/StartScreenFigure.d.ts +0 -8
- package/dist/types/components/FaceLivenessDetector/shared/TooFarIllustration.d.ts +0 -7
|
@@ -79,22 +79,15 @@ const LivenessCameraModule = (props) => {
|
|
|
79
79
|
const [mediaHeight, setMediaHeight] = useState(videoHeight);
|
|
80
80
|
const [aspectRatio, setAspectRatio] = useState(() => videoWidth && videoHeight ? videoWidth / videoHeight : 0);
|
|
81
81
|
React__default.useEffect(() => {
|
|
82
|
-
if (canvasRef &&
|
|
83
|
-
videoRef &&
|
|
84
|
-
canvasRef.current &&
|
|
85
|
-
videoRef.current &&
|
|
86
|
-
videoStream &&
|
|
87
|
-
isStartView) {
|
|
82
|
+
if (canvasRef?.current && videoRef?.current && videoStream && isStartView) {
|
|
88
83
|
drawStaticOval(canvasRef.current, videoRef.current, videoStream);
|
|
89
84
|
}
|
|
90
85
|
}, [canvasRef, videoRef, videoStream, colorMode, isStartView]);
|
|
91
86
|
React__default.useEffect(() => {
|
|
92
87
|
const updateColorModeHandler = (e) => {
|
|
93
88
|
if (e.matches &&
|
|
94
|
-
canvasRef &&
|
|
95
|
-
videoRef &&
|
|
96
|
-
canvasRef.current &&
|
|
97
|
-
videoRef.current &&
|
|
89
|
+
canvasRef?.current &&
|
|
90
|
+
videoRef?.current &&
|
|
98
91
|
videoStream &&
|
|
99
92
|
isStartView) {
|
|
100
93
|
drawStaticOval(canvasRef.current, videoRef.current, videoStream);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid';
|
|
1
2
|
import { createMachine, assign, spawn, actions } from 'xstate';
|
|
2
3
|
import { drawStaticOval, getBoundingBox, getColorsSequencesFromSessionInformation, isCameraDeviceVirtual, getFaceMatchState, isFaceDistanceBelowThreshold, estimateIllumination, getOvalDetailsFromSessionInformation, generateBboxFromLandmarks, drawLivenessOvalInCanvas, getOvalBoundingBox, getIntersectionOverUnion, getFaceMatchStateInLivenessOval, getStaticLivenessOvalDetails } from '../utils/liveness.mjs';
|
|
3
4
|
import { FaceMatchState } from '../types/liveness.mjs';
|
|
@@ -5,16 +6,68 @@ import { LivenessErrorState } from '../types/error.mjs';
|
|
|
5
6
|
import { BlazeFaceFaceDetection } from '../utils/blazefaceFaceDetection.mjs';
|
|
6
7
|
import { LivenessStreamProvider } from '../utils/streamProvider.mjs';
|
|
7
8
|
import { FreshnessColorDisplay } from '../utils/freshnessColorDisplay.mjs';
|
|
8
|
-
import { nanoid } from 'nanoid';
|
|
9
9
|
import { isServerSesssionInformationEvent, isDisconnectionEvent, isValidationExceptionEvent, isInternalServerExceptionEvent, isThrottlingExceptionEvent, isServiceQuotaExceededExceptionEvent, isInvalidSignatureRegionException } from '../utils/eventUtils.mjs';
|
|
10
10
|
import { STATIC_VIDEO_CONSTRAINTS } from '../../utils/helpers.mjs';
|
|
11
11
|
import { WS_CLOSURE_CODE } from '../utils/constants.mjs';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
const MIN_FACE_MATCH_TIME = 1000;
|
|
13
|
+
const CAMERA_ID_KEY = 'AmplifyLivenessCameraId';
|
|
15
14
|
const DEFAULT_FACE_FIT_TIMEOUT = 7000;
|
|
15
|
+
const MIN_FACE_MATCH_TIME = 1000;
|
|
16
16
|
let responseStream;
|
|
17
|
-
const
|
|
17
|
+
const responseStreamActor = async (callback) => {
|
|
18
|
+
try {
|
|
19
|
+
const stream = await responseStream;
|
|
20
|
+
for await (const event of stream) {
|
|
21
|
+
if (isServerSesssionInformationEvent(event)) {
|
|
22
|
+
callback({
|
|
23
|
+
type: 'SET_SESSION_INFO',
|
|
24
|
+
data: {
|
|
25
|
+
sessionInfo: event.ServerSessionInformationEvent.SessionInformation,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
else if (isDisconnectionEvent(event)) {
|
|
30
|
+
callback({ type: 'DISCONNECT_EVENT' });
|
|
31
|
+
}
|
|
32
|
+
else if (isValidationExceptionEvent(event)) {
|
|
33
|
+
callback({
|
|
34
|
+
type: 'SERVER_ERROR',
|
|
35
|
+
data: { error: { ...event.ValidationException } },
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else if (isInternalServerExceptionEvent(event)) {
|
|
39
|
+
callback({
|
|
40
|
+
type: 'SERVER_ERROR',
|
|
41
|
+
data: { error: { ...event.InternalServerException } },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else if (isThrottlingExceptionEvent(event)) {
|
|
45
|
+
callback({
|
|
46
|
+
type: 'SERVER_ERROR',
|
|
47
|
+
data: { error: { ...event.ThrottlingException } },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
else if (isServiceQuotaExceededExceptionEvent(event)) {
|
|
51
|
+
callback({
|
|
52
|
+
type: 'SERVER_ERROR',
|
|
53
|
+
data: { error: { ...event.ServiceQuotaExceededException } },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
let returnedError = error;
|
|
60
|
+
if (isInvalidSignatureRegionException(error)) {
|
|
61
|
+
returnedError = new Error('Invalid region in FaceLivenessDetector or credentials are scoped to the wrong region.');
|
|
62
|
+
}
|
|
63
|
+
if (returnedError instanceof Error) {
|
|
64
|
+
callback({
|
|
65
|
+
type: 'SERVER_ERROR',
|
|
66
|
+
data: { error: returnedError },
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
18
71
|
function getLastSelectedCameraId() {
|
|
19
72
|
return localStorage.getItem(CAMERA_ID_KEY);
|
|
20
73
|
}
|
|
@@ -99,12 +152,12 @@ const livenessMachine = createMachine({
|
|
|
99
152
|
},
|
|
100
153
|
states: {
|
|
101
154
|
cameraCheck: {
|
|
102
|
-
entry:
|
|
155
|
+
entry: 'resetErrorState',
|
|
103
156
|
invoke: {
|
|
104
157
|
src: 'checkVirtualCameraAndGetStream',
|
|
105
158
|
onDone: {
|
|
106
159
|
target: 'waitForDOMAndCameraDetails',
|
|
107
|
-
actions:
|
|
160
|
+
actions: 'updateVideoMediaStream',
|
|
108
161
|
},
|
|
109
162
|
onError: {
|
|
110
163
|
target: 'permissionDenied',
|
|
@@ -137,7 +190,7 @@ const livenessMachine = createMachine({
|
|
|
137
190
|
src: 'detectFace',
|
|
138
191
|
onDone: {
|
|
139
192
|
target: 'checkFaceDetectedBeforeStart',
|
|
140
|
-
actions:
|
|
193
|
+
actions: 'updateFaceMatchBeforeStartDetails',
|
|
141
194
|
},
|
|
142
195
|
},
|
|
143
196
|
},
|
|
@@ -155,7 +208,7 @@ const livenessMachine = createMachine({
|
|
|
155
208
|
src: 'detectFaceDistance',
|
|
156
209
|
onDone: {
|
|
157
210
|
target: 'checkFaceDistanceBeforeRecording',
|
|
158
|
-
actions:
|
|
211
|
+
actions: 'updateFaceDistanceBeforeRecording',
|
|
159
212
|
},
|
|
160
213
|
},
|
|
161
214
|
},
|
|
@@ -199,7 +252,7 @@ const livenessMachine = createMachine({
|
|
|
199
252
|
initial: 'ovalDrawing',
|
|
200
253
|
states: {
|
|
201
254
|
ovalDrawing: {
|
|
202
|
-
entry:
|
|
255
|
+
entry: 'sendTimeoutAfterOvalDrawingDelay',
|
|
203
256
|
invoke: {
|
|
204
257
|
src: 'detectInitialFaceAndDrawOval',
|
|
205
258
|
onDone: {
|
|
@@ -229,13 +282,13 @@ const livenessMachine = createMachine({
|
|
|
229
282
|
0: {
|
|
230
283
|
target: 'ovalMatching',
|
|
231
284
|
cond: 'hasRecordingStarted',
|
|
232
|
-
actions:
|
|
285
|
+
actions: 'updateRecordingStartTimestampMs',
|
|
233
286
|
},
|
|
234
287
|
100: { target: 'checkRecordingStarted' },
|
|
235
288
|
},
|
|
236
289
|
},
|
|
237
290
|
ovalMatching: {
|
|
238
|
-
entry:
|
|
291
|
+
entry: 'cancelOvalDrawingTimeout',
|
|
239
292
|
invoke: {
|
|
240
293
|
src: 'detectFaceAndMatchOval',
|
|
241
294
|
onDone: {
|
|
@@ -283,7 +336,7 @@ const livenessMachine = createMachine({
|
|
|
283
336
|
},
|
|
284
337
|
},
|
|
285
338
|
success: {
|
|
286
|
-
entry:
|
|
339
|
+
entry: 'stopRecording',
|
|
287
340
|
type: 'final',
|
|
288
341
|
},
|
|
289
342
|
},
|
|
@@ -336,13 +389,11 @@ const livenessMachine = createMachine({
|
|
|
336
389
|
},
|
|
337
390
|
permissionDenied: {
|
|
338
391
|
entry: 'callUserPermissionDeniedCallback',
|
|
339
|
-
on: {
|
|
340
|
-
RETRY_CAMERA_CHECK: 'cameraCheck',
|
|
341
|
-
},
|
|
392
|
+
on: { RETRY_CAMERA_CHECK: 'cameraCheck' },
|
|
342
393
|
},
|
|
343
394
|
mobileLandscapeWarning: {
|
|
344
395
|
entry: 'callMobileLandscapeWarningCallback',
|
|
345
|
-
always:
|
|
396
|
+
always: { target: 'error' },
|
|
346
397
|
},
|
|
347
398
|
timeout: {
|
|
348
399
|
entry: ['cleanUpResources', 'callUserTimeoutCallback', 'freezeStream'],
|
|
@@ -359,7 +410,7 @@ const livenessMachine = createMachine({
|
|
|
359
410
|
},
|
|
360
411
|
userCancel: {
|
|
361
412
|
entry: ['cleanUpResources', 'callUserCancelCallback', 'resetContext'],
|
|
362
|
-
always:
|
|
413
|
+
always: { target: 'cameraCheck' },
|
|
363
414
|
},
|
|
364
415
|
},
|
|
365
416
|
}, {
|
|
@@ -368,16 +419,17 @@ const livenessMachine = createMachine({
|
|
|
368
419
|
responseStreamActorRef: () => spawn(responseStreamActor),
|
|
369
420
|
}),
|
|
370
421
|
updateFailedAttempts: assign({
|
|
371
|
-
failedAttempts: (context) =>
|
|
372
|
-
return context.failedAttempts + 1;
|
|
373
|
-
},
|
|
422
|
+
failedAttempts: (context) => context.failedAttempts + 1,
|
|
374
423
|
}),
|
|
375
424
|
updateVideoMediaStream: assign({
|
|
376
425
|
videoAssociatedParams: (context, event) => ({
|
|
377
426
|
...context.videoAssociatedParams,
|
|
378
|
-
videoMediaStream: event.data
|
|
379
|
-
|
|
380
|
-
|
|
427
|
+
videoMediaStream: event.data
|
|
428
|
+
?.stream,
|
|
429
|
+
selectedDeviceId: event.data
|
|
430
|
+
?.selectedDeviceId,
|
|
431
|
+
selectableDevices: event.data
|
|
432
|
+
?.selectableDevices,
|
|
381
433
|
}),
|
|
382
434
|
}),
|
|
383
435
|
initializeFaceDetector: assign({
|
|
@@ -386,29 +438,23 @@ const livenessMachine = createMachine({
|
|
|
386
438
|
const { faceModelUrl, binaryPath } = componentProps.config;
|
|
387
439
|
const faceDetector = new BlazeFaceFaceDetection(binaryPath, faceModelUrl);
|
|
388
440
|
faceDetector.triggerModelLoading();
|
|
389
|
-
return {
|
|
390
|
-
...context.ovalAssociatedParams,
|
|
391
|
-
faceDetector,
|
|
392
|
-
};
|
|
441
|
+
return { ...context.ovalAssociatedParams, faceDetector };
|
|
393
442
|
},
|
|
394
443
|
}),
|
|
395
444
|
updateLivenessStreamProvider: assign({
|
|
396
|
-
livenessStreamProvider: (context, event) =>
|
|
397
|
-
return event.data?.livenessStreamProvider;
|
|
398
|
-
},
|
|
445
|
+
livenessStreamProvider: (context, event) => event.data?.livenessStreamProvider,
|
|
399
446
|
}),
|
|
400
447
|
setDOMAndCameraDetails: assign({
|
|
401
|
-
videoAssociatedParams: (context, event) => {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
};
|
|
408
|
-
},
|
|
448
|
+
videoAssociatedParams: (context, event) => ({
|
|
449
|
+
...context.videoAssociatedParams,
|
|
450
|
+
videoEl: event.data?.videoEl,
|
|
451
|
+
canvasEl: event.data?.canvasEl,
|
|
452
|
+
isMobile: event.data?.isMobile,
|
|
453
|
+
}),
|
|
409
454
|
freshnessColorAssociatedParams: (context, event) => ({
|
|
410
455
|
...context.freshnessColorAssociatedParams,
|
|
411
|
-
freshnessColorEl: event.data
|
|
456
|
+
freshnessColorEl: event.data
|
|
457
|
+
?.freshnessColorEl,
|
|
412
458
|
}),
|
|
413
459
|
}),
|
|
414
460
|
updateDeviceAndStream: assign({
|
|
@@ -416,8 +462,10 @@ const livenessMachine = createMachine({
|
|
|
416
462
|
setLastSelectedCameraId(event.data?.newDeviceId);
|
|
417
463
|
return {
|
|
418
464
|
...context.videoAssociatedParams,
|
|
419
|
-
selectedDeviceId: event.data
|
|
420
|
-
|
|
465
|
+
selectedDeviceId: event.data
|
|
466
|
+
?.newDeviceId,
|
|
467
|
+
videoMediaStream: event.data
|
|
468
|
+
?.newStream,
|
|
421
469
|
};
|
|
422
470
|
},
|
|
423
471
|
}),
|
|
@@ -478,50 +526,49 @@ const livenessMachine = createMachine({
|
|
|
478
526
|
'recording') {
|
|
479
527
|
context.livenessStreamProvider.startRecordingLivenessVideo();
|
|
480
528
|
}
|
|
481
|
-
return {
|
|
482
|
-
...context.videoAssociatedParams,
|
|
483
|
-
};
|
|
529
|
+
return { ...context.videoAssociatedParams };
|
|
484
530
|
},
|
|
485
531
|
}),
|
|
486
|
-
stopRecording: (
|
|
532
|
+
stopRecording: () => { },
|
|
487
533
|
updateFaceMatchBeforeStartDetails: assign({
|
|
488
|
-
faceMatchStateBeforeStart: (_, event) =>
|
|
489
|
-
return event.data.faceMatchState;
|
|
490
|
-
},
|
|
534
|
+
faceMatchStateBeforeStart: (_, event) => event.data.faceMatchState,
|
|
491
535
|
}),
|
|
492
536
|
updateFaceDistanceBeforeRecording: assign({
|
|
493
|
-
isFaceFarEnoughBeforeRecording: (_, event) =>
|
|
494
|
-
return event.data.isFaceFarEnoughBeforeRecording;
|
|
495
|
-
},
|
|
537
|
+
isFaceFarEnoughBeforeRecording: (_, event) => !!event.data.isFaceFarEnoughBeforeRecording,
|
|
496
538
|
}),
|
|
497
539
|
updateFaceDistanceWhileLoading: assign({
|
|
498
|
-
isFaceFarEnoughBeforeRecording: (_, event) =>
|
|
499
|
-
|
|
500
|
-
},
|
|
501
|
-
errorState: (_, event) => {
|
|
502
|
-
return event.data?.error;
|
|
503
|
-
},
|
|
540
|
+
isFaceFarEnoughBeforeRecording: (_, event) => !!event.data.isFaceFarEnoughBeforeRecording,
|
|
541
|
+
errorState: (_, event) => event.data?.error,
|
|
504
542
|
}),
|
|
505
543
|
updateOvalAndFaceDetailsPostDraw: assign({
|
|
506
544
|
ovalAssociatedParams: (context, event) => ({
|
|
507
545
|
...context.ovalAssociatedParams,
|
|
508
|
-
initialFace: event.data
|
|
509
|
-
|
|
510
|
-
|
|
546
|
+
initialFace: event.data
|
|
547
|
+
.initialFace,
|
|
548
|
+
ovalDetails: event.data
|
|
549
|
+
.ovalDetails,
|
|
550
|
+
scaleFactor: event.data
|
|
551
|
+
.scaleFactor,
|
|
511
552
|
}),
|
|
512
553
|
faceMatchAssociatedParams: (context, event) => ({
|
|
513
554
|
...context.faceMatchAssociatedParams,
|
|
514
|
-
faceMatchState: event.data
|
|
515
|
-
|
|
555
|
+
faceMatchState: event.data
|
|
556
|
+
.faceMatchState,
|
|
557
|
+
illuminationState: event.data
|
|
558
|
+
.illuminationState,
|
|
516
559
|
}),
|
|
517
560
|
}),
|
|
518
561
|
updateFaceDetailsPostMatch: assign({
|
|
519
562
|
faceMatchAssociatedParams: (context, event) => ({
|
|
520
563
|
...context.faceMatchAssociatedParams,
|
|
521
|
-
faceMatchState: event.data
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
564
|
+
faceMatchState: event.data
|
|
565
|
+
.faceMatchState,
|
|
566
|
+
faceMatchPercentage: event.data
|
|
567
|
+
.faceMatchPercentage,
|
|
568
|
+
illuminationState: event.data
|
|
569
|
+
.illuminationState,
|
|
570
|
+
currentDetectedFace: event.data
|
|
571
|
+
.detectedFace,
|
|
525
572
|
}),
|
|
526
573
|
}),
|
|
527
574
|
updateEndFaceMatch: assign({
|
|
@@ -544,40 +591,30 @@ const livenessMachine = createMachine({
|
|
|
544
591
|
};
|
|
545
592
|
},
|
|
546
593
|
}),
|
|
547
|
-
resetErrorState: assign({
|
|
548
|
-
errorState: (_) => undefined,
|
|
549
|
-
}),
|
|
594
|
+
resetErrorState: assign({ errorState: (_) => undefined }),
|
|
550
595
|
updateErrorStateForTimeout: assign({
|
|
551
|
-
errorState: (_, event) =>
|
|
552
|
-
return event.data?.errorState || LivenessErrorState.TIMEOUT;
|
|
553
|
-
},
|
|
596
|
+
errorState: (_, event) => event.data?.errorState || LivenessErrorState.TIMEOUT,
|
|
554
597
|
}),
|
|
555
598
|
updateErrorStateForRuntime: assign({
|
|
556
|
-
errorState: (_, event) =>
|
|
557
|
-
|
|
558
|
-
},
|
|
599
|
+
errorState: (_, event) => event.data?.errorState ||
|
|
600
|
+
LivenessErrorState.RUNTIME_ERROR,
|
|
559
601
|
}),
|
|
560
602
|
updateErrorStateForServer: assign({
|
|
561
603
|
errorState: (_) => LivenessErrorState.SERVER_ERROR,
|
|
562
604
|
}),
|
|
563
|
-
clearErrorState: assign({
|
|
564
|
-
errorState: (_) => undefined,
|
|
565
|
-
}),
|
|
605
|
+
clearErrorState: assign({ errorState: (_) => undefined }),
|
|
566
606
|
updateSessionInfo: assign({
|
|
567
|
-
serverSessionInformation: (
|
|
607
|
+
serverSessionInformation: (_, event) => {
|
|
568
608
|
return event.data.sessionInfo;
|
|
569
609
|
},
|
|
570
610
|
}),
|
|
571
|
-
updateShouldDisconnect: assign({
|
|
572
|
-
shouldDisconnect: (context) => {
|
|
573
|
-
return true;
|
|
574
|
-
},
|
|
575
|
-
}),
|
|
611
|
+
updateShouldDisconnect: assign({ shouldDisconnect: () => true }),
|
|
576
612
|
updateFreshnessDetails: assign({
|
|
577
613
|
freshnessColorAssociatedParams: (context, event) => {
|
|
578
614
|
return {
|
|
579
615
|
...context.freshnessColorAssociatedParams,
|
|
580
|
-
freshnessColorsComplete: event.data
|
|
616
|
+
freshnessColorsComplete: event.data
|
|
617
|
+
.freshnessColorsComplete,
|
|
581
618
|
};
|
|
582
619
|
},
|
|
583
620
|
}),
|
|
@@ -602,7 +639,7 @@ const livenessMachine = createMachine({
|
|
|
602
639
|
delay: (context) => {
|
|
603
640
|
return (context.serverSessionInformation?.Challenge
|
|
604
641
|
?.FaceMovementAndLightChallenge?.ChallengeConfig
|
|
605
|
-
?.OvalFitTimeout
|
|
642
|
+
?.OvalFitTimeout ?? DEFAULT_FACE_FIT_TIMEOUT);
|
|
606
643
|
},
|
|
607
644
|
id: 'ovalMatchTimeout',
|
|
608
645
|
}),
|
|
@@ -644,14 +681,12 @@ const livenessMachine = createMachine({
|
|
|
644
681
|
},
|
|
645
682
|
}),
|
|
646
683
|
callMobileLandscapeWarningCallback: assign({
|
|
647
|
-
errorState: (
|
|
648
|
-
return LivenessErrorState.MOBILE_LANDSCAPE_ERROR;
|
|
649
|
-
},
|
|
684
|
+
errorState: () => LivenessErrorState.MOBILE_LANDSCAPE_ERROR,
|
|
650
685
|
}),
|
|
651
|
-
callUserCancelCallback:
|
|
686
|
+
callUserCancelCallback: (context) => {
|
|
652
687
|
context.componentProps.onUserCancel?.();
|
|
653
688
|
},
|
|
654
|
-
callUserTimeoutCallback:
|
|
689
|
+
callUserTimeoutCallback: (context) => {
|
|
655
690
|
const error = new Error('Client Timeout');
|
|
656
691
|
error.name = context.errorState;
|
|
657
692
|
const livenessError = {
|
|
@@ -660,14 +695,14 @@ const livenessMachine = createMachine({
|
|
|
660
695
|
};
|
|
661
696
|
context.componentProps.onError?.(livenessError);
|
|
662
697
|
},
|
|
663
|
-
callErrorCallback:
|
|
698
|
+
callErrorCallback: (context, event) => {
|
|
664
699
|
const livenessError = {
|
|
665
700
|
state: context.errorState,
|
|
666
701
|
error: event.data?.error || event.data,
|
|
667
702
|
};
|
|
668
703
|
context.componentProps.onError?.(livenessError);
|
|
669
704
|
},
|
|
670
|
-
cleanUpResources:
|
|
705
|
+
cleanUpResources: (context) => {
|
|
671
706
|
const { freshnessColorEl } = context.freshnessColorAssociatedParams;
|
|
672
707
|
if (freshnessColorEl) {
|
|
673
708
|
freshnessColorEl.style.display = 'none';
|
|
@@ -686,9 +721,9 @@ const livenessMachine = createMachine({
|
|
|
686
721
|
else if (context.errorState === undefined) {
|
|
687
722
|
closureCode = WS_CLOSURE_CODE.USER_CANCEL;
|
|
688
723
|
}
|
|
689
|
-
|
|
724
|
+
context.livenessStreamProvider?.endStreamWithCode(closureCode);
|
|
690
725
|
},
|
|
691
|
-
freezeStream:
|
|
726
|
+
freezeStream: (context) => {
|
|
692
727
|
const { videoMediaStream, videoEl } = context.videoAssociatedParams;
|
|
693
728
|
context.isRecordingStopped = true;
|
|
694
729
|
videoEl?.pause();
|
|
@@ -696,7 +731,7 @@ const livenessMachine = createMachine({
|
|
|
696
731
|
track.stop();
|
|
697
732
|
});
|
|
698
733
|
},
|
|
699
|
-
pauseVideoStream:
|
|
734
|
+
pauseVideoStream: (context) => {
|
|
700
735
|
const { videoEl } = context.videoAssociatedParams;
|
|
701
736
|
context.isRecordingStopped = true;
|
|
702
737
|
videoEl.pause();
|
|
@@ -752,7 +787,6 @@ const livenessMachine = createMachine({
|
|
|
752
787
|
hasNotEnoughFaceDistanceBeforeRecording: (context) => {
|
|
753
788
|
return !context.isFaceFarEnoughBeforeRecording;
|
|
754
789
|
},
|
|
755
|
-
hasLivenessCheckSucceeded: (_, __, meta) => meta.state.event.data.isLive,
|
|
756
790
|
hasFreshnessColorShown: (context) => context.freshnessColorAssociatedParams.freshnessColorsComplete,
|
|
757
791
|
hasServerSessionInfo: (context) => {
|
|
758
792
|
return context.serverSessionInformation !== undefined;
|
|
@@ -805,14 +839,15 @@ const livenessMachine = createMachine({
|
|
|
805
839
|
// If the initial stream is of real camera, use it otherwise use the first real camera
|
|
806
840
|
const initialStreamDeviceId = tracksWithMoreThan15Fps[0].getSettings().deviceId;
|
|
807
841
|
const isInitialStreamFromRealDevice = realVideoDevices.some((device) => device.deviceId === initialStreamDeviceId);
|
|
808
|
-
|
|
842
|
+
const deviceId = isInitialStreamFromRealDevice
|
|
843
|
+
? initialStreamDeviceId
|
|
844
|
+
: realVideoDevices[0].deviceId;
|
|
809
845
|
let realVideoDeviceStream = initialStream;
|
|
810
846
|
if (!isInitialStreamFromRealDevice) {
|
|
811
|
-
deviceId = realVideoDevices[0].deviceId;
|
|
812
847
|
realVideoDeviceStream = await navigator.mediaDevices.getUserMedia({
|
|
813
848
|
video: {
|
|
814
849
|
...videoConstraints,
|
|
815
|
-
deviceId: { exact:
|
|
850
|
+
deviceId: { exact: deviceId },
|
|
816
851
|
},
|
|
817
852
|
audio: false,
|
|
818
853
|
});
|
|
@@ -824,6 +859,7 @@ const livenessMachine = createMachine({
|
|
|
824
859
|
selectableDevices: realVideoDevices,
|
|
825
860
|
};
|
|
826
861
|
},
|
|
862
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
827
863
|
async openLivenessStreamConnection(context) {
|
|
828
864
|
const { config } = context.componentProps;
|
|
829
865
|
const { credentialProvider, endpointOverride } = config;
|
|
@@ -846,6 +882,7 @@ const livenessMachine = createMachine({
|
|
|
846
882
|
await faceDetector.modelLoadingPromise;
|
|
847
883
|
}
|
|
848
884
|
catch (err) {
|
|
885
|
+
// eslint-disable-next-line no-console
|
|
849
886
|
console.log({ err });
|
|
850
887
|
}
|
|
851
888
|
// detect face
|
|
@@ -885,10 +922,11 @@ const livenessMachine = createMachine({
|
|
|
885
922
|
});
|
|
886
923
|
const { isDistanceBelowThreshold: isFaceFarEnoughBeforeRecording, error, } = await isFaceDistanceBelowThreshold({
|
|
887
924
|
faceDetector: faceDetector,
|
|
888
|
-
|
|
925
|
+
isMobile,
|
|
889
926
|
ovalDetails,
|
|
927
|
+
videoEl: videoEl,
|
|
928
|
+
// if this is the second face distance check reduce the threshold
|
|
890
929
|
reduceThreshold: faceDistanceCheckBeforeRecording,
|
|
891
|
-
isMobile,
|
|
892
930
|
});
|
|
893
931
|
return { isFaceFarEnoughBeforeRecording, error };
|
|
894
932
|
},
|
|
@@ -902,6 +940,7 @@ const livenessMachine = createMachine({
|
|
|
902
940
|
await livenessStreamProvider.videoRecorder.recorderStarted;
|
|
903
941
|
}
|
|
904
942
|
catch (err) {
|
|
943
|
+
// eslint-disable-next-line no-console
|
|
905
944
|
console.log({ err });
|
|
906
945
|
}
|
|
907
946
|
// detect face
|
|
@@ -917,6 +956,7 @@ const livenessMachine = createMachine({
|
|
|
917
956
|
break;
|
|
918
957
|
}
|
|
919
958
|
case 1: {
|
|
959
|
+
//exactly one face detected;
|
|
920
960
|
faceMatchState = FaceMatchState.FACE_IDENTIFIED;
|
|
921
961
|
initialFace = detectedFaces[0];
|
|
922
962
|
break;
|
|
@@ -1063,7 +1103,7 @@ const livenessMachine = createMachine({
|
|
|
1063
1103
|
throw new Error('Video chunks not recorded successfully.');
|
|
1064
1104
|
}
|
|
1065
1105
|
livenessStreamProvider.sendClientInfo(livenessActionDocument);
|
|
1066
|
-
|
|
1106
|
+
livenessStreamProvider.dispatchStopVideoEvent();
|
|
1067
1107
|
},
|
|
1068
1108
|
async getLiveness(context) {
|
|
1069
1109
|
const { onAnalysisComplete } = context.componentProps;
|
|
@@ -1072,59 +1112,5 @@ const livenessMachine = createMachine({
|
|
|
1072
1112
|
},
|
|
1073
1113
|
},
|
|
1074
1114
|
});
|
|
1075
|
-
const responseStreamActor = async (callback) => {
|
|
1076
|
-
try {
|
|
1077
|
-
const stream = await responseStream;
|
|
1078
|
-
for await (const event of stream) {
|
|
1079
|
-
if (isServerSesssionInformationEvent(event)) {
|
|
1080
|
-
callback({
|
|
1081
|
-
type: 'SET_SESSION_INFO',
|
|
1082
|
-
data: {
|
|
1083
|
-
sessionInfo: event.ServerSessionInformationEvent.SessionInformation,
|
|
1084
|
-
},
|
|
1085
|
-
});
|
|
1086
|
-
}
|
|
1087
|
-
else if (isDisconnectionEvent(event)) {
|
|
1088
|
-
callback({ type: 'DISCONNECT_EVENT' });
|
|
1089
|
-
}
|
|
1090
|
-
else if (isValidationExceptionEvent(event)) {
|
|
1091
|
-
callback({
|
|
1092
|
-
type: 'SERVER_ERROR',
|
|
1093
|
-
data: { error: { ...event.ValidationException } },
|
|
1094
|
-
});
|
|
1095
|
-
}
|
|
1096
|
-
else if (isInternalServerExceptionEvent(event)) {
|
|
1097
|
-
callback({
|
|
1098
|
-
type: 'SERVER_ERROR',
|
|
1099
|
-
data: { error: { ...event.InternalServerException } },
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
else if (isThrottlingExceptionEvent(event)) {
|
|
1103
|
-
callback({
|
|
1104
|
-
type: 'SERVER_ERROR',
|
|
1105
|
-
data: { error: { ...event.ThrottlingException } },
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
|
-
else if (isServiceQuotaExceededExceptionEvent(event)) {
|
|
1109
|
-
callback({
|
|
1110
|
-
type: 'SERVER_ERROR',
|
|
1111
|
-
data: { error: { ...event.ServiceQuotaExceededException } },
|
|
1112
|
-
});
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
catch (error) {
|
|
1117
|
-
let returnedError = error;
|
|
1118
|
-
if (isInvalidSignatureRegionException(error)) {
|
|
1119
|
-
returnedError = new Error('Invalid region in FaceLivenessDetector or credentials are scoped to the wrong region.');
|
|
1120
|
-
}
|
|
1121
|
-
if (returnedError instanceof Error) {
|
|
1122
|
-
callback({
|
|
1123
|
-
type: 'SERVER_ERROR',
|
|
1124
|
-
data: { error: returnedError },
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
};
|
|
1129
1115
|
|
|
1130
|
-
export {
|
|
1116
|
+
export { livenessMachine };
|
package/dist/esm/components/FaceLivenessDetector/service/utils/CustomWebSocketFetchHandler.mjs
CHANGED
|
@@ -23,10 +23,10 @@ const getIterator = (stream) => {
|
|
|
23
23
|
return stream;
|
|
24
24
|
}
|
|
25
25
|
if (isReadableStream(stream)) {
|
|
26
|
-
//If stream is a ReadableStream, transfer the ReadableStream to async iterable.
|
|
26
|
+
// If stream is a ReadableStream, transfer the ReadableStream to async iterable.
|
|
27
27
|
return readableStreamtoIterable(stream);
|
|
28
28
|
}
|
|
29
|
-
//For other types, just wrap them with an async iterable.
|
|
29
|
+
// For other types, just wrap them with an async iterable.
|
|
30
30
|
return {
|
|
31
31
|
[Symbol.asyncIterator]: async function* () {
|
|
32
32
|
yield stream;
|
|
@@ -85,8 +85,7 @@ class CustomWebSocketFetchHandler {
|
|
|
85
85
|
}
|
|
86
86
|
this.sockets[url].push(socket);
|
|
87
87
|
socket.binaryType = 'arraybuffer';
|
|
88
|
-
const { connectionTimeout = DEFAULT_WS_CONNECTION_TIMEOUT_MS } = await this
|
|
89
|
-
.configPromise;
|
|
88
|
+
const { connectionTimeout = DEFAULT_WS_CONNECTION_TIMEOUT_MS } = await this.configPromise;
|
|
90
89
|
await this.waitForReady(socket, connectionTimeout);
|
|
91
90
|
const { body } = request;
|
|
92
91
|
const bodyStream = getIterator(body);
|