@richard.fadiora/liveness-detection 4.3.13 → 5.0.0

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/README.md CHANGED
@@ -1,427 +1,164 @@
1
- # Liveness Detection SDK
1
+ # Liveness Detection SDK v5.0.0
2
2
 
3
+ A high-security, cross-framework **Liveness Detection SDK** designed for financial services. v5.0.0 introduces **Temporal Identity Locking** to prevent identity switching during the verification process.
3
4
 
5
+ ## 🔒 New Security Protocols (v5.0.0)
4
6
 
5
- A cross-framework **liveness detection SDK** that performs randomized user challenges and verifies real-user presence via a backend anti-spoofing API.
6
- This package provides a **framework-agnostic core** with optimized wrappers for **React ⚛️** and **Angular 🅰️**.
7
+ This version introduces a "Zero-Trust" frontend architecture:
7
8
 
8
- * * *
9
-
10
- ## 🚀 Key Features in v4.3.13
11
-
12
-
13
-
14
- * 📦 **Multi-Entry Support**: Dedicated subpaths for React and Angular to prevent framework leakage.
15
-
16
- * 🎨 **Prop-Driven Styling**: Fully customizable UI using class name injection (Tailwind/Bootstrap friendly).
17
-
18
- * 🧠 **Headless Mode**: Access core logic via hooks (React) or services (Angular) for 100% custom UI.
19
-
20
- * 🅰️ **Standalone Angular Support**: Angular wrapper uses standalone components; no NgModules needed.
21
-
22
- * 🔒 **Improved Security**: Enhanced challenge sequencing with strict timeout handling and backend frame verification.
23
-
24
-
25
- * * *
26
-
27
- ## 📌 Overview
28
-
29
-
30
-
31
- This SDK strengthens identity verification by combining:
32
-
33
- * 🎲 Randomized challenge-response validation
9
+ * **Identity Anchoring**: Locks the biometric proportions of the first person detected. If a different person attempts to complete a challenge, the session terminates.
34
10
 
35
- * 📋 Sequential challenge execution (Smile, Blink, Turn\_Head, Thumbs\_Up)
11
+ * **Continuity Guard**: Monitoring at 60fps to ensure the face never leaves the frame. A "Face Lost" event for >250ms triggers an automatic security reset.
36
12
 
37
- * 🧠 Real-time MediaPipe face & hand landmark detection
13
+ * **Teleport Detection**: Prevents video injection or photo-swapping by detecting unnatural "jumps" in face coordinates.
38
14
 
39
- * 🛡️ Protection against presentation attacks (photo/video), glare, and replay attacks
15
+ * **Multi-Tab Lock**: Prevents brute-force attempts by ensuring only one liveness session is active per browser instance.
40
16
 
41
17
 
42
- * * *
43
-
44
- ## 📦 Installation & Imports
45
-
46
-
47
-
48
- `npm install @richard.fadiora/liveness-detection`
49
-
50
- ### Import Paths
51
-
52
- #
53
-
54
- | Target | Import Path |
55
- | --- | --- |
56
- | Pure Logic (Core) | @richard.fadiora/liveness-detection |
57
- | React Wrapper ⚛️ | @richard.fadiora/liveness-detection/react |
58
- | Angular Wrapper 🅰️ | @richard.fadiora/liveness-detection/angular |
59
-
60
- These entry points ensure framework-specific code stays isolated and bundles remain small.
61
-
62
18
  * * *
63
19
 
64
20
  ## 🧩 Framework Usage
65
21
 
66
22
  ### 🅰️ Angular Implementation (Standalone)
67
23
 
68
- #
69
-
70
- The Angular wrapper is now **standalone**. Import it directly into your component’s `imports` array without using a module:
71
-
72
- import { Component } from '@angular/core';
73
- import { LivenessComponent } from '@richard.fadiora/liveness-detection/angular';
74
-
75
- @Component({
76
- selector: 'app-verify',
77
- standalone: true,
78
- imports: \[LivenessComponent\],
79
- template: \`
80
- <LivenessCheck
81
- \[apiUrl\]="'https://api.yoursite.com/verify'"
82
- \[containerClass\]="'my-custom-container'"
83
- \[buttonClass\]="'btn-primary'"
84
- (onComplete)="handleSuccess($event)"
85
- (onError)="handleError($event)">
86
- </LivenessCheck>
87
- \`
88
- })
89
- export class VerifyComponent {
90
-
91
- handleSuccess(result: any) {
92
- console.log("Image Captured:", result.image);
93
- }
94
-
95
- handleError(error: any) {
96
- console.error(error.reason);
97
- }
98
-
99
- }
100
-
101
- ⚠️ **Important Rule:** Standalone components **must not** be declared in an NgModule. Doing so disables standalone behavior and can cause runtime errors.
102
-
103
- * * *
104
-
105
- ### ⚛️ React Implementation
106
-
107
- #
108
-
109
- import { LivenessSDK } from "@richard.fadiora/liveness-detection/react";
110
-
111
- function App() {
112
- return (
113
- <LivenessSDK
114
- apiUrl\="https://api.yoursite.com/verify"
115
- classNames\={{ container: "p-4 bg-dark", video: "rounded-lg" }}
116
- onComplete\={(res) => console.log("Success:", res.image)}
117
- onError\={(err) => console.error(err.reason)}
118
- />
119
- );
120
- }
121
-
122
- * * *
123
-
124
- ## 🎨 Prop-Driven Styling
125
-
126
-
127
-
128
- | Angular Input | React Prop | Description |
129
- | --- | --- | --- |
130
- | `containerClass` | classNames.container | Main wrapper div |
131
- | `videoClass` | classNames.webcam | The video element |
132
- | `buttonClass` | classNames.button | The Start button |
133
- | `timerClass` | classNames.timer | Countdown timer text |
134
- | `instructionBoxClass` | classNames.challenge | Challenge instruction box |
135
- | `statusOverlayClass` | classNames.overlay | Overlay during verification |
136
- | `errorMessageClass` | classNames.error | Wrapper for error messages |
137
-
138
- * * *
139
- ## Customizing Challenge Labels
140
-
141
-
142
-
143
- You can override the default technical strings (e.g., `Thumbs_Up`) with user-friendly text or different languages using the `challengeMapping` input.
144
-
145
- | Technical ID | Default Display |
146
- | --- | --- |
147
- | `Smile` | "Smile" |
148
- | `Blink` | "Blink" |
149
- | `Turn_Head` | "Turn Head" |
150
- | `Thumbs_Up` | "Thumbs Up" |
151
-
152
- **Example: Localization (French)**
153
-
154
- TypeScript
155
-
156
- // app.component.ts
157
- challengeLabels = {
158
- 'Smile': 'Souriez',
159
- 'Blink': 'Clignez des yeux',
160
- 'Turn_Head': 'Tournez la tête',
161
- 'Thumbs_Up': 'Levez le pouce'
162
- };
163
- HTML
164
-
165
- <LivenessCheck [challengeMapping]="challengeLabels"></LivenessCheck>
166
-
167
- ###
168
-
169
- ### 📥 Text & Label Customization
170
-
171
- ###
172
-
173
- Customize all user-facing strings to match your app's tone or language.
174
-
175
- | Input Property | Default Value |
176
- | --- | --- |
177
- | `successMessage` | "Verification Successful!" |
178
- | `errorMessage` | "Verification failed. Please try again." |
179
- | `startButtonLabel` | "Start Verification" |
180
- | `retryButtonLabel` | "Try Again" |
181
-
182
- **Example Usage:**
183
-
184
- HTML
185
-
186
- <LivenessCheck
187
- [successMessage]="'Identity Confirmed!'"
188
- [errorMessage]="'We couldn\'t verify you. Move to a brighter room.'"
189
- [startButtonLabel]="'Begin Scan'"
190
- >
191
- </LivenessCheck>
192
-
193
- ## 🔄 State Synchronization
194
-
195
- ###
196
-
197
- The `onStateChange` event provides a real-time stream of the SDK's internal engine. This is useful for building custom instruction overlays or logging telemetry.
198
-
199
- ### The `LivenessState` Object
200
-
201
- ###
202
-
203
- When the state changes, the SDK emits an object with the following structure:
204
-
205
- | Property | Type | Description |
206
- | --- | --- | --- |
207
- | `status` | enum | The current phase of the SDK (see Status Flow below). |
208
- | `sequence` | Challenge[] | The array of 3 randomized challenges (e.g., ['Smile', 'Blink', 'Turn_Head']). |
209
- | `currentStep` | number | The index (0-2) of the challenge the user is currently performing. |
210
- | `timeLeft` | number | The countdown timer in seconds before the session expires. |
211
- | `isStepTransitioning` | boolean | true during the 1.5s pause after a successful challenge. |
212
- | `errorMsg` | string | Contains the technical error code (e.g., SPOOF_BLUR_DETECTED) when status is 'error'. |
213
-
214
- * * *
215
-
216
- ### Implementation: Custom Error Mapping
217
-
218
- ###
219
-
220
- You can use the `onStateChange` handler to "intercept" technical error codes and map them to user-friendly instructions in your local UI.
221
-
222
- **Example: Reactive Instructions**
24
+ The Angular wrapper now exposes `snapshots` via the state observable, allowing you to build "Verification Galleries."
223
25
 
224
26
  TypeScript
225
27
 
226
28
  // app.component.ts
227
- handleStateUpdate(state: LivenessState) {
228
- if (state.status === 'error' && state.errorMsg) {
229
-
230
- // Pattern matching for specific error prefixes
231
- switch (true) {
232
- case state.errorMsg.startsWith('FACE_'):
233
- this.myLocalStatus = "Center your face in the circle";
234
- break;
235
- case state.errorMsg.startsWith('LIGHT_'):
236
- this.myLocalStatus = "Find a brighter spot";
237
- break;
238
- case state.errorMsg.startsWith('TIMEOUT'):
239
- this.myLocalStatus = "Session expired. Tap retry.";
240
- break;
241
- default:
242
- this.myLocalStatus = state.errorMsg;
243
- }
29
+ @Component({
30
+ selector: 'app-verify',
31
+ standalone: true,
32
+ imports: [LivenessComponent],
33
+ template: `
34
+ <LivenessCheck
35
+ [apiUrl]="'https://api.yoursite.com/verify'"
36
+ (onStateChange)="handleState($event)"
37
+ (onComplete)="handleSuccess($event)">
38
+ </LivenessCheck>
39
+
40
+ <div class="snapshot-gallery">
41
+ @for (snap of currentSnapshots; track snap.timestamp) {
42
+ <img [src]="snap.image" class="w-20 h-20 rounded border-2 border-green-500" />
43
+ }
44
+ </div>
45
+ `
46
+ })
47
+ export class VerifyComponent {
48
+ currentSnapshots: any[] = [];
49
+
50
+ handleState(state: LivenessState) {
51
+ this.currentSnapshots = state.snapshots;
244
52
  }
245
- }
246
- HTML
247
-
248
- <LivenessCheck
249
- (onStateChange)="handleStateUpdate($event)"
250
- [errorMessage]="myLocalStatus">
251
- </LivenessCheck>
252
-
253
- * * *
254
-
255
- ### Why use the State Handler?
256
-
257
- ###
258
-
259
- * **Decoupled Logic:** You can keep the SDK's UI simple while handling complex business logic (like analytics or redirecting users) in your main app.
260
53
 
261
- * **Dynamic UI:** Change your app's background color, show a progress bar, or trigger haptic feedback based on the `status` transitions.
262
-
263
- * **Error Interception:** Translate technical backend codes into the specific "voice" of your brand.
264
-
265
- ## 🚨 Error Reference Guide
266
-
267
-
268
-
269
- When the SDK emits a `status: 'error'`, the `errorMsg` will contain one of the following technical codes. You can use these to provide contextual hints (e.g., "Too much light") in your UI.
270
-
271
- | Error Code | Meaning | Recommended User Hint |
272
- | --- | --- | --- |
273
- | `SCREEN_REFLECTION_DETECTED` | Excessive glare or light bouncing off the screen. | "Move away from direct light or windows." |
274
- | `SPOOF_BLUR_DETECTED` | Image is too dark or out of focus. | "Ensure your face is well-lit and clear." |
275
- | `SCREEN_MOIRE_PATTERN_DETECTED` | Digital pixel patterns detected (indicates a photo/screen). | "Please use your physical device camera." |
276
- | `STATIC_IMAGE_OR_SCREEN` | Natural micro-movements not detected. | "Blink or move slightly during the scan." |
277
- | `NO_IMAGE_DATA` | Camera permissions denied or stream interrupted. | "Enable camera access in your settings." |
278
- | `AI_SPOOF_DETECTION` | Low skin-texture confidence from the AI model. | "Verification failed. Please try again." |
279
- | `Network Error` | The backend API could not be reached. | "Check your internet connection." |
280
- | `Liveness Check Failed` | Generic fallback for unspecified validation errors. | "Please try again in a different environment." |
54
+ handleSuccess(result: LivenessSDKResult) {
55
+ // result.snapshots contains the final proof for your local storage
56
+ }
57
+ }
281
58
 
282
59
  * * *
283
60
 
284
- ### Pro-Tip: Implementing a User Hint System
285
-
286
- ##
61
+ ### ⚛️ React Implementation
287
62
 
288
- Instead of showing the raw code (which might confuse a non-technical user), use the `onStateChange` handler to map these codes to your friendly "Recommended User Hint" column.
63
+ The React wrapper has been updated to provide specific security feedback.
289
64
 
290
65
  TypeScript
291
66
 
292
- // Example: Mapping technical codes to friendly messages
293
- handleStateUpdate(state: LivenessState) {
294
- const errorMap: Record<string, string> = {
295
- 'SCREEN_REFLECTION_DETECTED': 'Too much light! Please move to a shaded area.',
296
- 'SPOOF_BLUR_DETECTED': 'It’s a bit dark. Try increasing your screen brightness.',
297
- 'NO_IMAGE_DATA': 'We can’t see you! Please check your camera permissions.'
298
- };
299
-
300
- if (state.status === 'error') {
301
- this.friendlyMessage = errorMap[state.errorMsg] || "Something went wrong. Let's try again.";
302
- }
67
+ import { LivenessSDK } from "@richard.fadiora/liveness-detection/react";
68
+
69
+ function LoanVerification() {
70
+ return (
71
+ <LivenessSDK
72
+ apiUrl="https://api.yoursite.com/verify"
73
+ onStateChange={(state) => {
74
+ if (state.status === "error") {
75
+ console.error("Security Halt:", state.errorMsg);
76
+ }
77
+ }}
78
+ onComplete={(res) => {
79
+ // res.snapshots includes the 3 challenge-response images
80
+ submitLoanApplication(res.snapshots);
81
+ }}
82
+ />
83
+ );
303
84
  }
304
85
 
305
86
  * * *
306
- ### 🧩 Type Handling
307
87
 
308
- ##
88
+ ## 🛠️ Advanced: Consuming the Headless Core
309
89
 
310
- The SDK emits state updates reactively. During the initialization phase, the state may briefly be `null` or `undefined`. Always use optional chaining when accessing state properties:
90
+ If you are building a custom UI for a high-stakes loan flow, you should consume the `LivenessEngine` directly to have granular control over the security lifecycle.
311
91
 
312
- TypeScript
92
+ ### Initializing the Engine
313
93
 
314
- handleStateUpdate(state: LivenessState | null) {
315
- if (state?.status === 'error') {
316
- // safely handle error
317
- }
318
- }
94
+ TypeScript
319
95
 
96
+ import { LivenessEngine } from "@richard.fadiora/liveness-detection";
320
97
 
321
- ### 📦 Latest Changes Summary
322
-
323
- ##
324
-
325
- * **Zero-CSS Architecture:** All default styles removed; fully styleable via `[ngClass]` inputs.
98
+ const engine = new LivenessEngine({
99
+ apiUrl: "...",
100
+ onStateChange: (state) => updateUI(state),
101
+ });
326
102
 
327
- * **Label Mapping:** Added `[challengeMapping]` to allow custom text for "Smile", "Blink", etc.
103
+ // 1. Load WASM and Models
104
+ await engine.loadModels();
328
105
 
329
- * **State Hook:** New `(onStateChange)` event for advanced lifecycle management.
106
+ // 2. Attach Video Element
107
+ engine.attachVideo(document.getElementById('my-video'));
330
108
 
331
- * **Encapsulation:** Set to `ViewEncapsulation.None` for seamless Tailwind and Global CSS integration.
332
-
333
-
334
- ## ⚙️ Configuration & Thresholds
335
-
336
-
337
-
338
- | Prop Name | Default | Description |
339
- | --- | --- | --- |
340
- | `duration` | 60 | Total session time in seconds |
341
- | `smileThreshold` | 0.20 | Minimum width for smile detection |
342
- | `blinkThreshold` | 0.01 | Sensitivity for eye-closure detection |
343
- | `minturnHeadThreshold` | 0.15 | Minimum yaw for right-turn detection |
344
- | `maxturnHeadThreshold` | 0.85 | Maximum yaw for left-turn detection |
345
-
346
- * * *
347
-
348
- ## ✅ Success & Failure Payload
349
-
350
- ### onComplete Result
351
-
352
- #
109
+ // 3. Start Session (Automatic Lock Check)
110
+ engine.start();
353
111
 
354
- {
355
- success: true,
356
- image: string, // Base64 encoded capture frame
357
- skinConfidence: number // AI model confidence score
358
- }
112
+ ### Security Reset vs. User Retry
359
113
 
360
- ### onError Result
361
-
362
- #
363
-
364
- {
365
- success: false,
366
- reason: string, // "timeout", "challenge\_failed", "camera\_denied"
367
- }
114
+ * **`engine.reset()`**: Use this when a security violation occurs (e.g., "Identity Mismatch"). It clears the biometric anchors, allowing a new session to begin from scratch.
115
+
116
+ * **`engine.stop()`**: Always call this when the user navigates away from the page to release the **Multi-Tab Lock**.
117
+
368
118
 
369
119
  * * *
370
120
 
371
- ## Atomic Initialization
121
+ ## 🚨 Updated Error Reference (v4.4.0)
372
122
 
373
- The SDK UI is context-aware; it will not render buttons or instructions until the hardware (Camera) and the software (AI Models) are fully synchronized and ready for capture.
123
+ The following codes are now emitted by the frontend engine **instantly**, without hitting the backend:
374
124
 
375
- ## 🛠️ Advanced: Headless Mode
376
-
377
-
378
-
379
- * **Angular 🅰️**: Inject `LivenessService` to access the `state$` observable.
380
-
381
- * **React ⚛️**: Use `useLiveness` hook.
382
-
383
-
384
- Allows full custom UI while using the detection engine.
125
+ | Error Code | Meaning | Context |
126
+ | --- | --- | --- |
127
+ | Multiple faces detected | Session killed because >1 person is in view. | Prevents "assistance" fraud. |
128
+ | Face lost. Session reset. | Face left the frame for more than 250ms. | Prevents seat-swapping. |
129
+ | Security violation: Identity mismatch. | Biometric ratios shifted significantly mid-session. | Prevents mid-stream person-swapping. |
130
+ | Another session is active. | User has the verification open in another tab. | Prevents multi-device brute forcing. |
385
131
 
386
132
  * * *
387
133
 
388
- ## 🏗️ SDK Architecture
389
-
134
+ ## 🏗️ Architectural Flow
390
135
 
136
+ ### Data Integrity Note
391
137
 
392
- Layered design allows the same core detection engine to support multiple frameworks:
138
+ The `snapshots` array in the `LivenessState` now contains **Challenge-Verified Snapshots**.
393
139
 
394
- * **core**: `LivenessEngine.ts` handles webcam, landmark detection, challenge sequencing, frame capture, and backend verification
140
+ 1. **Snapshot 1**: Captured exactly when "Smile" was detected.
395
141
 
396
- * **react**: React components + hooks handle UI and state
142
+ 2. **Snapshot 2**: Captured exactly when "Blink" was detected.
397
143
 
398
- * **angular**: Standalone components + services handle UI and state
144
+ 3. **Snapshot 3**: Captured exactly when the third challenge was met.
399
145
 
400
146
 
401
- * * *
402
-
403
- ## 🔧 Integration Notes
147
+ These images are cropped to **224x224px** focusing on the face, making them optimal for backend audit logs while keeping payload sizes minimal.
404
148
 
149
+ * * *
405
150
 
151
+ ## 🔧 Integration Notes for v5.0.0
406
152
 
407
- * **MediaPipe Dependency**: If you see `TS2307: Cannot find module '@mediapipe/tasks-vision'`, run `npm install @mediapipe/tasks-vision`.
408
-
409
- * **Angular Button Logic**: `[disabled]="!canStart"` — Angular inverts the boolean so button is enabled when `canStart = true`.
153
+ * **WASM Path**: Ensure your server allows cross-origin requests for the MediaPipe `.wasm` files hosted on JSDelivr.
410
154
 
411
- * **Webcam Permissions**: Required.
155
+ * **Memory Management**: If using in a Single Page App (SPA), always call `engine.stop()` in `onDestroy` or `useEffect` cleanup to release the webcam and the session lock.
412
156
 
413
- * **Timeout Rules**: Session resets if challenges aren’t completed within the configured duration.
414
-
415
- * **Backend**: Accepts multiple frames for final spoof analysis. Endpoint must be `/v1/verify`
416
-
417
- * **CORS**: Ensure API allows requests from your frontend domain.
157
+ * **Hardware Acceleration**: For the "Identity Lock" to run smoothly at 60fps, browsers must have hardware acceleration enabled.
418
158
 
419
159
 
420
160
  * * *
421
161
 
422
- ## 👨‍💻 Maintainer
423
-
424
-
162
+ **Maintainer:** Fadiora Richard
425
163
 
426
- **Fadiora Richard**
427
- Maintained by **Princeps Credit Systems Limited**
164
+ **Organization:** Princeps Credit Systems Limited