@kwik-id/sdk-node 0.1.0-alpha.3
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 +308 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +125 -0
- package/dist/lib/kwik-node-sdk.d.ts +15 -0
- package/dist/lib/kwik-node-sdk.d.ts.map +1 -0
- package/dist/lib/webhook-verifier.d.ts +3 -0
- package/dist/lib/webhook-verifier.d.ts.map +1 -0
- package/dist/package.json +28 -0
- package/dist/types/index.d.ts +135 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# @kwik-id/sdk-node
|
|
2
|
+
|
|
3
|
+
Server-side Node.js SDK for [KwikID](https://kwik-id.com) identity verification. Create verification sessions, retrieve results, and verify webhook signatures.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @kwik-id/sdk-node
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @kwik-id/sdk-node
|
|
11
|
+
# or
|
|
12
|
+
yarn add @kwik-id/sdk-node
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Requirements:** Node.js 18+
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { KwikIDNode } from "@kwik-id/sdk-node";
|
|
21
|
+
|
|
22
|
+
const kwikId = new KwikIDNode({
|
|
23
|
+
apiKey: process.env.KWIKID_API_KEY!,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// 1. Create a session (returns a clientSecret for the frontend SDK)
|
|
27
|
+
const session = await kwikId.createSession({
|
|
28
|
+
referenceId: "user_123",
|
|
29
|
+
userName: "Jane Doe",
|
|
30
|
+
userEmail: "jane@example.com",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// 2. Pass session.token to your frontend as the clientSecret
|
|
34
|
+
// The frontend SDK handles the verification flow
|
|
35
|
+
|
|
36
|
+
// 3. Later, retrieve the verification result
|
|
37
|
+
const result = await kwikId.getVerificationResult(session.jobId);
|
|
38
|
+
console.log(result.status); // "verified" | "rejected" | "pending"
|
|
39
|
+
console.log(result.scores); // { overall, liveness, document, faceMatch }
|
|
40
|
+
console.log(result.extractedData); // { firstName, lastName, dateOfBirth, ... }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const kwikId = new KwikIDNode({
|
|
47
|
+
apiKey: "your-api-key", // Required — from KwikID dashboard
|
|
48
|
+
baseUrl: "https://api.kwik-id.com", // Optional — defaults to production
|
|
49
|
+
timeout: 30000, // Optional — request timeout in ms (default: 30s)
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## API Reference
|
|
54
|
+
|
|
55
|
+
### Sessions
|
|
56
|
+
|
|
57
|
+
#### `createSession(options?)`
|
|
58
|
+
|
|
59
|
+
Creates a new verification session. Returns a `SessionToken` containing the `token` (client secret) to pass to the frontend SDK.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const session = await kwikId.createSession({
|
|
63
|
+
referenceId: "user_123", // Optional — your internal user/order ID
|
|
64
|
+
userName: "Jane Doe", // Optional — pre-fill user name
|
|
65
|
+
userEmail: "jane@example.com", // Optional — pre-fill user email
|
|
66
|
+
metadata: { plan: "premium" }, // Optional — arbitrary metadata
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// session.sessionId — unique session identifier
|
|
70
|
+
// session.jobId — verification job ID
|
|
71
|
+
// session.token — client secret for frontend SDK
|
|
72
|
+
// session.expiresAt — ISO 8601 expiration timestamp
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### `getSession(sessionId)`
|
|
76
|
+
|
|
77
|
+
Retrieves session details.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const details = await kwikId.getSession(session.sessionId);
|
|
81
|
+
// details.isUsed — whether the session has been consumed
|
|
82
|
+
// details.expiresAt — expiration timestamp
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Verifications
|
|
86
|
+
|
|
87
|
+
#### `listVerifications(options?)`
|
|
88
|
+
|
|
89
|
+
Lists verification jobs with pagination and filtering.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const list = await kwikId.listVerifications({
|
|
93
|
+
status: "verified", // Filter by status
|
|
94
|
+
referenceId: "user_123", // Filter by your reference ID
|
|
95
|
+
page: 1, // Pagination
|
|
96
|
+
limit: 20,
|
|
97
|
+
sortBy: "createdAt", // "createdAt" | "updatedAt" | "status"
|
|
98
|
+
sortOrder: "desc", // "asc" | "desc"
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// list.verifications — array of VerificationSummary
|
|
102
|
+
// list.pagination — { total, page, limit, totalPages }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### `getVerification(id)`
|
|
106
|
+
|
|
107
|
+
Gets full details of a single verification.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const verification = await kwikId.getVerification(jobId);
|
|
111
|
+
// verification.status — "pending" | "verified" | "rejected" | ...
|
|
112
|
+
// verification.documentType — "passport" | "id_card" | "drivers_license" | ...
|
|
113
|
+
// verification.userName
|
|
114
|
+
// verification.completedAt
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### `getVerificationByReference(referenceId)`
|
|
118
|
+
|
|
119
|
+
Looks up a verification by your reference ID.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
const verification = await kwikId.getVerificationByReference("user_123");
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### `getVerificationResult(id)`
|
|
126
|
+
|
|
127
|
+
Gets the detailed result including scores and extracted document data.
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const result = await kwikId.getVerificationResult(jobId);
|
|
131
|
+
|
|
132
|
+
// Scores (0–1 scale)
|
|
133
|
+
result.scores?.overall; // Combined score
|
|
134
|
+
result.scores?.liveness; // Liveness detection score
|
|
135
|
+
result.scores?.document; // Document authenticity score
|
|
136
|
+
result.scores?.faceMatch; // Face match score
|
|
137
|
+
|
|
138
|
+
// Extracted document data (from OCR)
|
|
139
|
+
result.extractedData?.firstName;
|
|
140
|
+
result.extractedData?.lastName;
|
|
141
|
+
result.extractedData?.dateOfBirth; // YYYY-MM-DD
|
|
142
|
+
result.extractedData?.idNumber; // Document number
|
|
143
|
+
result.extractedData?.expiryDate; // YYYY-MM-DD
|
|
144
|
+
result.extractedData?.documentType;
|
|
145
|
+
result.extractedData?.country;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Webhooks
|
|
149
|
+
|
|
150
|
+
#### `verifyWebhookSignature(options)`
|
|
151
|
+
|
|
152
|
+
Verifies incoming webhook signatures to ensure authenticity. Webhooks are signed using HMAC-SHA256.
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { verifyWebhookSignature } from "@kwik-id/sdk-node";
|
|
156
|
+
|
|
157
|
+
// Express example
|
|
158
|
+
app.post("/webhooks/kwikid", (req, res) => {
|
|
159
|
+
const result = verifyWebhookSignature({
|
|
160
|
+
payload: JSON.stringify(req.body),
|
|
161
|
+
signature: req.headers["kwik-signature"] as string,
|
|
162
|
+
secret: process.env.KWIKID_WEBHOOK_SECRET!,
|
|
163
|
+
toleranceSeconds: 300, // Optional — reject events older than 5min (default)
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (!result.valid) {
|
|
167
|
+
console.error("Invalid webhook:", result.error);
|
|
168
|
+
return res.status(400).send("Invalid signature");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const { event } = result;
|
|
172
|
+
|
|
173
|
+
switch (event.event) {
|
|
174
|
+
case "verification.completed":
|
|
175
|
+
console.log("Verified:", event.data.jobId);
|
|
176
|
+
console.log("Scores:", event.data.scores);
|
|
177
|
+
console.log("Extracted:", event.data.extractedData);
|
|
178
|
+
break;
|
|
179
|
+
case "verification.rejected":
|
|
180
|
+
console.log("Rejected:", event.data.jobId);
|
|
181
|
+
break;
|
|
182
|
+
case "verification.failed":
|
|
183
|
+
console.log("Failed:", event.data.errorMessage);
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
res.sendStatus(200);
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Webhook Events
|
|
192
|
+
|
|
193
|
+
| Event | Description |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `verification.started` | User began the verification flow |
|
|
196
|
+
| `verification.uploaded` | Documents and selfie uploaded |
|
|
197
|
+
| `verification.completed` | Verification passed |
|
|
198
|
+
| `verification.rejected` | Verification rejected (e.g., document mismatch) |
|
|
199
|
+
| `verification.failed` | Verification failed (e.g., processing error) |
|
|
200
|
+
|
|
201
|
+
### Webhook Payload
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
{
|
|
205
|
+
event: "verification.completed",
|
|
206
|
+
timestamp: "2024-01-15T10:30:00.000Z",
|
|
207
|
+
data: {
|
|
208
|
+
jobId: "job_abc123",
|
|
209
|
+
referenceId: "user_123",
|
|
210
|
+
status: "verified",
|
|
211
|
+
environment: "live", // "live" | "test"
|
|
212
|
+
scores: {
|
|
213
|
+
documentScore: 0.95,
|
|
214
|
+
faceMatchScore: 0.92,
|
|
215
|
+
livenessScore: 0.98,
|
|
216
|
+
},
|
|
217
|
+
extractedData: {
|
|
218
|
+
firstName: "Jane",
|
|
219
|
+
lastName: "Doe",
|
|
220
|
+
fullName: "Jane Doe",
|
|
221
|
+
dateOfBirth: "1990-01-15",
|
|
222
|
+
documentNumber: "AB1234567",
|
|
223
|
+
nationality: "Cameroon",
|
|
224
|
+
expiryDate: "2030-12-31",
|
|
225
|
+
issuanceDate: "2020-01-01",
|
|
226
|
+
documentType: "id_card",
|
|
227
|
+
},
|
|
228
|
+
metadata: { plan: "premium" },
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Framework Examples
|
|
234
|
+
|
|
235
|
+
### Express
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
import express from "express";
|
|
239
|
+
import { KwikIDNode } from "@kwik-id/sdk-node";
|
|
240
|
+
|
|
241
|
+
const app = express();
|
|
242
|
+
const kwikId = new KwikIDNode({ apiKey: process.env.KWIKID_API_KEY! });
|
|
243
|
+
|
|
244
|
+
// Create a session for the frontend
|
|
245
|
+
app.post("/api/create-session", async (req, res) => {
|
|
246
|
+
const session = await kwikId.createSession({
|
|
247
|
+
referenceId: req.user.id,
|
|
248
|
+
userName: req.user.name,
|
|
249
|
+
});
|
|
250
|
+
res.json({ clientSecret: session.token });
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Next.js (App Router)
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
// app/api/verification/session/route.ts
|
|
258
|
+
import { KwikIDNode } from "@kwik-id/sdk-node";
|
|
259
|
+
import { NextResponse } from "next/server";
|
|
260
|
+
|
|
261
|
+
const kwikId = new KwikIDNode({ apiKey: process.env.KWIKID_API_KEY! });
|
|
262
|
+
|
|
263
|
+
export async function POST(request: Request) {
|
|
264
|
+
const { userId, userName } = await request.json();
|
|
265
|
+
const session = await kwikId.createSession({
|
|
266
|
+
referenceId: userId,
|
|
267
|
+
userName,
|
|
268
|
+
});
|
|
269
|
+
return NextResponse.json({ clientSecret: session.token });
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### NestJS
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
import { Injectable } from "@nestjs/common";
|
|
277
|
+
import { KwikIDNode } from "@kwik-id/sdk-node";
|
|
278
|
+
|
|
279
|
+
@Injectable()
|
|
280
|
+
export class VerificationService {
|
|
281
|
+
private kwikId = new KwikIDNode({ apiKey: process.env.KWIKID_API_KEY! });
|
|
282
|
+
|
|
283
|
+
async createSession(userId: string, userName: string) {
|
|
284
|
+
return this.kwikId.createSession({ referenceId: userId, userName });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async getResult(jobId: string) {
|
|
288
|
+
return this.kwikId.getVerificationResult(jobId);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Error Handling
|
|
294
|
+
|
|
295
|
+
All methods throw an `Error` with a descriptive message on failure:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
try {
|
|
299
|
+
const result = await kwikId.getVerification("invalid-id");
|
|
300
|
+
} catch (error) {
|
|
301
|
+
// "KwikIDNode: GET /api/v1/verifications/invalid-id failed — Not found"
|
|
302
|
+
console.error(error.message);
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("crypto"),w="https://api.kwik-id.com",y=3e4;class g{apiKey;baseUrl;timeout;constructor(e){if(!e.apiKey)throw new Error("KwikIDNode: apiKey is required");this.apiKey=e.apiKey,this.baseUrl=(e.baseUrl??w).replace(/\/$/,""),this.timeout=e.timeout??y}async createSession(e={}){return this.request("POST","/api/v1/sessions",e)}async getSession(e){const r=await this.request("GET",`/api/v1/sessions/${e}`);if(r.error)throw new Error(`KwikIDNode: getSession failed — ${r.error.message??r.error.code}`);return r}async listVerifications(e={}){const r=new URLSearchParams;e.status&&r.set("status",e.status),e.referenceId&&r.set("referenceId",e.referenceId),e.page!=null&&r.set("page",String(e.page)),e.limit!=null&&r.set("limit",String(e.limit)),e.sortBy&&r.set("sortBy",e.sortBy),e.sortOrder&&r.set("sortOrder",e.sortOrder);const s=r.toString();return this.request("GET",`/api/v1/verifications${s?`?${s}`:""}`)}async getVerification(e){const r=await this.request("GET",`/api/v1/verifications/${e}`);if(r.error)throw new Error(`KwikIDNode: getVerification failed — ${r.error.message}`);return r}async getVerificationByReference(e){const r=await this.request("GET",`/api/v1/verifications/reference/${encodeURIComponent(e)}`);if(r.error)throw new Error(`KwikIDNode: getVerificationByReference failed — ${r.error.message}`);return r}async getVerificationResult(e){const r=await this.request("GET",`/api/v1/verifications/${e}/result`);if(r.error)throw new Error(`KwikIDNode: getVerificationResult failed — ${r.error.message}`);return r}async request(e,r,s){const c=`${this.baseUrl}${r}`,a=new AbortController,n=setTimeout(()=>a.abort(),this.timeout);let i;try{i=await fetch(c,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:s!==void 0?JSON.stringify(s):void 0,signal:a.signal})}catch(t){throw t.name==="AbortError"?new Error(`KwikIDNode: request timed out after ${this.timeout}ms`):t}finally{clearTimeout(n)}if(!i.ok){let t=`HTTP ${i.status}`;try{const f=await i.json();t=f.message??f.error??t}catch{}throw new Error(`KwikIDNode: ${e} ${r} failed — ${t}`)}return i.json()}}function p(u){const{payload:e,signature:r,secret:s,toleranceSeconds:c=300}=u,a={};for(const o of r.split(",")){const l=o.indexOf("=");l!==-1&&(a[o.slice(0,l)]=o.slice(l+1))}const n=a.t,i=a.v1;if(!n||!i)return{valid:!1,error:"Invalid signature format"};const t=parseInt(n,10);if(isNaN(t))return{valid:!1,error:"Invalid timestamp in signature"};if(c>0){const o=Math.floor(Date.now()/1e3);if(Math.abs(o-t)>c)return{valid:!1,error:"Timestamp too old"}}const f=`${n}.${e}`,h=m.createHmac("sha256",s).update(f).digest("hex");let d;try{d=m.timingSafeEqual(Buffer.from(h,"hex"),Buffer.from(i,"hex"))}catch{return{valid:!1,error:"Signature comparison failed"}}if(!d)return{valid:!1,error:"Signature mismatch"};try{return{valid:!0,event:JSON.parse(e)}}catch{return{valid:!1,error:"Failed to parse payload as JSON"}}}exports.KwikIDNode=g;exports.verifyWebhookSignature=p;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { KwikIDNode } from './lib/kwik-node-sdk.js';
|
|
2
|
+
export { verifyWebhookSignature } from './lib/webhook-verifier.js';
|
|
3
|
+
export type { KwikIDNodeConfig, CreateSessionOptions, SessionToken, SessionDetails, Verification, VerificationSummary, VerificationResult, VerificationScores, ListVerificationsOptions, VerificationList, VerifyWebhookOptions, VerifyWebhookResult, WebhookEvent, WebhookEventType, } from './types/index.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACnB,YAAY,EACZ,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.es.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { createHmac as h, timingSafeEqual as w } from "crypto";
|
|
2
|
+
const g = "https://api.kwik-id.com", y = 3e4;
|
|
3
|
+
class v {
|
|
4
|
+
apiKey;
|
|
5
|
+
baseUrl;
|
|
6
|
+
timeout;
|
|
7
|
+
constructor(e) {
|
|
8
|
+
if (!e.apiKey)
|
|
9
|
+
throw new Error("KwikIDNode: apiKey is required");
|
|
10
|
+
this.apiKey = e.apiKey, this.baseUrl = (e.baseUrl ?? g).replace(/\/$/, ""), this.timeout = e.timeout ?? y;
|
|
11
|
+
}
|
|
12
|
+
// ─── Sessions ────────────────────────────────────────────────────────────
|
|
13
|
+
async createSession(e = {}) {
|
|
14
|
+
return this.request("POST", "/api/v1/sessions", e);
|
|
15
|
+
}
|
|
16
|
+
async getSession(e) {
|
|
17
|
+
const r = await this.request(
|
|
18
|
+
"GET",
|
|
19
|
+
`/api/v1/sessions/${e}`
|
|
20
|
+
);
|
|
21
|
+
if (r.error)
|
|
22
|
+
throw new Error(`KwikIDNode: getSession failed — ${r.error.message ?? r.error.code}`);
|
|
23
|
+
return r;
|
|
24
|
+
}
|
|
25
|
+
// ─── Verifications ───────────────────────────────────────────────────────
|
|
26
|
+
async listVerifications(e = {}) {
|
|
27
|
+
const r = new URLSearchParams();
|
|
28
|
+
e.status && r.set("status", e.status), e.referenceId && r.set("referenceId", e.referenceId), e.page != null && r.set("page", String(e.page)), e.limit != null && r.set("limit", String(e.limit)), e.sortBy && r.set("sortBy", e.sortBy), e.sortOrder && r.set("sortOrder", e.sortOrder);
|
|
29
|
+
const s = r.toString();
|
|
30
|
+
return this.request("GET", `/api/v1/verifications${s ? `?${s}` : ""}`);
|
|
31
|
+
}
|
|
32
|
+
async getVerification(e) {
|
|
33
|
+
const r = await this.request(
|
|
34
|
+
"GET",
|
|
35
|
+
`/api/v1/verifications/${e}`
|
|
36
|
+
);
|
|
37
|
+
if (r.error)
|
|
38
|
+
throw new Error(`KwikIDNode: getVerification failed — ${r.error.message}`);
|
|
39
|
+
return r;
|
|
40
|
+
}
|
|
41
|
+
async getVerificationByReference(e) {
|
|
42
|
+
const r = await this.request(
|
|
43
|
+
"GET",
|
|
44
|
+
`/api/v1/verifications/reference/${encodeURIComponent(e)}`
|
|
45
|
+
);
|
|
46
|
+
if (r.error)
|
|
47
|
+
throw new Error(`KwikIDNode: getVerificationByReference failed — ${r.error.message}`);
|
|
48
|
+
return r;
|
|
49
|
+
}
|
|
50
|
+
async getVerificationResult(e) {
|
|
51
|
+
const r = await this.request(
|
|
52
|
+
"GET",
|
|
53
|
+
`/api/v1/verifications/${e}/result`
|
|
54
|
+
);
|
|
55
|
+
if (r.error)
|
|
56
|
+
throw new Error(`KwikIDNode: getVerificationResult failed — ${r.error.message}`);
|
|
57
|
+
return r;
|
|
58
|
+
}
|
|
59
|
+
// ─── Internal ────────────────────────────────────────────────────────────
|
|
60
|
+
async request(e, r, s) {
|
|
61
|
+
const c = `${this.baseUrl}${r}`, a = new AbortController(), n = setTimeout(() => a.abort(), this.timeout);
|
|
62
|
+
let i;
|
|
63
|
+
try {
|
|
64
|
+
i = await fetch(c, {
|
|
65
|
+
method: e,
|
|
66
|
+
headers: {
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
"X-API-Key": this.apiKey
|
|
69
|
+
},
|
|
70
|
+
body: s !== void 0 ? JSON.stringify(s) : void 0,
|
|
71
|
+
signal: a.signal
|
|
72
|
+
});
|
|
73
|
+
} catch (t) {
|
|
74
|
+
throw t.name === "AbortError" ? new Error(`KwikIDNode: request timed out after ${this.timeout}ms`) : t;
|
|
75
|
+
} finally {
|
|
76
|
+
clearTimeout(n);
|
|
77
|
+
}
|
|
78
|
+
if (!i.ok) {
|
|
79
|
+
let t = `HTTP ${i.status}`;
|
|
80
|
+
try {
|
|
81
|
+
const f = await i.json();
|
|
82
|
+
t = f.message ?? f.error ?? t;
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
throw new Error(`KwikIDNode: ${e} ${r} failed — ${t}`);
|
|
86
|
+
}
|
|
87
|
+
return i.json();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function $(u) {
|
|
91
|
+
const { payload: e, signature: r, secret: s, toleranceSeconds: c = 300 } = u, a = {};
|
|
92
|
+
for (const o of r.split(",")) {
|
|
93
|
+
const l = o.indexOf("=");
|
|
94
|
+
l !== -1 && (a[o.slice(0, l)] = o.slice(l + 1));
|
|
95
|
+
}
|
|
96
|
+
const n = a.t, i = a.v1;
|
|
97
|
+
if (!n || !i)
|
|
98
|
+
return { valid: !1, error: "Invalid signature format" };
|
|
99
|
+
const t = parseInt(n, 10);
|
|
100
|
+
if (isNaN(t))
|
|
101
|
+
return { valid: !1, error: "Invalid timestamp in signature" };
|
|
102
|
+
if (c > 0) {
|
|
103
|
+
const o = Math.floor(Date.now() / 1e3);
|
|
104
|
+
if (Math.abs(o - t) > c)
|
|
105
|
+
return { valid: !1, error: "Timestamp too old" };
|
|
106
|
+
}
|
|
107
|
+
const f = `${n}.${e}`, m = h("sha256", s).update(f).digest("hex");
|
|
108
|
+
let d;
|
|
109
|
+
try {
|
|
110
|
+
d = w(Buffer.from(m, "hex"), Buffer.from(i, "hex"));
|
|
111
|
+
} catch {
|
|
112
|
+
return { valid: !1, error: "Signature comparison failed" };
|
|
113
|
+
}
|
|
114
|
+
if (!d)
|
|
115
|
+
return { valid: !1, error: "Signature mismatch" };
|
|
116
|
+
try {
|
|
117
|
+
return { valid: !0, event: JSON.parse(e) };
|
|
118
|
+
} catch {
|
|
119
|
+
return { valid: !1, error: "Failed to parse payload as JSON" };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export {
|
|
123
|
+
v as KwikIDNode,
|
|
124
|
+
$ as verifyWebhookSignature
|
|
125
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { KwikIDNodeConfig, CreateSessionOptions, SessionToken, SessionDetails, Verification, VerificationResult, ListVerificationsOptions, VerificationList } from '../types/index.js';
|
|
2
|
+
export declare class KwikIDNode {
|
|
3
|
+
private readonly apiKey;
|
|
4
|
+
private readonly baseUrl;
|
|
5
|
+
private readonly timeout;
|
|
6
|
+
constructor(config: KwikIDNodeConfig);
|
|
7
|
+
createSession(options?: CreateSessionOptions): Promise<SessionToken>;
|
|
8
|
+
getSession(sessionId: string): Promise<SessionDetails>;
|
|
9
|
+
listVerifications(options?: ListVerificationsOptions): Promise<VerificationList>;
|
|
10
|
+
getVerification(id: string): Promise<Verification>;
|
|
11
|
+
getVerificationByReference(referenceId: string): Promise<Verification>;
|
|
12
|
+
getVerificationResult(id: string): Promise<VerificationResult>;
|
|
13
|
+
private request;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=kwik-node-sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kwik-node-sdk.d.ts","sourceRoot":"","sources":["../../src/lib/kwik-node-sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,wBAAwB,EACxB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAK3B,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,EAAE,gBAAgB;IAW9B,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,YAAY,CAAC;IAIxE,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAatD,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAapF,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAWlD,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAWtE,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;YAatD,OAAO;CAsCtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-verifier.d.ts","sourceRoot":"","sources":["../../src/lib/webhook-verifier.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAEnF,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAmDzF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kwik-id/sdk-node",
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=18"
|
|
7
|
+
},
|
|
8
|
+
"main": "./dist/index.cjs.js",
|
|
9
|
+
"module": "./dist/index.es.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
"./package.json": "./package.json",
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.es.js",
|
|
16
|
+
"require": "./dist/index.cjs.js",
|
|
17
|
+
"default": "./dist/index.es.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"!**/*.tsbuildinfo"
|
|
23
|
+
],
|
|
24
|
+
"nx": {
|
|
25
|
+
"name": "sdk-node"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {}
|
|
28
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
export interface KwikIDNodeConfig {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
timeout?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface CreateSessionOptions {
|
|
7
|
+
referenceId?: string;
|
|
8
|
+
userName?: string;
|
|
9
|
+
userEmail?: string;
|
|
10
|
+
metadata?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
export interface SessionToken {
|
|
13
|
+
sessionId: string;
|
|
14
|
+
jobId: string;
|
|
15
|
+
token: string;
|
|
16
|
+
expiresAt: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SessionDetails {
|
|
19
|
+
sessionId: string;
|
|
20
|
+
jobId: string;
|
|
21
|
+
isUsed: boolean;
|
|
22
|
+
expiresAt: string;
|
|
23
|
+
}
|
|
24
|
+
export interface VerificationSummary {
|
|
25
|
+
id: string;
|
|
26
|
+
referenceId?: string;
|
|
27
|
+
status: string;
|
|
28
|
+
documentType?: string;
|
|
29
|
+
userName?: string;
|
|
30
|
+
userEmail?: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
completedAt?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface Verification {
|
|
35
|
+
id: string;
|
|
36
|
+
referenceId?: string;
|
|
37
|
+
status: string;
|
|
38
|
+
documentType?: string;
|
|
39
|
+
userName?: string;
|
|
40
|
+
userEmail?: string;
|
|
41
|
+
country?: string;
|
|
42
|
+
createdAt: string;
|
|
43
|
+
startedAt?: string;
|
|
44
|
+
completedAt?: string;
|
|
45
|
+
duration?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface VerificationScores {
|
|
48
|
+
overall: number;
|
|
49
|
+
liveness?: number;
|
|
50
|
+
document?: number;
|
|
51
|
+
faceMatch?: number;
|
|
52
|
+
}
|
|
53
|
+
export interface VerificationResult {
|
|
54
|
+
id: string;
|
|
55
|
+
referenceId?: string;
|
|
56
|
+
status: string;
|
|
57
|
+
scores?: VerificationScores;
|
|
58
|
+
extractedData?: {
|
|
59
|
+
documentType?: string;
|
|
60
|
+
country?: string;
|
|
61
|
+
firstName?: string;
|
|
62
|
+
lastName?: string;
|
|
63
|
+
dateOfBirth?: string;
|
|
64
|
+
idNumber?: string;
|
|
65
|
+
expiryDate?: string;
|
|
66
|
+
};
|
|
67
|
+
error?: {
|
|
68
|
+
code: string;
|
|
69
|
+
message: string;
|
|
70
|
+
};
|
|
71
|
+
completedAt?: string;
|
|
72
|
+
}
|
|
73
|
+
export interface ListVerificationsOptions {
|
|
74
|
+
status?: string;
|
|
75
|
+
referenceId?: string;
|
|
76
|
+
page?: number;
|
|
77
|
+
limit?: number;
|
|
78
|
+
sortBy?: "createdAt" | "updatedAt" | "status";
|
|
79
|
+
sortOrder?: "asc" | "desc";
|
|
80
|
+
}
|
|
81
|
+
export interface VerificationList {
|
|
82
|
+
verifications: VerificationSummary[];
|
|
83
|
+
pagination: {
|
|
84
|
+
total: number;
|
|
85
|
+
page: number;
|
|
86
|
+
limit: number;
|
|
87
|
+
totalPages: number;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export interface VerifyWebhookOptions {
|
|
91
|
+
payload: string;
|
|
92
|
+
signature: string;
|
|
93
|
+
secret: string;
|
|
94
|
+
toleranceSeconds?: number;
|
|
95
|
+
}
|
|
96
|
+
export type VerifyWebhookResult = {
|
|
97
|
+
valid: true;
|
|
98
|
+
event: WebhookEvent;
|
|
99
|
+
} | {
|
|
100
|
+
valid: false;
|
|
101
|
+
error: string;
|
|
102
|
+
};
|
|
103
|
+
export type WebhookEventType = "verification.started" | "verification.uploaded" | "verification.completed" | "verification.rejected" | "verification.failed";
|
|
104
|
+
export interface WebhookEvent {
|
|
105
|
+
event: WebhookEventType;
|
|
106
|
+
timestamp: string;
|
|
107
|
+
data: {
|
|
108
|
+
jobId: string;
|
|
109
|
+
referenceId?: string;
|
|
110
|
+
status: string;
|
|
111
|
+
environment: "live" | "test";
|
|
112
|
+
scores?: {
|
|
113
|
+
documentScore?: number;
|
|
114
|
+
documentBackScore?: number;
|
|
115
|
+
faceMatchScore?: number;
|
|
116
|
+
livenessScore?: number;
|
|
117
|
+
};
|
|
118
|
+
extractedData?: {
|
|
119
|
+
firstName?: string;
|
|
120
|
+
lastName?: string;
|
|
121
|
+
fullName?: string;
|
|
122
|
+
dateOfBirth?: string;
|
|
123
|
+
documentNumber?: string;
|
|
124
|
+
uniqueIdentifier?: string;
|
|
125
|
+
nationality?: string;
|
|
126
|
+
expiryDate?: string;
|
|
127
|
+
issuanceDate?: string;
|
|
128
|
+
documentType?: string;
|
|
129
|
+
};
|
|
130
|
+
errorCode?: string;
|
|
131
|
+
errorMessage?: string;
|
|
132
|
+
metadata?: Record<string, unknown>;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kwik-id/sdk-node",
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=18"
|
|
7
|
+
},
|
|
8
|
+
"main": "./dist/index.cjs.js",
|
|
9
|
+
"module": "./dist/index.es.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
"./package.json": "./package.json",
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.es.js",
|
|
16
|
+
"require": "./dist/index.cjs.js",
|
|
17
|
+
"default": "./dist/index.es.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"!**/*.tsbuildinfo"
|
|
23
|
+
],
|
|
24
|
+
"nx": {
|
|
25
|
+
"name": "sdk-node"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {}
|
|
28
|
+
}
|