React Native
A hardened react-native-webview wrapper (RN 0.74+) with a component, a modal and a promise API.
Version0.1.2
Install
Peer dependencies: react ≥ 18, react-native ≥ 0.74, react-native-webview ≥ 13, and optionally
expo-screen-capture ≥ 6 (lets the SDK block screenshots and screen recording).
npm install @okyc/react-native react-native-webview
npx expo install expo-screen-capture # optional: screenshot blocking
cd ios && pod installPermissions
iOS needs camera (and, for uploads, photo library) usage strings:
<key>NSCameraUsageDescription</key>
<string>The camera is used to capture your ID document and a selfie for identity verification.</string>Android needs INTERNET and CAMERA:
<uses-permission android:name="android.permission.INTERNET" />
<!-- Document + selfie capture: react-native-webview asks for the runtime permission when the page calls getUserMedia. -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />When the page asks for the camera, react-native-webview requests the runtime permission. If the user refuses,
the flow shows its own "camera blocked" state with an upload fallback where the workflow allows one. File
inputs work on both platforms without extra code.
Start the flow
Create the session on your backend and pass its url to the app.
export function makeConfig(sessionUrl: string): OkycConfig {
return {
sessionUrl, // the `url` returned by POST /v1/sessions
locale: 'en', // optional; defaults to the session's language
theme: 'system', // 'light' | 'dark' | 'system'
allowedHosts: [], // extra hosts, only when self-hosting
blockScreenshots: true, // needs expo-screen-capture
autoCloseDelay: 2000, // ms the result screen stays visible after `complete`
};
}export function App() {
return (
<OkycProvider>
<VerifyButton />
</OkycProvider>
);
}
function VerifyButton() {
const start = async () => {
const sessionUrl = await fetchSessionUrlFromYourBackend();
const result = await Okyc.start(makeConfig(sessionUrl), { onEvent });
handleResult(result);
};
return <Button title="Verify identity" onPress={start} />;
}Three ways to present it:
| API | |
|---|---|
<OkycVerification config onComplete onCancel onError onEvent /> | Inline component; you own the screen. |
<OkycModal visible config … /> | Full-screen modal with a toolbar; close and Android back ask "Exit verification?". |
OkycProvider + useOkyc().start(config) or Okyc.start(config) | Promise that resolves to an OkycResult. One verification at a time. |
The modal form:
export function VerifyScreen({ sessionUrl }: { sessionUrl: string }) {
const [visible, setVisible] = useState(true);
const done = (result: OkycResult) => {
setVisible(false);
handleResult(result);
};
return (
<View style={{ flex: 1 }}>
<Text>Verification</Text>
<OkycModal
visible={visible}
config={makeConfig(sessionUrl)}
onEvent={onEvent}
onComplete={done}
onCancel={done}
onError={done}
/>
</View>
);
}Options
| Option | Type | Default | Description |
|---|---|---|---|
sessionUrl | string | required | The url returned by POST /v1/sessions. Never embed a secret API key in an app. |
locale | string | null | Flow language (en, hi); must be one of the workflow's languages. Sent as ?locale=. |
theme | light | dark | system | "system" | Colour scheme of the flow. Sent as ?theme=. |
allowedHosts | string[] | [] | Extra hosts the WebView may load (self-hosting). |
blockScreenshots | boolean | true | Android: FLAG_SECURE. iOS: cover the flow in the app switcher and while the screen is recorded. |
autoCloseDelay | duration_ms | 2000 ms | How long the result screen stays visible after complete before the SDK closes and returns completed. 0 or null = return the result immediately (no result screen). |
Results and events
function handleResult(result: OkycResult) {
switch (result.type) {
case 'completed':
// UX only: confirm the decision on your backend (webhook or GET /v1/sessions/:id).
console.log('finished', result.sessionId, result.status);
break;
case 'cancelled':
console.log('user closed the flow');
break;
case 'failed':
console.warn('verification failed', result.code, result.message);
break;
}
}function onEvent(event: OkycEvent) {
// Payloads carry ids and statuses only — never personal data.
track(`okyc_${event.type}`, { sessionId: event.sessionId, ...event.payload });
}OkycResult is { type: 'completed', sessionId, status }, { type: 'cancelled', sessionId? } or
{ type: 'failed', sessionId?, code, message }. Event payloads are flat and PII-free (for example
{ step: 'pan' }).
Failure codes in React Native:
| Code | Meaning |
|---|---|
invalid_config | Missing or malformed session URL or configuration. |
load_failed | The flow page failed to load (HTTP error or blank page). |
network_error | No connection or DNS/TLS failure while loading. |
navigation_blocked | Repeated navigation to non-allowlisted hosts. |
permission_denied | The camera permission was permanently denied and the flow cannot continue. |
flow_error | The flow reported an unrecoverable error. |
webview_unavailable | The WebView renderer crashed or is unavailable. |
Theming and locale
theme: 'light' | 'dark' forces the flow's colour scheme before the page loads; system follows the device
and your dashboard branding. The flow language comes from the session: pass locale when you create it
(POST /v1/sessions); the applicant can also switch it in the flow.
Security defaults
- Only the flow origin, DigiLocker and
allowedHostsload;target=_blankhttps links open in the system browser; other hosts, http downgrades,file:,intent:andjavascript:are blocked. - Bridge messages are accepted only from the session's own page (origin and session id; not DigiLocker or
allowedHosts) and must be JSON withsource: 'okyc',v: 1and a known type; payloads are reduced to scalars and messages over 10 KB are dropped.blob:/data:URLs load only inside frames. - Incognito WebView without cache, shared or third-party cookies, file URLs or mixed content; gestures and link
previews off; debugging only in
__DEV__. - Screenshots and recording are blocked when
expo-screen-captureis installed (isScreenshotBlockingAvailable()tells you). - The session URL is never logged or included in error messages.
Troubleshooting
| Symptom | Fix |
|---|---|
A verification is already in progress | Only one start() at a time; wait for the result. |
| Local http URL refused | HTTP is accepted only for localhost, 127.0.0.1, 10.0.2.2, [::1] and *.localhost. |
| Screenshots still possible | Install expo-screen-capture (bare apps need expo-modules). |
Going live
- Create sessions with a
sk_live_…key on your backend; nothing changes in the app. - Install
expo-screen-capturesoblockScreenshotstakes effect. - Decide on the webhook, not on
completed.
iOS
Present the verification flow from UIKit or SwiftUI (iOS 15+) and await a typed OkycResult.
Flutter
Okyc.start pushes a full-screen verification route (Flutter 3.24+, Android and iOS) and returns a typed result.
© 2026 Aiistic Technologies Private Limited. OKYC is a product of Aiistic Technologies Private Limited. Privacy notice