biometry-sdk 1.2.6 → 1.3.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 +138 -111
- package/dist/components/biometry-onboarding.d.ts +1 -26
- package/dist/components/biometry-onboarding.js +233 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +2 -0
- package/dist/sdk.js +179 -0
- package/dist/sdk.test.js +255 -0
- package/dist/types.js +14 -0
- package/package.json +11 -5
- package/dist/biometry-sdk.esm.js +0 -1423
- package/dist/components/process-video.d.ts +0 -53
package/README.md
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
# biometry-sdk
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
|
-
The **Biometry Web SDK** is a software development kit designed to simplify the integration of Biometry's API services into your web application. Providing tools, UI components, and utilities enables biometric
|
|
4
|
+
The **Biometry Web SDK** is a software development kit designed to simplify the integration of Biometry's API services into your web application. Providing tools, UI components, and utilities enables biometric enrollment (face and voice), liveness checks, and user consent.
|
|
5
5
|
|
|
6
6
|
## Table of Contents:
|
|
7
7
|
- [Installation](#installation)
|
|
8
8
|
- [Basic Usage (Direct SDK Methods)](#basic-usage-direct-sdk-methods)
|
|
9
|
-
- [
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
- [Consents](#1-consents)
|
|
10
|
+
- [1.1 Give Authorization Consent](#11-give-authorization-consent)
|
|
11
|
+
- [1.2 Give Storage Consent](#12-give-storage-consent)
|
|
12
|
+
- [Face Enrollment](#2-face-enrollment)
|
|
13
|
+
- [Voice Enrollment](#3-voice-enrollment)
|
|
12
14
|
- [Process Video](#4-process-video)
|
|
13
15
|
- [Advanced Usage And Best Practices](#advanced-usage-and-best-practices)
|
|
14
16
|
- [Typical FaceMatch Flow](#typical-facematch-flow)
|
|
15
|
-
- [Use Cases with processVideoRequestId and usePrefilledVideo](#use-cases-with-processVideoRequestId-and-usePrefilledVideo)
|
|
16
17
|
- [Error Handling](#error-handling)
|
|
17
18
|
- [Security And Privacy Considerations](#security-and-privacy-considerations)
|
|
18
19
|
- [UI Components](#ui-components)
|
|
19
|
-
- [Face
|
|
20
|
+
- [Face Enrollment Component](#face-enrollment-component)
|
|
20
21
|
- [Process Video Component](#process-video-component)
|
|
21
22
|
- [License](#license)
|
|
22
23
|
- [More Information](#more-information)
|
|
@@ -40,36 +41,49 @@ const sdk = new BiometrySDK('YOUR_API_KEY');
|
|
|
40
41
|
### Example
|
|
41
42
|
You can find an example in the example/ directory. The example demonstrates how you might integrate the BiometrySDK in a React component with the state.
|
|
42
43
|
|
|
43
|
-
### 1.
|
|
44
|
-
|
|
44
|
+
### 1. Consents
|
|
45
|
+
#### 1.1 Give Authorization Consent
|
|
46
|
+
You **must** obtain user authorization consent before performing any biometric operations (Face Recognition, Voice Recognition, etc.):
|
|
45
47
|
```javascript
|
|
46
|
-
await sdk.
|
|
48
|
+
await sdk.giveAuthorizationConsent(true, 'John Doe');
|
|
47
49
|
// or
|
|
48
|
-
sdk.
|
|
50
|
+
sdk.giveAuthorizationConsent(true, 'John Doe').then(() => {
|
|
49
51
|
console.log('Consent given');
|
|
50
52
|
});
|
|
51
53
|
```
|
|
52
54
|
- The first argument (`true`) indicates that the user has granted consent.
|
|
53
55
|
- The second argument is the user’s full name (used for record-keeping within Biometry).
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
#### 1.2 Give Storage Consent
|
|
58
|
+
You **must** obtain user consent before storing biometric data (Face Enrollment, Voice Enrollment):
|
|
59
|
+
```javascript
|
|
60
|
+
await sdk.giveStorageConsent(true, 'John Doe');
|
|
61
|
+
// or
|
|
62
|
+
sdk.giveStorageConsent(true, 'John Doe').then(() => {
|
|
63
|
+
console.log('Consent given');
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
- The first argument (`true`) indicates that the user has granted consent.
|
|
67
|
+
- The second argument is the user’s full name (used for record-keeping within Biometry).
|
|
68
|
+
|
|
69
|
+
### 2. Face Enrollment
|
|
70
|
+
Enroll a user’s face for future recognition or matching:
|
|
57
71
|
```javascript
|
|
58
72
|
const faceFile = new File([/* face image bytes */], 'face.jpg', { type: 'image/jpeg' });
|
|
59
73
|
|
|
60
|
-
|
|
61
|
-
const faceResponse = await sdk.
|
|
62
|
-
console.log('Face
|
|
74
|
+
await sdk.giveStorageConsent(true, 'John Doe');
|
|
75
|
+
const faceResponse = await sdk.enrollFace(faceFile, 'John Doe');
|
|
76
|
+
console.log('Face Enrollment Response:', faceResponse);
|
|
63
77
|
```
|
|
64
78
|
|
|
65
|
-
### 3. Voice
|
|
79
|
+
### 3. Voice Enrollment
|
|
66
80
|
Enroll a user’s voice for future authentication checks:
|
|
67
81
|
```javascript
|
|
68
82
|
const voiceFile = new File([/* voice audio bytes */], 'voice.wav', { type: 'audio/wav' });
|
|
69
83
|
|
|
70
|
-
await sdk.
|
|
71
|
-
const voiceResponse = await sdk.
|
|
72
|
-
console.log('Voice
|
|
84
|
+
await sdk.giveStorageConsent(true, 'John Doe');
|
|
85
|
+
const voiceResponse = await sdk.enrollVoice(voiceFile, 'John Doe');
|
|
86
|
+
console.log('Voice Enrollment Response:', voiceResponse);
|
|
73
87
|
```
|
|
74
88
|
### 4. Process Video
|
|
75
89
|
Process a user’s video for liveness checks and identity authorization:
|
|
@@ -78,22 +92,77 @@ Process a user’s video for liveness checks and identity authorization:
|
|
|
78
92
|
const phrase = "one two three four five six";
|
|
79
93
|
const userFullName = 'John Doe';
|
|
80
94
|
|
|
81
|
-
await sdk.
|
|
95
|
+
await sdk.giveAuthorizationConsent(true, userFullName);
|
|
82
96
|
|
|
83
97
|
try {
|
|
84
98
|
const response = await sdk.processVideo(videoFile, phrase, userFullName);
|
|
85
99
|
console.log('Process Video Response:', response);
|
|
86
100
|
|
|
87
|
-
//
|
|
88
|
-
|
|
101
|
+
// Retrieve the processVideoRequestId from the *response headers* called x-request-id.
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Error processing video:', error);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
#### Additional
|
|
107
|
+
- `processVideoRequestId`: After calling `sdk.processVideo()`, you typically receive a unique ID (`x-request-id`). You can pass this `processVideoRequestId` into subsequent calls (e.g., `faceMatch`) to reference the previously uploaded video frames.
|
|
108
|
+
- `usePrefilledVideo`: When set to `true`, indicates that the SDK should reuse the video already on file from a previous `processVideo` call rather than requiring a new upload.
|
|
109
|
+
### 5. Face match
|
|
110
|
+
Use matchFaces to compare a reference image (e.g., a document or a captured selfie) with a face from a video:
|
|
111
|
+
```javascript
|
|
112
|
+
/**
|
|
113
|
+
* matchFaces(
|
|
114
|
+
* image: File,
|
|
115
|
+
* video?: File,
|
|
116
|
+
* userFullName?: string,
|
|
117
|
+
* processVideoRequestId?: string,
|
|
118
|
+
* usePrefilledVideo?: boolean,
|
|
119
|
+
* requestUserProvidedId?: string
|
|
120
|
+
* ): Promise<FaceMatchResponse>
|
|
121
|
+
*/
|
|
122
|
+
const faceFile = new File([/* face image bytes */], 'face.jpg', { type: 'image/jpeg' });
|
|
123
|
+
const videoFile = new File([/* file parts */], 'video.mp4', { type: 'video/mp4' });
|
|
124
|
+
const userFullName = 'John Doe';
|
|
125
|
+
|
|
126
|
+
const faceMatchResponse = await sdk.faceMatch(
|
|
127
|
+
faceFile,
|
|
128
|
+
videoFile,
|
|
129
|
+
userFullName
|
|
130
|
+
);
|
|
131
|
+
// OR
|
|
132
|
+
const faceMatchResponse = await sdk.faceMatch(
|
|
133
|
+
faceFile, // The image containing the user's face (doc or selfie)
|
|
134
|
+
null, // No local video provided (we're reusing the old one)
|
|
135
|
+
'John Doe',
|
|
136
|
+
processVideoRequestId, // From the /process-video response headers
|
|
137
|
+
true // usePrefilledVideo
|
|
138
|
+
);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 6. Sessions
|
|
142
|
+
Session is a way to group transactions together. It is useful when you want to group transactions that are related to each other. For example, you can start a session and then use the session ID to link transactions within a unified group.
|
|
143
|
+
```javascript
|
|
144
|
+
const sessionId = await sdk.startSession();
|
|
145
|
+
|
|
146
|
+
const videoFile = new File([/* file parts */], 'video.mp4', { type: 'video/mp4' });
|
|
147
|
+
const phrase = "one two three four five six";
|
|
148
|
+
const userFullName = 'John Doe';
|
|
149
|
+
|
|
150
|
+
await sdk.giveAuthorizationConsent(true, userFullName, { sessionId });
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const response = await sdk.processVideo(videoFile, phrase, userFullName, { sessionId });
|
|
154
|
+
console.log('Process Video Response:', response);
|
|
155
|
+
|
|
156
|
+
// Retrieve the processVideoRequestId from the *response headers* called x-request-id.
|
|
89
157
|
} catch (error) {
|
|
90
158
|
console.error('Error processing video:', error);
|
|
91
159
|
}
|
|
92
160
|
```
|
|
161
|
+
|
|
93
162
|
## Advanced Usage And Best Practices
|
|
94
163
|
### Typical FaceMatch Flow
|
|
95
|
-
One common advanced scenario involves document authentication in
|
|
96
|
-
1. Face
|
|
164
|
+
One common advanced scenario involves document authentication in enrollment face and face matching:
|
|
165
|
+
1. Face Enrollment: Capture the user’s live face or the user uploads a picture of their identity document (front side with the face)
|
|
97
166
|
2. Process Video: Capture the user’s live face
|
|
98
167
|
3. Face Match: Compare the extracted face from the document with the user’s live face to verify identity.
|
|
99
168
|
|
|
@@ -102,13 +171,13 @@ Below is a possible flow (method names in your SDK may vary slightly depending o
|
|
|
102
171
|
// 1. Acquire user consent
|
|
103
172
|
await sdk.giveConsent(true, userFullName);
|
|
104
173
|
|
|
105
|
-
// 2.
|
|
106
|
-
// (Either using
|
|
174
|
+
// 2. Enroll or capture the user’s face
|
|
175
|
+
// (Either using enrollFace or processVideo, depending on your user flow)
|
|
107
176
|
const userFaceFile = new File([/* user selfie bytes */], 'image.jpg', { type: 'image/jpeg' });
|
|
108
177
|
const userVideoFile = new File([/* user selfie bytes */], 'video.mp4', { type: 'video/*' });
|
|
109
|
-
const
|
|
178
|
+
const enrollResponse = await sdk.enrollFace(userFaceFile, userFullName);
|
|
110
179
|
|
|
111
|
-
// 3. Face Match (Compare video face with user’s
|
|
180
|
+
// 3. Face Match (Compare video face with user’s enrolled face)
|
|
112
181
|
const faceMatchResponse = await sdk.faceMatch(
|
|
113
182
|
userFaceFile,
|
|
114
183
|
userVideoFile,
|
|
@@ -123,27 +192,13 @@ Below is a possible flow (method names in your SDK may vary slightly depending o
|
|
|
123
192
|
}
|
|
124
193
|
```
|
|
125
194
|
|
|
126
|
-
### Use Cases with processVideoRequestId and usePrefilledVideo
|
|
127
|
-
- `processVideoRequestId`: After calling `sdk.processVideo()`, you typically receive a unique ID (`x-request-id`). You can pass this `processVideoRequestId` into subsequent calls (e.g., `faceMatch`) to reference the previously uploaded video frames.
|
|
128
|
-
- `usePrefilledVideo`: When set to `true`, indicates that the SDK should reuse the video already on file from a previous `processVideo` call rather than requiring a new upload.
|
|
129
|
-
Example:
|
|
130
|
-
```javascript
|
|
131
|
-
const { x-request-id } = await sdk.processVideo(videoFile, phrase, userFullName);
|
|
132
|
-
|
|
133
|
-
// Later on, we can reuse that video for face match or advanced checks
|
|
134
|
-
const faceMatchResp = await sdk.faceMatch(null, null, userFullName, {
|
|
135
|
-
processVideoRequestId: requestId,
|
|
136
|
-
usePrefilledVideo: true
|
|
137
|
-
});
|
|
138
|
-
```
|
|
139
|
-
Here, `faceMatch` might not require new face data if it can extract frames from the previously uploaded video.
|
|
140
195
|
### Error Handling
|
|
141
196
|
All SDK calls can throw errors for various reasons:
|
|
142
197
|
- Network/Connection Issues
|
|
143
198
|
- Invalid File Types
|
|
144
|
-
- No Face Detected (Face
|
|
145
|
-
- No Speech Detected (Voice
|
|
146
|
-
- Multiple Faces Detected (Face
|
|
199
|
+
- No Face Detected (Face Enrollment)
|
|
200
|
+
- No Speech Detected (Voice Enrollment)
|
|
201
|
+
- Multiple Faces Detected (Face Enrollment)
|
|
147
202
|
- Liveness Check Failure (Process Video)
|
|
148
203
|
|
|
149
204
|
Always wrap calls in try/catch and provide user-friendly messages or fallback logic.
|
|
@@ -166,9 +221,6 @@ Always wrap calls in try/catch and provide user-friendly messages or fallback lo
|
|
|
166
221
|
In addition to direct SDK methods, the Biometry Web SDK offers reusable Web Components that handle user interactions (camera, video recording, error states) automatically.
|
|
167
222
|
The Biometry Web SDK includes reusable, customizable web components for crucial features. These components are easy to embed into your application and handle the most common biometric operations with minimal setup.
|
|
168
223
|
|
|
169
|
-
### Face Onboarding Component
|
|
170
|
-
This component provides an intuitive interface for onboarding users with their cameras. It integrates directly with the `BiometrySDK backend`, managing camera capture, consent checks, and error handling.
|
|
171
|
-
|
|
172
224
|
### Integration
|
|
173
225
|
**Option 1: Using npm (Recommended for full SDK usage)**
|
|
174
226
|
1. Install the SDK package via **npm**:
|
|
@@ -180,26 +232,14 @@ This component provides an intuitive interface for onboarding users with their c
|
|
|
180
232
|
// index.js
|
|
181
233
|
import './node_modules/biometry-sdk/dist/biometry-sdk.esm.js';
|
|
182
234
|
```
|
|
183
|
-
3. Connect the script to your **HTML file** and use the component:
|
|
184
|
-
```html
|
|
185
|
-
<script type="module" src="./index.js"></script>
|
|
186
|
-
|
|
187
|
-
<biometry-onboarding
|
|
188
|
-
api-key="your-api-key"
|
|
189
|
-
user-fullname="John Doe">
|
|
190
|
-
</biometry-onboarding>
|
|
191
|
-
```
|
|
192
|
-
|
|
193
235
|
**Option 2: Using CDN (Quick Integration)**
|
|
194
236
|
```html
|
|
195
237
|
<script type="module" src="https://cdn.jsdelivr.net/npm/biometry-sdk/dist/biometry-sdk.esm.js"></script>
|
|
196
|
-
|
|
197
|
-
<biometry-onboarding
|
|
198
|
-
api-key="your-api-key"
|
|
199
|
-
user-fullname="John Doe">
|
|
200
|
-
</biometry-onboarding>
|
|
201
238
|
```
|
|
202
239
|
|
|
240
|
+
### Face Enrollment Component
|
|
241
|
+
This component provides an intuitive interface for enrollment users with their cameras. It integrates directly with the `BiometrySDK backend`, managing camera capture, consent checks, and error handling.
|
|
242
|
+
|
|
203
243
|
### Usage
|
|
204
244
|
**Required attributes:**
|
|
205
245
|
- `api-key`: Your Biometry API key.
|
|
@@ -212,15 +252,15 @@ This component provides an intuitive interface for onboarding users with their c
|
|
|
212
252
|
|
|
213
253
|
**Basic Usage**
|
|
214
254
|
```html
|
|
215
|
-
<biometry-
|
|
255
|
+
<biometry-enrollment
|
|
216
256
|
api-key="your-api-key"
|
|
217
257
|
user-fullname="John Doe">
|
|
218
|
-
</biometry-
|
|
258
|
+
</biometry-enrollment>
|
|
219
259
|
```
|
|
220
260
|
|
|
221
261
|
**Advanced Usage**
|
|
222
262
|
```html
|
|
223
|
-
<biometry-
|
|
263
|
+
<biometry-enrollment
|
|
224
264
|
api-key="your-api-key"
|
|
225
265
|
user-fullname="John Doe">
|
|
226
266
|
|
|
@@ -229,41 +269,17 @@ This component provides an intuitive interface for onboarding users with their c
|
|
|
229
269
|
|
|
230
270
|
<!-- Custom Status Messages -->
|
|
231
271
|
<div slot="loading">Please wait while we process your photo...</div>
|
|
232
|
-
<div slot="success">Congratulations! You have been
|
|
272
|
+
<div slot="success">Congratulations! You have been enrolled.</div>
|
|
233
273
|
<div slot="error-no-face">No face detected. Make sure your face is visible.</div>
|
|
234
274
|
<div slot="error-multiple-faces">Multiple faces detected. Please try again alone.</div>
|
|
235
275
|
<div slot="error-not-centered">Align your face with the center of the screen.</div>
|
|
236
276
|
<div slot="error-other">Oops! Something went wrong. Please try again.</div>
|
|
237
|
-
</biometry-
|
|
277
|
+
</biometry-enrollment>
|
|
238
278
|
```
|
|
239
279
|
|
|
240
280
|
### Process Video Component
|
|
241
281
|
The **Process Video** component enables you to record, upload, and process a video within your application. It integrates with Biometry's services to check liveness and authorize the user.
|
|
242
282
|
|
|
243
|
-
### Integration
|
|
244
|
-
**Option 1: Install via npm**
|
|
245
|
-
1. To include the component in your project, install the biometry-sdk package:
|
|
246
|
-
```bash
|
|
247
|
-
npm install biometry-sdk
|
|
248
|
-
```
|
|
249
|
-
2. After installation, import the component into your project:
|
|
250
|
-
```javascript
|
|
251
|
-
// index.js
|
|
252
|
-
import './node_modules/biometry-sdk/dist/biometry-sdk.esm.js';
|
|
253
|
-
```
|
|
254
|
-
3. Include the component in your HTML:
|
|
255
|
-
You can skip the npm installation and include the component directly in your HTML:
|
|
256
|
-
```html
|
|
257
|
-
<script type="module" src="./index.js"></script>
|
|
258
|
-
|
|
259
|
-
<process-video ...></process-video>
|
|
260
|
-
```
|
|
261
|
-
**Option 2: Using CDN (Quick Integration)**
|
|
262
|
-
```html
|
|
263
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/biometry-sdk/dist/biometry-sdk.esm.js"></script>
|
|
264
|
-
|
|
265
|
-
<process-video ...></process-video>
|
|
266
|
-
```
|
|
267
283
|
### Usage
|
|
268
284
|
**Basic Usage**
|
|
269
285
|
```html
|
|
@@ -315,36 +331,47 @@ For more detailed information on Biometry’s API endpoints, parameters, and res
|
|
|
315
331
|
```bash
|
|
316
332
|
npm install biometry-sdk
|
|
317
333
|
```
|
|
318
|
-
- **Consent**: (Required before
|
|
334
|
+
- **Consent**: (Required before enrollment/processing)
|
|
319
335
|
```javascript
|
|
320
336
|
sdk.giveConsent(true, userFullName)
|
|
321
337
|
```
|
|
322
|
-
- **Voice
|
|
338
|
+
- **Voice Enrollment**:
|
|
339
|
+
```javascript
|
|
340
|
+
sdk.enrollVoice(file, userFullName)
|
|
341
|
+
```
|
|
342
|
+
- **Face Enrollment**:
|
|
323
343
|
```javascript
|
|
324
|
-
sdk.
|
|
344
|
+
sdk.enrollFace(file, userFullName)
|
|
325
345
|
```
|
|
326
|
-
- **Face
|
|
346
|
+
- **Face match (basic):**
|
|
327
347
|
```javascript
|
|
328
|
-
sdk.
|
|
348
|
+
sdk.faceMatch(image, video, userFullName);
|
|
329
349
|
```
|
|
330
|
-
- Face match
|
|
350
|
+
- **Face match (advanced w/ reusing video or linking IDs):**
|
|
331
351
|
```javascript
|
|
332
|
-
sdk.faceMatch(
|
|
352
|
+
sdk.faceMatch(
|
|
353
|
+
image, // Reference image file that contains user's face.
|
|
354
|
+
video, // Video file that contains user's face.
|
|
355
|
+
userFullName,
|
|
356
|
+
processVideoRequestId, // ID from the response header of /process-video endpoint.
|
|
357
|
+
usePrefilledVideo // Pass true to use the video from the process-video endpoint.
|
|
358
|
+
);
|
|
333
359
|
```
|
|
334
|
-
- Process Video (basic)
|
|
360
|
+
- **Process Video (basic):**
|
|
335
361
|
```javascript
|
|
336
362
|
sdk.processVideo(file, phrase, userFullName);
|
|
337
363
|
```
|
|
338
|
-
- Process Video (advanced w/ reusing video or linking IDs)
|
|
364
|
+
- **Process Video (advanced w/ reusing video or linking IDs):**
|
|
339
365
|
```javascript
|
|
340
|
-
sdk.processVideo(
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
366
|
+
sdk.processVideo(
|
|
367
|
+
video, // Video file that you want to process
|
|
368
|
+
phrase,
|
|
369
|
+
userFullName,
|
|
370
|
+
requestUserProvidedId // An optional user-provided ID to link transactions within a unified group
|
|
371
|
+
);
|
|
345
372
|
```
|
|
346
|
-
- UI Components
|
|
347
|
-
- `<biometry-
|
|
348
|
-
- `<process-video ...>` (video
|
|
349
|
-
With these **direct SDK methods**, **UI components**, and advanced **best practices** (
|
|
350
|
-
|
|
373
|
+
- **UI Components:**
|
|
374
|
+
- `<biometry-enrollment ...>` (face enrollment)
|
|
375
|
+
- `<process-video ...>` (video enrollment)
|
|
376
|
+
With these **direct SDK methods**, **UI components**, and advanced **best practices** (faceEnroll + faceMatch flows, reuse of video, error handling), you can build robust, privacy-conscious biometric solutions on your web application.
|
|
377
|
+
|
|
@@ -1,26 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
private readonly shadow;
|
|
3
|
-
private sdk;
|
|
4
|
-
private videoElement;
|
|
5
|
-
private canvasElement;
|
|
6
|
-
private captureButton;
|
|
7
|
-
private resultCode?;
|
|
8
|
-
private description?;
|
|
9
|
-
constructor();
|
|
10
|
-
static get observedAttributes(): string[];
|
|
11
|
-
get apiKey(): string | null;
|
|
12
|
-
set apiKey(value: string | null);
|
|
13
|
-
get userFullname(): string | null;
|
|
14
|
-
set userFullname(value: string | null);
|
|
15
|
-
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
16
|
-
connectedCallback(): void;
|
|
17
|
-
disconnectedCallback(): void;
|
|
18
|
-
validateAttributes(): void;
|
|
19
|
-
init(): void;
|
|
20
|
-
private cleanup;
|
|
21
|
-
private initializeSDK;
|
|
22
|
-
private toggleState;
|
|
23
|
-
private attachSlotListeners;
|
|
24
|
-
private setupCamera;
|
|
25
|
-
private capturePhoto;
|
|
26
|
-
}
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { BiometrySDK } from "../sdk.js";
|
|
2
|
+
import { BiometryAttributes, BiometryOnboardingState } from "../types.js";
|
|
3
|
+
class BiometryOnboarding extends HTMLElement {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this.videoElement = null;
|
|
7
|
+
this.canvasElement = null;
|
|
8
|
+
this.captureButton = null;
|
|
9
|
+
this.shadow = this.attachShadow({ mode: "open" });
|
|
10
|
+
this.sdk = null;
|
|
11
|
+
this.toggleState = this.toggleState.bind(this);
|
|
12
|
+
this.capturePhoto = this.capturePhoto.bind(this);
|
|
13
|
+
}
|
|
14
|
+
static get observedAttributes() {
|
|
15
|
+
return Object.values(BiometryAttributes);
|
|
16
|
+
}
|
|
17
|
+
get apiKey() {
|
|
18
|
+
return this.getAttribute("api-key");
|
|
19
|
+
}
|
|
20
|
+
set apiKey(value) {
|
|
21
|
+
if (value) {
|
|
22
|
+
this.setAttribute("api-key", value);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
this.removeAttribute("api-key");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
get userFullname() {
|
|
29
|
+
return this.getAttribute("user-fullname");
|
|
30
|
+
}
|
|
31
|
+
set userFullname(value) {
|
|
32
|
+
if (value) {
|
|
33
|
+
this.setAttribute("user-fullname", value);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
this.removeAttribute("user-fullname");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
40
|
+
if (name === "api-key" || name === "user-fullname") {
|
|
41
|
+
this.validateAttributes();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
connectedCallback() {
|
|
45
|
+
this.validateAttributes();
|
|
46
|
+
this.init();
|
|
47
|
+
}
|
|
48
|
+
disconnectedCallback() {
|
|
49
|
+
this.cleanup();
|
|
50
|
+
}
|
|
51
|
+
validateAttributes() {
|
|
52
|
+
if (!this.apiKey) {
|
|
53
|
+
console.error("API key is required.");
|
|
54
|
+
this.toggleState(BiometryOnboardingState.ErrorOther);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!this.userFullname) {
|
|
58
|
+
console.error("User fullname is required.");
|
|
59
|
+
this.toggleState(BiometryOnboardingState.ErrorOther);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
init() {
|
|
64
|
+
this.shadow.innerHTML = `
|
|
65
|
+
<style>
|
|
66
|
+
.wrapper {
|
|
67
|
+
position: relative;
|
|
68
|
+
}
|
|
69
|
+
video {
|
|
70
|
+
transform: scaleX(-1); /* Flip video for preview */
|
|
71
|
+
max-width: 100%;
|
|
72
|
+
border-radius: var(--border-radius, 8px);
|
|
73
|
+
}
|
|
74
|
+
canvas {
|
|
75
|
+
display: none;
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
78
|
+
<div class="wrapper">
|
|
79
|
+
<slot name="video">
|
|
80
|
+
<video id="video" autoplay playsinline></video>
|
|
81
|
+
</slot>
|
|
82
|
+
<slot name="canvas">
|
|
83
|
+
<canvas id="canvas" style="display: none;"></canvas>
|
|
84
|
+
</slot>
|
|
85
|
+
<slot name="button">
|
|
86
|
+
<button id="button">Capture Photo</button>
|
|
87
|
+
</slot>
|
|
88
|
+
<div class="status">
|
|
89
|
+
<slot name="loading" class="loading"></slot>
|
|
90
|
+
<slot name="success" class="success"></slot>
|
|
91
|
+
<slot name="error-no-face" class="error-no-face"></slot>
|
|
92
|
+
<slot name="error-multiple-faces" class="error-multiple-faces"></slot>
|
|
93
|
+
<slot name="error-not-centered" class="error-not-centered"></slot>
|
|
94
|
+
<slot name="error-other" class="error-other"></slot>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
`;
|
|
98
|
+
this.initializeSDK();
|
|
99
|
+
this.attachSlotListeners();
|
|
100
|
+
this.setupCamera();
|
|
101
|
+
this.toggleState("");
|
|
102
|
+
}
|
|
103
|
+
cleanup() {
|
|
104
|
+
var _a;
|
|
105
|
+
if ((_a = this.videoElement) === null || _a === void 0 ? void 0 : _a.srcObject) {
|
|
106
|
+
const tracks = this.videoElement.srcObject.getTracks();
|
|
107
|
+
tracks.forEach((track) => track.stop());
|
|
108
|
+
}
|
|
109
|
+
if (this.videoElement) {
|
|
110
|
+
this.videoElement.srcObject = null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
initializeSDK() {
|
|
114
|
+
if (this.apiKey) {
|
|
115
|
+
this.sdk = new BiometrySDK(this.apiKey);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
this.toggleState(BiometryOnboardingState.ErrorOther);
|
|
119
|
+
console.error("API key is required to initialize the SDK.");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
toggleState(state) {
|
|
123
|
+
const slots = [
|
|
124
|
+
BiometryOnboardingState.Loading,
|
|
125
|
+
BiometryOnboardingState.Success,
|
|
126
|
+
BiometryOnboardingState.ErrorNoFace,
|
|
127
|
+
BiometryOnboardingState.ErrorMultipleFaces,
|
|
128
|
+
BiometryOnboardingState.ErrorNotCentered,
|
|
129
|
+
BiometryOnboardingState.ErrorOther,
|
|
130
|
+
];
|
|
131
|
+
slots.forEach((slotName) => {
|
|
132
|
+
const slot = this.shadow.querySelector(`slot[name="${slotName}"]`);
|
|
133
|
+
if (slot) {
|
|
134
|
+
slot.style.display = slotName === state ? "block" : "none";
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
attachSlotListeners() {
|
|
139
|
+
const videoSlot = this.shadow.querySelector('slot[name="video"]');
|
|
140
|
+
const canvasSlot = this.shadow.querySelector('slot[name="canvas"]');
|
|
141
|
+
const buttonSlot = this.shadow.querySelector('slot[name="button"]');
|
|
142
|
+
const assignedVideoElements = videoSlot.assignedElements();
|
|
143
|
+
this.videoElement = (assignedVideoElements.length > 0 ? assignedVideoElements[0] : null) || this.shadow.querySelector("#video");
|
|
144
|
+
const assignedCanvasElements = canvasSlot.assignedElements();
|
|
145
|
+
this.canvasElement = (assignedCanvasElements.length > 0 ? assignedCanvasElements[0] : null) || this.shadow.querySelector("#canvas");
|
|
146
|
+
const assignedButtonElements = buttonSlot.assignedElements();
|
|
147
|
+
this.captureButton = (assignedButtonElements.length > 0 ? assignedButtonElements[0] : null) || this.shadow.querySelector("#button");
|
|
148
|
+
if (!this.videoElement) {
|
|
149
|
+
console.error("Video element is missing.");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (!this.captureButton) {
|
|
153
|
+
console.error("Capture button is missing.");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this.captureButton.addEventListener("click", this.capturePhoto);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
setupCamera() {
|
|
161
|
+
if (!this.videoElement) {
|
|
162
|
+
console.error("Video element is missing.");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
navigator.mediaDevices
|
|
166
|
+
.getUserMedia({ video: true })
|
|
167
|
+
.then((stream) => {
|
|
168
|
+
this.videoElement.srcObject = stream;
|
|
169
|
+
})
|
|
170
|
+
.catch((error) => {
|
|
171
|
+
console.error("Error accessing camera:", error);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
async capturePhoto() {
|
|
175
|
+
try {
|
|
176
|
+
if (!this.videoElement || !this.canvasElement || !this.sdk) {
|
|
177
|
+
console.error("Essential elements or SDK are not initialized.");
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const context = this.canvasElement.getContext("2d");
|
|
181
|
+
this.canvasElement.width = this.videoElement.videoWidth;
|
|
182
|
+
this.canvasElement.height = this.videoElement.videoHeight;
|
|
183
|
+
context.drawImage(this.videoElement, 0, 0, this.canvasElement.width, this.canvasElement.height);
|
|
184
|
+
this.toggleState("loading");
|
|
185
|
+
this.canvasElement.toBlob(async (blob) => {
|
|
186
|
+
try {
|
|
187
|
+
if (!blob) {
|
|
188
|
+
console.error("Failed to capture photo.");
|
|
189
|
+
this.toggleState(BiometryOnboardingState.ErrorOther);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const file = new File([blob], "onboard-face.jpg", { type: "image/jpeg" });
|
|
193
|
+
try {
|
|
194
|
+
const response = await this.sdk.onboardFace(file, this.userFullname);
|
|
195
|
+
const result = response.data.onboard_result;
|
|
196
|
+
this.resultCode = result === null || result === void 0 ? void 0 : result.code;
|
|
197
|
+
this.description = (result === null || result === void 0 ? void 0 : result.description) || "Unknown error occurred.";
|
|
198
|
+
switch (this.resultCode) {
|
|
199
|
+
case 0:
|
|
200
|
+
this.toggleState(BiometryOnboardingState.Success);
|
|
201
|
+
break;
|
|
202
|
+
case 1:
|
|
203
|
+
this.toggleState(BiometryOnboardingState.ErrorNoFace);
|
|
204
|
+
break;
|
|
205
|
+
case 2:
|
|
206
|
+
this.toggleState(BiometryOnboardingState.ErrorMultipleFaces);
|
|
207
|
+
break;
|
|
208
|
+
case 3:
|
|
209
|
+
this.toggleState(BiometryOnboardingState.ErrorNotCentered);
|
|
210
|
+
break;
|
|
211
|
+
default:
|
|
212
|
+
this.toggleState(BiometryOnboardingState.ErrorOther);
|
|
213
|
+
}
|
|
214
|
+
console.log("Onboarding result:", result);
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
console.error("Error onboarding face:", error);
|
|
218
|
+
this.toggleState(BiometryOnboardingState.ErrorOther);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
console.error("Error in toBlob callback:", error);
|
|
223
|
+
this.toggleState(BiometryOnboardingState.ErrorOther);
|
|
224
|
+
}
|
|
225
|
+
}, "image/jpeg");
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
console.error("Error capturing photo:", error);
|
|
229
|
+
this.toggleState(BiometryOnboardingState.ErrorOther);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
customElements.define("biometry-onboarding", BiometryOnboarding);
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
ADDED