@verifner/react-native
v0.1.0-alpha.0
Published
VerifNer React Native SDK: guided document and selfie capture for Nigerien identity verification
Maintainers
Readme
@verifner/react-native
Alpha (
0.1.0-alpha.0). The API may change before 0.1.0; see What the alpha does not do yet.
Guided identity capture for the VerifNer verification API in a React Native
app: document front (and back for a CNI), selfie, live quality gates,
compression for 3G, upload with a short-lived client token. The React Native
counterpart of @verifner/web:
same session flow, same screens, same French and English copy.
How it works
- Your server calls
POST /v1/sessionswith the secret key and gets{ id, client_token }(15 minutes, single use). - Your app renders
<VerifNerFlow>with those two values. The secret key never reaches the phone. - The SDK uploads to
POST /v1/sessions/:id/verifyand callsonComplete. - Your server decides from the
verification.completedwebhook orGET /v1/sessions/:id, never from the app: a result shown in the app can be tampered with.
Install
npm install @verifner/react-native \
react-native-svg @react-native-community/image-editor \
react-native-vision-camera@^4.6
cd ios && pod install| Peer dependency | Why |
|---|---|
| react-native ≥ 0.74, react ≥ 18.2 | |
| react-native-svg ≥ 15 | Icons and illustrations |
| @react-native-community/image-editor ≥ 4.2 | Crop to the guide, resize to 1600 px, JPEG at about 300 KB |
| react-native-vision-camera ^4.6 | The default camera. Optional: any camera library fits behind a small adapter |
Camera permission:
- iOS,
Info.plist:NSCameraUsageDescription, e.g. « VerifNer utilise la caméra pour photographier votre pièce d'identité et prendre un selfie. » - Android,
AndroidManifest.xml:<uses-permission android:name="android.permission.CAMERA" />
1. Open a session on your server
app.post('/kyc/session', async (req, res) => {
const r = await fetch('https://api.verifner.com/v1/sessions', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.VERIFNER_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ reference: req.user.id }), // document_type: 'cni' | 'passport' is optional
});
const s = await r.json();
res.json({ sessionId: s.id, clientToken: s.client_token });
});2. Render the flow
import { VerifNerFlow } from '@verifner/react-native';
import { visionCamera } from '@verifner/react-native/vision-camera';
<SafeAreaView style={{ flex: 1 }}>
<VerifNerFlow
sessionId={sessionId}
clientToken={clientToken}
apiUrl="https://api.verifner.com"
camera={visionCamera}
lang="fr"
onComplete={(result) => navigation.replace('KycThanks')}
onError={(e) => console.warn(e.code)}
onExit={() => navigation.goBack()}
/>
</SafeAreaView>example/App.tsx is a whole integration on one screen, and
example/server.mjs the stand-in for your server
(VERIFNER_API_KEY=vn_test_… node example/server.mjs).
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| sessionId | string | required | The session id |
| clientToken | string | required | The session client_token |
| apiUrl | string | required | https://api.verifner.com |
| camera | CameraAdapter | required | visionCamera, or your own |
| lang | fr or en | fr | The welcome screen also has a switch |
| documentType | cni or passport | | Skips the document picker |
| selfie | boolean | true | Ask for a selfie |
| theme | light, dark or auto | auto | auto follows the phone |
| fontFamily | string | system font | A font registered in your app |
| branding | object | the application's | Colour, logo, button shape, theme, texts. Normally set in the console (Personnalisation) and sent with the session; this prop replaces it. Entreprise plan |
| autoCapture | boolean | false | Document photos take themselves once sharp and steady. By default the person taps a shutter that lights up when the view is right |
| precheck | boolean | true | On "Vérifiez la photo", VerifNer's vision model says what to fix (wrong side, cut off, glare…). Advice only, never blocking |
| showVerdict | boolean | false | Show the verdict (verified, in review, not confirmed), never its reasons. By default: "Vérification envoyée" |
| pickImage | () => Promise<ImageFile \| null> | | Offer the gallery for document photos (never the selfie) |
| onComplete | (result) => void | | { id, verdict, score, document_type, latency_ms, created_at } |
| onError | ({ code, message }) => void | | An error shown to the person |
| onStep | (step) => void | | intro, document, prepare, front, back, selfie, review, uploading, result, error |
| onExit | () => void | | Adds a close button on the last screen and on errors, and handles Android's back on the first screen |
| style | ViewStyle | | Style of the root view |
The flow fills its parent (flex: 1). Wrap it in a safe area.
Gallery
import { launchImageLibrary } from 'react-native-image-picker';
<VerifNerFlow
{...}
pickImage={async () => {
const r = await launchImageLibrary({ mediaType: 'photo', selectionLimit: 1 });
const a = r.assets?.[0];
return a?.uri && a.width && a.height ? { uri: a.uri, width: a.width, height: a.height } : null;
}}
/>Errors shown in the app
| code | Cause | What the person sees |
|---|---|---|
| unreadable, not_an_image, extraction_failed | Photo too blurred or unreadable (422) | A button to retake. The session stays usable |
| session_expired, session_completed, session_invalid | Session older than its TTL, already used, or bad token | Start again: open a new session |
| insufficient_balance | Live key, the prepaid wallet is empty (402) | Try later |
| network | No connection after three tries | A button to send again (the same photos) |
| camera_denied, camera_unavailable | No permission or no camera | Retry, and a button to the phone's settings |
| capture_failed, file_unreadable | The photo could not be taken or the picked file read | Retry |
Headless use
The API client works without the UI:
import { createClient } from '@verifner/react-native';
const vn = createClient({ apiUrl, sessionId, clientToken });
const session = await vn.session(); // mode, status, branding, training
const issue = await vn.precheck(frontUri, 'cni', 'front'); // 'ok', an issue, or null
const out = await vn.verify({ front, back, selfie }, { onProgress: (f) => {} });
if (out.ok) out.result.verdict; else out.error.code;verify retries 5xx answers and dropped connections (1 s, 2 s; three tries
in all) and never retries a 4xx. prepareUpload(image, { stage, guide })
crops, resizes and compresses a photo the same way the flow does.
Another camera library
camera takes any object with this shape (CameraAdapter):
interface CameraAdapter {
requestPermission(): Promise<boolean>;
// forwardRef component; fill `style` with the preview in "cover" mode
View: ComponentType<{ facing: 'back' | 'front'; active: boolean; style; onReady?(); onError?(code) } & RefAttributes<CameraHandle>>;
}
interface CameraHandle {
takePhoto(): Promise<{ uri; width; height }>; // size as displayed (EXIF applied)
takeSnapshot?(): Promise<{ uri; width; height }>; // low-res preview frame; enables the live quality gate
}src/adapters/vision-camera.tsx is the reference (about 60 lines). Without
takeSnapshot the shutter is simply always available.
What the SDK does for you
- Live quality gate, a few times a second: the region under the guide is scored for sharpness (Laplacian variance), glare and darkness with the same thresholds as the web SDK and the API's blur gate. The shutter lights up green when the view is clean and steady; it works anyway after 8 s, so no phone is ever blocked.
- Photo check on the review screen: VerifNer's vision model says what is wrong (« C'est le verso : photographiez le recto »). About 2 s, never blocking, not stored or billed.
- Crop to the guide with a small margin, resize to 1600 px on the long edge, JPEG stepped down from 0.85 to 0.6 until it fits about 300 KB.
- Retries on bad networks, with progress while uploading.
- Android back walks back through the screens.
What the alpha does not do yet
Compared with @verifner/web 0.5:
- No face detection on the selfie (the web's pico.js « Rapprochez-vous », « Une seule personne » hints). The selfie is checked for light and sharpness only; the API still compares faces.
- No Satoshi by default: the system font, or
fontFamily. - No console preview mode (
preview). - Live quality uses preview snapshots (
takeSnapshot), which costs more battery than frame processors and writes temporary files the OS clears. A frame-processor path is planned. - Not yet validated on a range of devices: the crop of documents to the guide depends on each camera's reported orientation; please report a device where the cropped photo is off.
- Only
react-native-vision-camera4 ships an adapter (5 is a different API); Expo Camera can be wrapped with the adapter interface above.
Contributing
See CONTRIBUTING.md for the development workflow and CODE_OF_CONDUCT.md for how we work together. Report security issues privately, as described in SECURITY.md. Never put a key, a client token, an identifier or a real identity document in an issue or a pull request.
npm install
npm run typecheck
npm run lint
npm test # Jest: quality gates, crop geometry, flow, client, pixels, theme
npm run build # lib/module + lib/typescript (react-native-builder-bob)Pre-releases are published under the next dist-tag:
npm install @verifner/react-native@next.
Licence
MIT.
