biometry-sdk 1.0.5 → 1.1.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 +53 -8
- package/dist/components/biometry-onboarding.d.ts +1 -0
- package/dist/components/biometry-onboarding.js +233 -0
- package/dist/sdk.js +3 -2
- package/dist/types.d.ts +46 -23
- package/dist/types.js +14 -1
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# biometry-web-sdk
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
|
-
The
|
|
4
|
+
The **Biometry Web SDK** is a software development kit (SDK) designed to facilitate the integration of Biometry's API services.
|
|
5
5
|
|
|
6
6
|
## Features
|
|
7
|
-
- Consent management
|
|
8
|
-
- Voice onboarding
|
|
9
|
-
- Face onboarding
|
|
10
|
-
- Face
|
|
11
|
-
-
|
|
7
|
+
- **Consent management**: Ask a permission to store their biometric data for authentication using Biometry.
|
|
8
|
+
- **Voice onboarding**: Onboard voice for Voice Recognition.
|
|
9
|
+
- **Face onboarding**: Onboard face for face recognition.
|
|
10
|
+
- Includes a customizable **Face Onboarding UI Component** for streamlined user interactions.
|
|
11
|
+
- **Face match**: Compares extracted image from user’s personal document with the frame from the `/process-video.`
|
|
12
|
+
- **Process video**: Process the video through Biometry services to check liveness and authorize user.
|
|
13
|
+
- (UI Component for this feature coming soon)
|
|
12
14
|
|
|
13
15
|
## Installation
|
|
14
16
|
```bash
|
|
@@ -33,10 +35,53 @@ console.log(response);
|
|
|
33
35
|
|
|
34
36
|
You can find an example in the example/ directory. The example demonstrates how to integrate the SDK into a React app.
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
## UI Components
|
|
39
|
+
The **Biometry Web SDK** includes reusable and customizable web components for key features. These components make it simple to add biometric functionalities to your application.
|
|
40
|
+
|
|
41
|
+
### Face Onboarding Component
|
|
42
|
+
The `Face Onboarding` component provides an intuitive interface for onboarding users with their camera. It integrates with the `BiometrySDK` to handle backend communication and error states.
|
|
43
|
+
|
|
44
|
+
#### Usage
|
|
45
|
+
Here's how to integrate the `Face Onboarding` component into your application:
|
|
46
|
+
|
|
47
|
+
**Import in your html component:**
|
|
48
|
+
```html
|
|
49
|
+
<script type="module" src="node_modules/biometry-sdk/dist/components/biometry-onboarding.js"></script>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Basic Usage**
|
|
53
|
+
```html
|
|
54
|
+
<biometry-onboarding
|
|
55
|
+
api-key="your-api-key"
|
|
56
|
+
user-fullname="John Doe">
|
|
57
|
+
</biometry-onboarding>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Advanced Usage**
|
|
61
|
+
```html
|
|
62
|
+
<biometry-onboarding
|
|
63
|
+
api-key="your-api-key"
|
|
64
|
+
user-fullname="Jane Doe">
|
|
65
|
+
|
|
66
|
+
<video slot="video" autoplay playsinline style="width: 100%; border-radius: 10px;"></video>
|
|
67
|
+
<button slot="button" style="padding: 10px 20px; font-size: 16px;">Capture</button>
|
|
68
|
+
|
|
69
|
+
<!-- Custom Status Messages -->
|
|
70
|
+
<div slot="loading">Please wait while we process your photo...</div>
|
|
71
|
+
<div slot="success">Congratulations! You have been onboarded.</div>
|
|
72
|
+
<div slot="error-no-face">No face detected. Make sure your face is visible.</div>
|
|
73
|
+
<div slot="error-multiple-faces">Multiple faces detected. Please try again alone.</div>
|
|
74
|
+
<div slot="error-not-centered">Align your face with the center of the screen.</div>
|
|
75
|
+
<div slot="error-other">Oops! Something went wrong. Please try again.</div>
|
|
76
|
+
</biometry-onboarding>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Process Video Component
|
|
80
|
+
|
|
81
|
+
## License
|
|
37
82
|
|
|
38
83
|
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
|
|
39
84
|
|
|
40
|
-
|
|
85
|
+
## More Information
|
|
41
86
|
|
|
42
87
|
For more detailed documentation on the Biometry API, visit the [official documentation](https://developer.biometrysolutions.com/overview/).
|
|
@@ -0,0 +1 @@
|
|
|
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/sdk.js
CHANGED
|
@@ -20,8 +20,9 @@ export class BiometrySDK {
|
|
|
20
20
|
body,
|
|
21
21
|
});
|
|
22
22
|
if (!response.ok) {
|
|
23
|
-
const
|
|
24
|
-
|
|
23
|
+
const errorData = await response.json().catch(() => ({}));
|
|
24
|
+
const errorMessage = (errorData === null || errorData === void 0 ? void 0 : errorData.error) || (errorData === null || errorData === void 0 ? void 0 : errorData.message) || 'Unknown error occurred';
|
|
25
|
+
throw new Error(`Error ${response.status}: ${errorMessage}`);
|
|
25
26
|
}
|
|
26
27
|
return await response.json();
|
|
27
28
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
export declare enum BiometryAttributes {
|
|
2
|
+
ApiKey = "api-key",
|
|
3
|
+
UserFullname = "user-fullname"
|
|
4
|
+
}
|
|
5
|
+
export declare enum BiometryOnboardingState {
|
|
6
|
+
Loading = "loading",
|
|
7
|
+
Success = "success",
|
|
8
|
+
ErrorNoFace = "error-no-face",
|
|
9
|
+
ErrorMultipleFaces = "error-multiple-faces",
|
|
10
|
+
ErrorNotCentered = "error-not-centered",
|
|
11
|
+
ErrorOther = "error-other"
|
|
12
|
+
}
|
|
1
13
|
export interface ConsentResponse {
|
|
2
14
|
is_consent_given: boolean;
|
|
3
15
|
user_fullname: string;
|
|
@@ -5,31 +17,41 @@ export interface ConsentResponse {
|
|
|
5
17
|
export interface VoiceOnboardingResponse {
|
|
6
18
|
status: "good" | "qafailed" | "enrolled";
|
|
7
19
|
}
|
|
20
|
+
type Base64String = string & {
|
|
21
|
+
readonly __brand: unique symbol;
|
|
22
|
+
};
|
|
23
|
+
export interface DocAuthInfo {
|
|
24
|
+
document_type: string;
|
|
25
|
+
country_code: string;
|
|
26
|
+
nationality_code: string;
|
|
27
|
+
nationality_name: string;
|
|
28
|
+
sex: string;
|
|
29
|
+
first_name: string;
|
|
30
|
+
father_name: string;
|
|
31
|
+
last_name: string;
|
|
32
|
+
expiry_date: string;
|
|
33
|
+
document_number: string;
|
|
34
|
+
birth_date: string;
|
|
35
|
+
portrait_photo: Base64String;
|
|
36
|
+
signature: Base64String;
|
|
37
|
+
document_category: string;
|
|
38
|
+
issuing_state: string;
|
|
39
|
+
front_document_type_id: string;
|
|
40
|
+
contains_rfid: boolean;
|
|
41
|
+
errors?: string[];
|
|
42
|
+
}
|
|
8
43
|
export interface FaceOnboardingResponse {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
};
|
|
13
|
-
document_auth: {
|
|
14
|
-
document_type: string;
|
|
15
|
-
country_code: string;
|
|
16
|
-
nationality_code: string;
|
|
17
|
-
nationality_name: string;
|
|
18
|
-
sex: string;
|
|
19
|
-
first_name: string;
|
|
20
|
-
father_name: string;
|
|
21
|
-
last_name: string;
|
|
22
|
-
expiry_date: string;
|
|
23
|
-
document_number: string;
|
|
24
|
-
birth_date: string;
|
|
25
|
-
portrait_photo: string;
|
|
26
|
-
signature: string;
|
|
27
|
-
document_category: string;
|
|
28
|
-
issuing_state: string;
|
|
29
|
-
front_document_type_id: string;
|
|
30
|
-
contains_rfid: boolean;
|
|
44
|
+
data: {
|
|
45
|
+
onboard_result: FaceOnboardingResult;
|
|
46
|
+
document_auth?: DocAuthInfo;
|
|
31
47
|
};
|
|
32
|
-
message
|
|
48
|
+
message?: string;
|
|
49
|
+
error?: string;
|
|
50
|
+
scoring_result?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface FaceOnboardingResult {
|
|
53
|
+
code: number;
|
|
54
|
+
description: string;
|
|
33
55
|
}
|
|
34
56
|
export interface FaceMatchResponse {
|
|
35
57
|
code: number;
|
|
@@ -80,3 +102,4 @@ export interface ProcessVideoResponse {
|
|
|
80
102
|
};
|
|
81
103
|
message: string;
|
|
82
104
|
}
|
|
105
|
+
export {};
|
package/dist/types.js
CHANGED
|
@@ -1 +1,14 @@
|
|
|
1
|
-
export
|
|
1
|
+
export var BiometryAttributes;
|
|
2
|
+
(function (BiometryAttributes) {
|
|
3
|
+
BiometryAttributes["ApiKey"] = "api-key";
|
|
4
|
+
BiometryAttributes["UserFullname"] = "user-fullname";
|
|
5
|
+
})(BiometryAttributes || (BiometryAttributes = {}));
|
|
6
|
+
export var BiometryOnboardingState;
|
|
7
|
+
(function (BiometryOnboardingState) {
|
|
8
|
+
BiometryOnboardingState["Loading"] = "loading";
|
|
9
|
+
BiometryOnboardingState["Success"] = "success";
|
|
10
|
+
BiometryOnboardingState["ErrorNoFace"] = "error-no-face";
|
|
11
|
+
BiometryOnboardingState["ErrorMultipleFaces"] = "error-multiple-faces";
|
|
12
|
+
BiometryOnboardingState["ErrorNotCentered"] = "error-not-centered";
|
|
13
|
+
BiometryOnboardingState["ErrorOther"] = "error-other";
|
|
14
|
+
})(BiometryOnboardingState || (BiometryOnboardingState = {}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "biometry-sdk",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -8,10 +8,14 @@
|
|
|
8
8
|
"README.md"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc"
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "jest"
|
|
12
13
|
},
|
|
13
14
|
"devDependencies": {
|
|
15
|
+
"@types/jest": "^29.5.14",
|
|
16
|
+
"jest": "^29.7.0",
|
|
17
|
+
"ts-jest": "^29.2.5",
|
|
14
18
|
"typescript": "^5.0.0"
|
|
15
19
|
},
|
|
16
20
|
"license": "MIT"
|
|
17
|
-
}
|
|
21
|
+
}
|