@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.
Files changed (2) hide show
  1. package/README.md +267 -36
  2. 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 SDK for React Native / Expo. Mirrors the KwikId web SDK flow
4
- (document capture → selfie → upload → submit) natively, using
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 status.** This release captures documents and a selfie and submits them
8
- > for verification. It does **not** yet run on-device liveness (passive or active
9
- > challenges), image-quality checks, or the desktop→mobile handoff UI — those land
10
- > in later alphas. No client-side liveness result is sent with the submission.
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
- > **Requires Expo SDK 53 and a custom dev build / EAS build.** This SDK uses
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
- ## Install
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; they will be
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
- ["@kwik-id/sdk-react-native", { "cameraPermissionText": "We verify your identity" }],
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
- ### 2. Prebuild + run a dev build
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 # or run:android, or `eas build --profile development`
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
- Your backend creates a session (`POST /api/v1/sessions` with your API key) and
51
- returns its `token`. Pass that token to the SDK as `clientSecret`:
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
- ## Architecture
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
- This package is **standalone**: it reuses the KwikId backend protocol and type
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
- Built with [`react-native-builder-bob`](https://github.com/callstack/react-native-builder-bob).
82
- See the repo plan for the phased rollout (capture → passive liveness → active challenges → handoff).
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kwik-id/sdk-react-native",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.1",
4
4
  "description": "KwikId on-device KYC + liveness SDK for React Native / Expo",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Kwik Nkap Technologies",