@richard.fadiora/liveness-detection 4.3.7 → 4.3.9
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 +347 -232
- package/dist/angular/README.md +347 -232
- package/dist/angular/fesm2022/richard.fadiora-liveness-detection.mjs +29 -2
- package/dist/angular/fesm2022/richard.fadiora-liveness-detection.mjs.map +1 -1
- package/dist/angular/types/richard.fadiora-liveness-detection.d.ts +8 -1
- package/dist/index.cjs +1 -3
- package/dist/index.js +6 -643
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,294 +1,409 @@
|
|
|
1
1
|
# Liveness Detection SDK
|
|
2
2
|
|
|
3
|
-
A cross-framework **liveness detection SDK** that performs randomized user challenges and verifies real-user presence via a backend anti-spoofing API. Works with **React** and **Angular** via framework-specific wrappers while the **core engine is framework-agnostic**.
|
|
4
3
|
|
|
5
|
-
This version introduces:
|
|
6
4
|
|
|
7
|
-
- **
|
|
8
|
-
-
|
|
9
|
-
- **Configurable thresholds** for `Smile`, `Blink`, and `Turn_Head` challenges
|
|
10
|
-
- Sequential challenge execution with **strict timeout handling**
|
|
11
|
-
- Pause between steps for user feedback
|
|
12
|
-
- Cross-framework integration (React hook & Angular service)
|
|
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 🅰️**.
|
|
13
7
|
|
|
14
|
-
|
|
8
|
+
* * *
|
|
9
|
+
|
|
10
|
+
## 🚀 Key Features in v4.3.9
|
|
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
|
+
* * *
|
|
15
26
|
|
|
16
27
|
## 📌 Overview
|
|
17
28
|
|
|
29
|
+
|
|
30
|
+
|
|
18
31
|
This SDK strengthens identity verification by combining:
|
|
19
32
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
* 🎲 Randomized challenge-response validation
|
|
34
|
+
|
|
35
|
+
* 📋 Sequential challenge execution (Smile, Blink, Turn\_Head, Thumbs\_Up)
|
|
36
|
+
|
|
37
|
+
* 🧠 Real-time MediaPipe face & hand landmark detection
|
|
38
|
+
|
|
39
|
+
* 🛡️ Protection against presentation attacks (photo/video), glare, and replay attacks
|
|
40
|
+
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
* * *
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
- Screen glare attacks
|
|
32
|
-
- Video replay/injection attacks
|
|
44
|
+
## 📦 Installation & Imports
|
|
33
45
|
|
|
34
|
-
---
|
|
35
46
|
|
|
36
|
-
## ⚙️ Architecture
|
|
37
47
|
|
|
38
|
-
|
|
48
|
+
`npm install @richard.fadiora/liveness-detection`
|
|
39
49
|
|
|
40
|
-
|
|
41
|
-
- Handles:
|
|
42
|
-
- Challenge sequence generation
|
|
43
|
-
- MediaPipe face & hand model detection
|
|
44
|
-
- Challenge validation (`Smile`, `Blink`, `Turn_Head`, `Thumbs_Up`)
|
|
45
|
-
- Face cropping
|
|
46
|
-
- Detection loop
|
|
47
|
-
- Final frame capture and backend verification
|
|
48
|
-
- Can be used directly or via React/Angular wrappers
|
|
50
|
+
### Import Paths
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
#
|
|
51
53
|
|
|
52
|
-
|
|
|
53
|
-
|
|
54
|
-
|
|
|
55
|
-
|
|
|
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 |
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
These entry points ensure framework-specific code stays isolated and bundles remain small.
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
* * *
|
|
60
63
|
|
|
61
|
-
|
|
64
|
+
## 🧩 Framework Usage
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
- Timer starts immediately (default **60 seconds**, configurable via `duration`).
|
|
65
|
-
- Each challenge is verified sequentially, with a brief pause between steps for feedback.
|
|
66
|
-
- If the timer expires, the session terminates and no frames are sent to the backend.
|
|
66
|
+
### 🅰️ Angular Implementation (Standalone)
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
#
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
- Sequential verification ensures **no steps are skipped**.
|
|
72
|
-
- Developers can render custom UI via the **render prop** while using the same underlying logic.
|
|
73
|
-
- Challenge thresholds are configurable through props:
|
|
70
|
+
The Angular wrapper is now **standalone**. Import it directly into your component’s `imports` array without using a module:
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
+
}
|
|
81
100
|
|
|
82
|
-
|
|
101
|
+
⚠️ **Important Rule:** Standalone components **must not** be declared in an NgModule. Doing so disables standalone behavior and can cause runtime errors.
|
|
83
102
|
|
|
84
|
-
|
|
85
|
-
- Frames sent to backend API (`apiUrl` prop).
|
|
86
|
-
- Backend performs spoof detection, glare detection, and video injection detection.
|
|
103
|
+
* * *
|
|
87
104
|
|
|
88
|
-
|
|
105
|
+
### ⚛️ React Implementation
|
|
89
106
|
|
|
90
|
-
|
|
107
|
+
#
|
|
91
108
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
+
}
|
|
98
121
|
|
|
99
|
-
|
|
100
|
-
- UI shows failure message.
|
|
101
|
-
- `onError` callback is triggered:
|
|
102
|
-
```js
|
|
103
|
-
onError({ success: false, reason, skinConfidence: null });
|
|
104
|
-
```
|
|
105
|
-
- Component resets; user must start a new session.
|
|
122
|
+
* * *
|
|
106
123
|
|
|
107
|
-
|
|
124
|
+
## 🎨 Prop-Driven Styling
|
|
108
125
|
|
|
109
|
-
## 📦 Props
|
|
110
126
|
|
|
111
|
-
| Prop Name | Type | Required | Description |
|
|
112
|
-
|------------|-----------------------------|----------|-------------|
|
|
113
|
-
| `apiUrl` | `string` | Yes | Backend endpoint used for liveness verification |
|
|
114
|
-
| `onComplete` | `(result: object) => void` | Yes | Callback fired after verification succeeds |
|
|
115
|
-
| `onError` | `(result: object) => void` | Yes | Callback fired after verification fails |
|
|
116
|
-
| `duration` | `number` | No | Maximum time for all challenges (default: 60s) |
|
|
117
|
-
| `render` | `(sdk: object) => JSX.Element` | No | Optional render prop for full UI customization |
|
|
118
|
-
| `classNames` | `object` | No | Optional CSS class names to customize default UI |
|
|
119
|
-
| `smileThreshold` | `number` | No | Minimum smile width for Smile challenge |
|
|
120
|
-
| `blinkThreshold` | `number` | No | Maximum eye aspect ratio for Blink challenge |
|
|
121
|
-
| `minturnHeadThreshold` | `number` | No | Minimum yaw for Turn_Head challenge |
|
|
122
|
-
| `maxturnHeadThreshold` | `number` | No | Maximum yaw for Turn_Head challenge |
|
|
123
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 |
|
|
124
137
|
|
|
125
|
-
|
|
138
|
+
* * *
|
|
139
|
+
## Customizing Challenge Labels
|
|
126
140
|
|
|
127
|
-
## 🧩 Usage Example
|
|
128
141
|
|
|
129
|
-
### Default UI
|
|
130
|
-
```jsx
|
|
131
|
-
import { LivenessSDK } from "@richard.fadiora/liveness-detection";
|
|
132
142
|
|
|
133
|
-
|
|
134
|
-
return (
|
|
135
|
-
<LivenessSDK
|
|
136
|
-
apiUrl="https://your-backend-api.com/liveness-check"
|
|
137
|
-
onComplete={(result) => console.log("Success:", result)}
|
|
138
|
-
onError={(error) => console.log("Failed:", error.reason)}
|
|
139
|
-
duration={60}
|
|
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.
|
|
143
144
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
|
175
|
-
|
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
###
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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**
|
|
223
|
+
|
|
224
|
+
TypeScript
|
|
225
|
+
|
|
226
|
+
// 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
|
+
}
|
|
244
|
+
}
|
|
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
|
+
|
|
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." |
|
|
281
|
+
|
|
282
|
+
* * *
|
|
283
|
+
|
|
284
|
+
### Pro-Tip: Implementing a User Hint System
|
|
285
|
+
|
|
286
|
+
##
|
|
287
|
+
|
|
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.
|
|
289
|
+
|
|
290
|
+
TypeScript
|
|
291
|
+
|
|
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
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
* * *
|
|
306
|
+
|
|
307
|
+
### 📦 Latest Changes Summary
|
|
308
|
+
|
|
309
|
+
##
|
|
310
|
+
|
|
311
|
+
* **Zero-CSS Architecture:** All default styles removed; fully styleable via `[ngClass]` inputs.
|
|
312
|
+
|
|
313
|
+
* **Label Mapping:** Added `[challengeMapping]` to allow custom text for "Smile", "Blink", etc.
|
|
314
|
+
|
|
315
|
+
* **State Hook:** New `(onStateChange)` event for advanced lifecycle management.
|
|
316
|
+
|
|
317
|
+
* **Encapsulation:** Set to `ViewEncapsulation.None` for seamless Tailwind and Global CSS integration.
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
## ⚙️ Configuration & Thresholds
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
| Prop Name | Default | Description |
|
|
325
|
+
| --- | --- | --- |
|
|
326
|
+
| `duration` | 60 | Total session time in seconds |
|
|
327
|
+
| `smileThreshold` | 0.20 | Minimum width for smile detection |
|
|
328
|
+
| `blinkThreshold` | 0.01 | Sensitivity for eye-closure detection |
|
|
329
|
+
| `minturnHeadThreshold` | 0.15 | Minimum yaw for right-turn detection |
|
|
330
|
+
| `maxturnHeadThreshold` | 0.85 | Maximum yaw for left-turn detection |
|
|
331
|
+
|
|
332
|
+
* * *
|
|
333
|
+
|
|
334
|
+
## ✅ Success & Failure Payload
|
|
335
|
+
|
|
336
|
+
### onComplete Result
|
|
337
|
+
|
|
338
|
+
#
|
|
339
|
+
|
|
340
|
+
{
|
|
341
|
+
success: true,
|
|
342
|
+
image: string, // Base64 encoded capture frame
|
|
343
|
+
skinConfidence: number // AI model confidence score
|
|
239
344
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
345
|
+
|
|
346
|
+
### onError Result
|
|
347
|
+
|
|
348
|
+
#
|
|
349
|
+
|
|
350
|
+
{
|
|
351
|
+
success: false,
|
|
352
|
+
reason: string, // "timeout", "challenge\_failed", "camera\_denied"
|
|
243
353
|
}
|
|
244
|
-
```
|
|
245
354
|
|
|
246
|
-
|
|
355
|
+
* * *
|
|
356
|
+
|
|
357
|
+
## 🛠️ Advanced: Headless Mode
|
|
247
358
|
|
|
248
|
-
## ⏳ Timeout Rules
|
|
249
359
|
|
|
250
|
-
- Maximum session duration: set via `duration` prop (default 60s).
|
|
251
|
-
- On timeout: challenge stops, state resets, backend verification is not triggered.
|
|
252
360
|
|
|
253
|
-
|
|
361
|
+
* **Angular 🅰️**: Inject `LivenessService` to access the `state$` observable.
|
|
362
|
+
|
|
363
|
+
* **React ⚛️**: Use `useLiveness` hook.
|
|
364
|
+
|
|
254
365
|
|
|
255
|
-
|
|
366
|
+
Allows full custom UI while using the detection engine.
|
|
256
367
|
|
|
257
|
-
|
|
258
|
-
- **Server-side**: Frame-based spoof analysis, glare detection, video injection detection
|
|
259
|
-
- **Session control**: Timeout enforcement, manual restart on failure
|
|
260
|
-
- Reduces false positives and prevents replay attacks.
|
|
368
|
+
* * *
|
|
261
369
|
|
|
262
|
-
|
|
370
|
+
## 🏗️ SDK Architecture
|
|
263
371
|
|
|
264
|
-
## 📊 Verification Criteria
|
|
265
372
|
|
|
266
|
-
- All 3 challenges completed
|
|
267
|
-
- All 5 frames successfully sent to backend
|
|
268
|
-
- Backend confirms: no spoofing, no glare, human skin texture, no video injection
|
|
269
373
|
|
|
270
|
-
|
|
374
|
+
Layered design allows the same core detection engine to support multiple frameworks:
|
|
271
375
|
|
|
272
|
-
|
|
376
|
+
* **core**: `LivenessEngine.ts` handles webcam, landmark detection, challenge sequencing, frame capture, and backend verification
|
|
377
|
+
|
|
378
|
+
* **react**: React components + hooks handle UI and state
|
|
379
|
+
|
|
380
|
+
* **angular**: Standalone components + services handle UI and state
|
|
381
|
+
|
|
273
382
|
|
|
274
|
-
|
|
275
|
-
- Backend must accept 5 frames in expected format
|
|
276
|
-
- `apiUrl` must be reachable and have an endpoint `/v1/verify`
|
|
277
|
-
- CORS must be configured properly
|
|
383
|
+
* * *
|
|
278
384
|
|
|
279
|
-
|
|
385
|
+
## 🔧 Integration Notes
|
|
280
386
|
|
|
281
|
-
## 🚀 Ideal Use Cases
|
|
282
387
|
|
|
283
|
-
- KYC verification flows
|
|
284
|
-
- Identity onboarding
|
|
285
|
-
- Account recovery
|
|
286
|
-
- Secure login
|
|
287
|
-
- Financial / compliance apps
|
|
288
388
|
|
|
289
|
-
|
|
389
|
+
* **MediaPipe Dependency**: If you see `TS2307: Cannot find module '@mediapipe/tasks-vision'`, run `npm install @mediapipe/tasks-vision`.
|
|
390
|
+
|
|
391
|
+
* **Angular Button Logic**: `[disabled]="!canStart"` — Angular inverts the boolean so button is enabled when `canStart = true`.
|
|
392
|
+
|
|
393
|
+
* **Webcam Permissions**: Required.
|
|
394
|
+
|
|
395
|
+
* **Timeout Rules**: Session resets if challenges aren’t completed within the configured duration.
|
|
396
|
+
|
|
397
|
+
* **Backend**: Accepts multiple frames for final spoof analysis. Endpoint must be `/v1/verify`
|
|
398
|
+
|
|
399
|
+
* **CORS**: Ensure API allows requests from your frontend domain.
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
* * *
|
|
290
403
|
|
|
291
404
|
## 👨💻 Maintainer
|
|
292
405
|
|
|
293
|
-
Fadiora Richard.
|
|
294
406
|
|
|
407
|
+
|
|
408
|
+
**Fadiora Richard**
|
|
409
|
+
Maintained by **Princeps Credit Systems Limited**
|