@kwik-id/sdk-react-native 0.1.0-alpha.0 → 0.1.0-alpha.1
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 +267 -36
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,82 +1,313 @@
|
|
|
1
1
|
# @kwik-id/sdk-react-native
|
|
2
2
|
|
|
3
|
-
On-device KYC
|
|
4
|
-
(
|
|
3
|
+
On-device identity verification (KYC) for React Native / Expo apps, by
|
|
4
|
+
[KwikID](https://identity.kwiknkap.com). The SDK runs the same flow as the KwikID web
|
|
5
|
+
SDK, natively: pick a document → photograph the front (and back) → take a selfie →
|
|
6
|
+
upload → submit. Camera capture uses
|
|
5
7
|
[`react-native-vision-camera`](https://react-native-vision-camera.com/).
|
|
6
8
|
|
|
7
|
-
> **Alpha
|
|
8
|
-
>
|
|
9
|
-
> challenges), image-quality checks, or the desktop→mobile handoff UI
|
|
10
|
-
>
|
|
9
|
+
> **Alpha.** This release captures documents and a selfie and submits them for
|
|
10
|
+
> verification. It does **not** yet run on-device liveness (passive or active
|
|
11
|
+
> challenges), image-quality checks, or the desktop→mobile handoff UI. Those land in
|
|
12
|
+
> later alphas. No client-side liveness result is sent with the submission. Expect
|
|
13
|
+
> breaking changes between alpha versions.
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
> native vision-camera modules and **will not run in Expo Go.** Run `expo prebuild` and build a
|
|
14
|
-
> development client (or use `eas build`). `KwikIdRNSDK.initialize()` throws an
|
|
15
|
-
> actionable error if it detects Expo Go at runtime.
|
|
15
|
+
## Requirements
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
- **Expo SDK 53** (React Native 0.79, React 19).
|
|
18
|
+
- **A custom dev build or EAS build.** The SDK uses native camera modules and
|
|
19
|
+
**does not run in Expo Go**. In Expo Go, `KwikIdRNSDK.initialize()` throws an
|
|
20
|
+
error that explains how to switch to a dev build, and the verification modal shows
|
|
21
|
+
that message.
|
|
22
|
+
- A physical device for capture testing. Simulators/emulators have no real camera.
|
|
23
|
+
- A KwikID account and API key from the [KwikID dashboard](https://identity.kwiknkap.com).
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
18
26
|
|
|
19
27
|
```sh
|
|
20
28
|
npx expo install @kwik-id/sdk-react-native react-native-vision-camera \
|
|
21
29
|
expo-image-manipulator expo-file-system expo-device expo-localization expo-constants
|
|
22
30
|
```
|
|
23
31
|
|
|
32
|
+
Use `npx expo install` (not plain `npm install`) so the Expo modules resolve to the
|
|
33
|
+
versions that match your Expo SDK.
|
|
34
|
+
|
|
24
35
|
`react-native-reanimated`, `react-native-worklets-core` and
|
|
25
|
-
`react-native-vision-camera-face-detector` are optional peers for now
|
|
26
|
-
required once on-device liveness ships.
|
|
36
|
+
`react-native-vision-camera-face-detector` are optional peers for now. You don't need
|
|
37
|
+
them in this alpha; they become required once on-device liveness ships.
|
|
27
38
|
|
|
28
|
-
### 1. Add the config plugins (`app.json`)
|
|
39
|
+
### 1. Add the config plugins (`app.json` / `app.config.js`)
|
|
29
40
|
|
|
30
41
|
```jsonc
|
|
31
42
|
{
|
|
32
43
|
"expo": {
|
|
33
44
|
"plugins": [
|
|
34
45
|
"react-native-vision-camera",
|
|
35
|
-
[
|
|
46
|
+
[
|
|
47
|
+
"@kwik-id/sdk-react-native",
|
|
48
|
+
{ "cameraPermissionText": "We use the camera to verify your identity" },
|
|
49
|
+
],
|
|
36
50
|
],
|
|
37
51
|
},
|
|
38
52
|
}
|
|
39
53
|
```
|
|
40
54
|
|
|
41
|
-
|
|
55
|
+
The KwikID plugin adds the camera permission on both platforms
|
|
56
|
+
(`NSCameraUsageDescription` on iOS, `android.permission.CAMERA` on Android).
|
|
57
|
+
`cameraPermissionText` is optional; it is the text iOS shows in the permission prompt.
|
|
58
|
+
|
|
59
|
+
### 2. Prebuild and run a dev build
|
|
42
60
|
|
|
43
61
|
```sh
|
|
44
62
|
npx expo prebuild
|
|
45
|
-
npx expo run:ios
|
|
63
|
+
npx expo run:ios # or: npx expo run:android
|
|
64
|
+
# or build in the cloud: eas build --profile development
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Re-run `expo prebuild` whenever you add or change config plugins.
|
|
68
|
+
|
|
69
|
+
## How it works
|
|
70
|
+
|
|
71
|
+
1. **Your backend** creates a verification session with your secret API key and gets
|
|
72
|
+
back a short-lived session `token`. Never put your API key in the app.
|
|
73
|
+
2. **Your app** passes that token to the SDK as `clientSecret` and opens the flow.
|
|
74
|
+
3. The SDK guides the user through capture, uploads the images and submits the
|
|
75
|
+
verification. `start()` resolves with `status: "submitted"` and a `jobId`.
|
|
76
|
+
4. **KwikID** processes the verification asynchronously and sends the result to your
|
|
77
|
+
backend by **webhook**. The app does not receive the final verified/rejected
|
|
78
|
+
decision.
|
|
79
|
+
|
|
80
|
+
### Create a session on your backend
|
|
81
|
+
|
|
82
|
+
With [`@kwik-id/sdk-node`](https://www.npmjs.com/package/@kwik-id/sdk-node):
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { KwikIDNode } from "@kwik-id/sdk-node";
|
|
86
|
+
|
|
87
|
+
const kwikId = new KwikIDNode({ apiKey: process.env.KWIKID_API_KEY! });
|
|
88
|
+
|
|
89
|
+
// e.g. in your POST /kyc/session route
|
|
90
|
+
const session = await kwikId.createSession({
|
|
91
|
+
referenceId: user.id, // your own user/order ID; echoed back in webhooks
|
|
92
|
+
userName: user.name,
|
|
93
|
+
userEmail: user.email,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return { clientSecret: session.token, jobId: session.jobId };
|
|
46
97
|
```
|
|
47
98
|
|
|
99
|
+
Or call the REST API directly: `POST /api/v1/sessions` with an `X-API-Key` header.
|
|
100
|
+
The response's `token` is the `clientSecret`.
|
|
101
|
+
|
|
102
|
+
Session tokens expire. Create a new session for each verification attempt, just
|
|
103
|
+
before you open the flow.
|
|
104
|
+
|
|
48
105
|
## Usage
|
|
49
106
|
|
|
50
|
-
|
|
51
|
-
|
|
107
|
+
### Quick start: `useKwikVerification`
|
|
108
|
+
|
|
109
|
+
The hook gives you a `start()` function that opens the full-screen verification flow
|
|
110
|
+
and resolves when the user finishes or closes it. Render `<kyc.Modal />` once,
|
|
111
|
+
anywhere in the component.
|
|
52
112
|
|
|
53
113
|
```tsx
|
|
54
114
|
import { useKwikVerification } from "@kwik-id/sdk-react-native";
|
|
115
|
+
import { Alert, Button } from "react-native";
|
|
116
|
+
|
|
117
|
+
export function VerifyIdentityButton({ clientSecret }: { clientSecret: string }) {
|
|
118
|
+
const kyc = useKwikVerification({
|
|
119
|
+
clientSecret,
|
|
120
|
+
appName: "Acme Bank", // optional: header title (defaults to your org name)
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const verify = async () => {
|
|
124
|
+
const outcome = await kyc.start();
|
|
125
|
+
|
|
126
|
+
switch (outcome.status) {
|
|
127
|
+
case "submitted":
|
|
128
|
+
// Documents are in. The final decision arrives at your backend by webhook.
|
|
129
|
+
Alert.alert("Thanks!", "Your documents were submitted for review.");
|
|
130
|
+
break;
|
|
131
|
+
case "cancelled":
|
|
132
|
+
// The user closed the flow.
|
|
133
|
+
break;
|
|
134
|
+
case "error":
|
|
135
|
+
// The flow ended on an error screen (expired session, network, etc.).
|
|
136
|
+
console.warn("KYC failed:", outcome.error?.message);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
55
140
|
|
|
56
|
-
function VerifyButton({ clientSecret }: { clientSecret: string }) {
|
|
57
|
-
const kyc = useKwikVerification({ clientSecret });
|
|
58
141
|
return (
|
|
59
142
|
<>
|
|
60
|
-
<Button
|
|
61
|
-
title="Verify identity"
|
|
62
|
-
onPress={async () => {
|
|
63
|
-
const outcome = await kyc.start();
|
|
64
|
-
if (outcome.status === "submitted") {
|
|
65
|
-
// result is delivered to your backend via webhook
|
|
66
|
-
}
|
|
67
|
-
}}
|
|
68
|
-
/>
|
|
143
|
+
<Button title="Verify identity" onPress={verify} />
|
|
69
144
|
<kyc.Modal />
|
|
70
145
|
</>
|
|
71
146
|
);
|
|
72
147
|
}
|
|
73
148
|
```
|
|
74
149
|
|
|
75
|
-
|
|
150
|
+
Fetching the session token right before opening the flow:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
const [clientSecret, setClientSecret] = useState("");
|
|
154
|
+
const kyc = useKwikVerification({ clientSecret });
|
|
155
|
+
|
|
156
|
+
const verify = async () => {
|
|
157
|
+
const res = await fetch("https://your-backend.example.com/kyc/session", { method: "POST" });
|
|
158
|
+
setClientSecret((await res.json()).clientSecret);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Open once the token is in state, so the modal receives it.
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (clientSecret) kyc.start().then(handleOutcome);
|
|
164
|
+
}, [clientSecret]);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### Options
|
|
168
|
+
|
|
169
|
+
| Option | Type | Default | Description |
|
|
170
|
+
| --------------- | ----------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
171
|
+
| `clientSecret` | `string` | **required** | Session token from your backend. |
|
|
172
|
+
| `appName` | `string` | org name from the session | Title shown in the flow's header. |
|
|
173
|
+
| `locale` | `"en" \| "fr"` | device language | UI language. If omitted, the SDK uses French when the device language is French and English otherwise. |
|
|
174
|
+
| `theme` | `KwikIdTheme` | KwikID blue on white | Colors; see [Theming](#theming). |
|
|
175
|
+
| `documentTypes` | `DocumentType[]` | all types enabled for your org | Restrict which documents the user can pick. Intersected with your session's configuration. |
|
|
176
|
+
| `onSubmitted` | `({ jobId }) => void` | none | Called as soon as the submission succeeds, before the user taps Done. |
|
|
177
|
+
| `baseUrl` | `string` | `https://api.identity.kwiknkap.com` | API base URL. Override only to target a non-production environment. |
|
|
178
|
+
| `livenessMode` | `"passive" \| "active" \| "hybrid"` | none | **Not yet supported.** Accepted but ignored until on-device liveness ships. |
|
|
179
|
+
|
|
180
|
+
#### Return value
|
|
181
|
+
|
|
182
|
+
| Field | Type | Description |
|
|
183
|
+
| -------- | ------------------------------------ | --------------------------------------------------------------------------- |
|
|
184
|
+
| `start` | `() => Promise<VerificationOutcome>` | Opens the flow. Resolves when the user finishes or closes it. |
|
|
185
|
+
| `cancel` | `() => void` | Closes the flow programmatically; a pending `start()` resolves `cancelled`. |
|
|
186
|
+
| `isOpen` | `boolean` | Whether the flow is currently open. |
|
|
187
|
+
| `Modal` | `React.FC` | Render once; the verification UI mounts here. |
|
|
188
|
+
|
|
189
|
+
#### `VerificationOutcome`
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
interface VerificationOutcome {
|
|
193
|
+
status: "submitted" | "cancelled" | "error";
|
|
194
|
+
jobId?: string; // set when status is "submitted"
|
|
195
|
+
error?: Error; // set when status is "error"
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`"submitted"` means the documents were received, **not** that the user is verified.
|
|
200
|
+
Use the webhook (or `getVerificationResult(jobId)` in `@kwik-id/sdk-node`) to read the
|
|
201
|
+
decision.
|
|
202
|
+
|
|
203
|
+
### Declarative: `<KwikIDVerificationModal />`
|
|
204
|
+
|
|
205
|
+
If you prefer to control visibility yourself:
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
import { KwikIDVerificationModal } from "@kwik-id/sdk-react-native";
|
|
209
|
+
|
|
210
|
+
<KwikIDVerificationModal
|
|
211
|
+
visible={open}
|
|
212
|
+
clientSecret={clientSecret}
|
|
213
|
+
locale="fr"
|
|
214
|
+
onSubmitted={({ jobId }) => saveJobId(jobId)}
|
|
215
|
+
onComplete={(outcome) => {
|
|
216
|
+
setOpen(false);
|
|
217
|
+
handleOutcome(outcome);
|
|
218
|
+
}}
|
|
219
|
+
/>;
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
It takes the same options as the hook, plus `visible`, `onComplete(outcome)` (always
|
|
223
|
+
called when the flow ends, including on cancel) and `onClose()` (called only when the
|
|
224
|
+
user dismisses the flow).
|
|
225
|
+
|
|
226
|
+
### Theming
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
useKwikVerification({
|
|
230
|
+
clientSecret,
|
|
231
|
+
theme: {
|
|
232
|
+
primary: "#0F766E", // buttons, progress bar, accents (default #1E3A8A)
|
|
233
|
+
background: "#FFFFFF", // screen background
|
|
234
|
+
foreground: "#111111", // text
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The built-in flow currently uses `primary`, `background` and `foreground`. `accent`,
|
|
240
|
+
`error` and `success` are reserved for upcoming screens.
|
|
241
|
+
|
|
242
|
+
### Localization
|
|
243
|
+
|
|
244
|
+
The flow ships with **English** and **French**. Without a `locale` option it follows
|
|
245
|
+
the device language. Pass `locale` to force a language, e.g. to match an in-app
|
|
246
|
+
language setting. Other device languages fall back to English.
|
|
247
|
+
|
|
248
|
+
### Document types
|
|
249
|
+
|
|
250
|
+
`passport`, `id_card`, `drivers_license`, `voters_card` and `residence_permit`.
|
|
251
|
+
National ID cards, driver's licenses and voter's cards ask for front and back;
|
|
252
|
+
passports and residence permits need only the front. If your
|
|
253
|
+
session configuration doesn't list any document types, the user can choose from
|
|
254
|
+
passport, national ID card and driver's license.
|
|
255
|
+
|
|
256
|
+
## Error handling and timeouts
|
|
257
|
+
|
|
258
|
+
When something goes wrong, the flow shows an error screen and `start()` resolves with
|
|
259
|
+
`status: "error"` after the user taps Done. Common causes:
|
|
260
|
+
|
|
261
|
+
- **Expired session:** the user sees "Your verification session has expired." Create
|
|
262
|
+
a new session and start again.
|
|
263
|
+
- **Network problems:** each API request and each upload times out after **30
|
|
264
|
+
seconds**. For uploads the timer restarts whenever upload progress is reported, so a
|
|
265
|
+
slow upload that's still moving is not cut off; only a stalled one is cancelled.
|
|
266
|
+
- **Camera permission denied:** the flow shows a screen with an **Open Settings**
|
|
267
|
+
button. The user can grant access there and come back to continue.
|
|
268
|
+
- **Expo Go:** see [Requirements](#requirements).
|
|
269
|
+
|
|
270
|
+
## Advanced: headless SDK
|
|
271
|
+
|
|
272
|
+
`KwikIdRNSDK` is the engine behind the built-in UI. Use it if you build your own
|
|
273
|
+
capture screens. You are then responsible for capture, compression (`compressCapture`)
|
|
274
|
+
and calling the API in the right order.
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
import { KwikIdRNSDK } from "@kwik-id/sdk-react-native";
|
|
278
|
+
|
|
279
|
+
const sdk = new KwikIdRNSDK({ clientSecret, timeout: 30_000, debug: __DEV__ });
|
|
280
|
+
|
|
281
|
+
const session = await sdk.initialize(); // validates the token; throws in Expo Go
|
|
282
|
+
sdk.on("upload:progress", ({ type, progress }) => console.log(type, progress));
|
|
283
|
+
|
|
284
|
+
await sdk.uploadCapture("document_front", frontAsset);
|
|
285
|
+
await sdk.uploadCapture("document_back", backAsset); // not for passports
|
|
286
|
+
await sdk.uploadCapture("selfie", selfieAsset);
|
|
287
|
+
const { jobId } = await sdk.submit("id_card");
|
|
288
|
+
|
|
289
|
+
sdk.destroy();
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Events emitted today: `ready`, `error`, `session:created`, `upload:starting`,
|
|
293
|
+
`upload:progress`, `upload:complete`, `upload:failed`, `verification:submitting`,
|
|
294
|
+
`verification:submitted`, `verification:failed`. The other event names in
|
|
295
|
+
`KwikIdEvents` are reserved for liveness and handoff and are not emitted yet.
|
|
296
|
+
|
|
297
|
+
`CameraView` (the capture component used by the built-in flow) is also exported for
|
|
298
|
+
custom screens.
|
|
299
|
+
|
|
300
|
+
## Troubleshooting
|
|
301
|
+
|
|
302
|
+
| Symptom | Fix |
|
|
303
|
+
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
304
|
+
| "This SDK requires a custom dev build or EAS build" | You're in Expo Go. Run `npx expo prebuild` and `npx expo run:ios` / `run:android`, or use an EAS dev build. |
|
|
305
|
+
| Camera preview is black / no camera found | Use a physical device, and check that the vision-camera and KwikID plugins are in `app.json`, then rebuild. |
|
|
306
|
+
| iOS crashes when the camera opens | `NSCameraUsageDescription` is missing: add the KwikID config plugin and re-run `expo prebuild`. |
|
|
307
|
+
| "Your verification session has expired" | Session tokens are short-lived. Create a new session right before calling `start()`. |
|
|
76
308
|
|
|
77
|
-
|
|
78
|
-
vocabulary but ships its own RN-native API client, session manager, crypto, camera,
|
|
79
|
-
and UI. It has **no runtime dependency** on the other `@kwik-id/*` libraries.
|
|
309
|
+
## License
|
|
80
310
|
|
|
81
|
-
|
|
82
|
-
|
|
311
|
+
Proprietary. Copyright © 2026 Kwik Nkap Technologies. All rights reserved. Use is
|
|
312
|
+
subject to the terms in the [LICENSE](./LICENSE) file and your agreement with Kwik Nkap
|
|
313
|
+
Technologies.
|
package/package.json
CHANGED