@nafsi/idv-react-native-sdk
v1.0.1
Published
JWT-based identity verification SDK for React Native with dynamic workflow support
Maintainers
Readme
Nafsi React Native SDK
JWT-based identity verification SDK for React Native with dynamic workflow support.
Features
- 🔐 JWT-Based Configuration - Initialize SDK with a single JWT token containing all configuration
- 📸 Automatic ID Cropping - Precise geometric mapping to crop ID cards to exact dimensions (590x372px)
- 📷 High-Quality Capture - Maximum quality (95%) with accurate dimension detection for sharp images
- 🎯 Accurate Cropping - Real photo dimensions used for geometric mapping (no more hardcoded values)
- 🎭 Dynamic Workflows - Conditionally capture selfie based on backend workflow configuration
- 🎨 Highly Customizable - Customize colors, logos, images, messages, and button text
- 🌐 Backend Branding Control - Full branding control via
react_native_sdk_configsin workflow - 📦 Compression Built-in - Automatic payload compression using pako for bandwidth optimization
- ✅ TypeScript Support - Full TypeScript support with comprehensive type definitions
- ⚡ Expo Compatible - Works seamlessly with Expo-managed projects
- 🚀 Auto-Start Demo Mode - Perfect for marketing portals and demos
Installation
npm install nafsi-react-native
# or
yarn add nafsi-react-nativePeer Dependencies
Install required peer dependencies:
expo install expo-camera expo-image-manipulator expo-file-system react-native-svgQuick Start
import React from 'react';
import {View} from 'react-native';
import NafsiVerification from 'nafsi-react-native';
export default function App() {
const jwtToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."; // From your backend
return (
<View style={{flex: 1}}>
<NafsiVerification
token={jwtToken}
onSuccess={(result) => {
console.log('Verification successful!', result);
// Navigate to success screen or handle result
}}
onError={(error) => {
console.error('Verification failed:', error);
// Show error message or retry
}}
onCancel={() => {
console.log('User cancelled verification');
// Navigate back
}}
/>
</View>
);
}JWT Token Format
The SDK expects a JWT token with the following payload structure:
{
"workflowId": "your-workflow-id",
"clientId": "your-client-id",
"organisationId": 14,
"apiUrl": "https://apisv2.windeal.co.ke/postdata",
"refresh_token": "your-refresh-token",
"config": "kenya_national_id",
"iat": 1767688286,
"exp": 1767691886
}Configuration Priority
The SDK uses a three-tier configuration system with the following priority order:
- Default values (lowest priority) - Built-in SDK defaults
- Developer customization (medium priority) - Props passed to
<NafsiVerification> - Workflow database config (highest priority) - Configuration from
getWorkflowByIdAPI response
This means that configuration from your workflow's react_native_sdk_configs or web_sdk_configs will override any
customization you provide via props.
Workflow SDK Config Example
When you call getWorkflowById, the response may include SDK configuration:
{
"workflow": {
"id": "workflow-123",
"react_native_sdk_configs": {
"logo": "https://example.com/logo.png",
"title": "Welcome to Naweza Africa",
"tagline": "Your leading Home Service Market in Africa!",
"hero_image": "https://example.com/hero.png",
"background_image": "https://example.com/background.jpg",
"colors": {
"primary": "#9333ea",
"secondary": "#6b21a8",
"text": "#1a1a1a",
"background": "#dbeafe"
}
}
}
}This workflow config will automatically override your prop-based customization.
Customization
Theme Colors
<NafsiVerification
token={jwtToken}
customization={{
theme: {
primaryColor: '#00b8ff',
secondaryColor: '#17D27C',
accentColor: '#fa764a',
backgroundColor: '#ffffff',
textColor: '#333333',
buttonTextColor: '#ffffff',
}
}}
/>Landing Page
<NafsiVerification
token={jwtToken}
customization={{
landing: {
backgroundImage: 'https://example.com/background.jpg',
logoUrl: 'https://example.com/logo.png',
heroMessage: 'Verify Your Identity',
tagline: 'Quick and secure verification in minutes',
getStartedButtonText: 'Start Verification',
}
}}
/>Messages
<NafsiVerification
token={jwtToken}
customization={{
messages: {
idFrontTitle: 'Capture ID Front',
idFrontSubtitle: 'Position your ID card within the frame',
idBackTitle: 'Capture ID Back',
idBackSubtitle: 'Position the back of your ID within the frame',
selfieTitle: 'Take a Selfie',
selfieSubtitle: 'Position your face within the oval',
reviewTitle: 'Review Your Captures',
startVerificationButton: 'Submit Verification',
retakeButton: 'Retake',
continueButton: 'Continue',
}
}}
/>Camera Settings
<NafsiVerification
token={jwtToken}
customization={{
camera: {
idFrameColor: '#17D27C',
selfieOutlineColor: '#17D27C',
captureButtonColor: '#00b8ff',
}
}}
/>API Reference
NafsiConfig Props
| Prop | Type | Required | Description |
|-----------------|----------------------------------------|----------|-----------------------------------------------------|
| token | string | Yes | JWT token containing workflow and API configuration |
| customization | NafsiCustomization | No | UI/UX customization options |
| onSuccess | (result: VerificationResult) => void | No | Callback fired when verification succeeds |
| onError | (error: NafsiError) => void | No | Callback fired when verification fails |
| onCancel | () => void | No | Callback fired when user cancels verification |
| onStepChange | (step: VerificationStep) => void | No | Callback fired when user moves to a different step |
VerificationResult
interface VerificationResult {
status: 'success' | 'failed' | 'pending';
verification_id?: string;
extracted_data?: {
full_name?: string;
id_number?: string;
date_of_birth?: string;
place_of_birth?: string;
gender?: string;
};
face_match?: {
match: boolean;
confidence: number;
};
liveness?: {
is_live: boolean;
confidence: number;
};
selfie_url?: string;
}Advanced Usage
Using Hooks Directly
For custom flows, you can use the hooks directly:
import {useNafsiFlow} from 'nafsi-react-native';
function CustomVerificationFlow() {
const {
currentStep,
requiresSelfie,
mergedCustomization, // Final merged configuration
images,
captureImage,
submitVerification,
isSubmitting,
} = useNafsiFlow({
token: jwtToken,
onSuccess: handleSuccess,
onError: handleError,
});
// Access the final merged configuration
console.log('Using theme:', mergedCustomization.theme);
console.log('Using landing config:', mergedCustomization.landing);
// Custom rendering logic
return <YourCustomUI/>;
}Understanding Configuration Merging
The SDK provides utilities to manually merge configurations if needed:
import {mergeConfigurations, getWorkflowSdkConfig} from 'nafsi-react-native';
// Get SDK config from workflow
const workflowConfig = getWorkflowSdkConfig(workflow);
// Merge with your customization
const merged = mergeConfigurations(
{
theme: {primaryColor: '#00b8ff'},
landing: {heroMessage: 'My Custom Message'}
},
workflowConfig
);
console.log('Merged config:', merged);
// Workflow config takes priority over your customizationUsing Services
Access low-level services for advanced use cases:
import {
ApiService,
TokenService,
WorkflowService,
CompressionService,
} from 'nafsi-react-native';
const apiService = new ApiService('https://api.example.com');
const tokenService = new TokenService(apiService);
const workflowService = new WorkflowService(apiService);
// Decode JWT
const decoded = tokenService.decodeJWT(jwtToken);
// Refresh access token
const accessToken = await tokenService.refreshAccessToken(
decoded.refresh_token,
decoded.organisationId
);
// Fetch workflow
const workflow = await workflowService.fetchWorkflow(
decoded.ss,
decoded.clientId,
accessToken,
decoded.organisationId
);
// Check if selfie is required
const requiresSelfie = workflowService.requiresSelfie(workflow);
// Compress data
const compressed = CompressionService.compress({data: 'example'});Workflow Configuration
The SDK dynamically determines which steps to show based on the workflow fetched from your backend.
Selfie Capture Conditions
Selfie capture is conditionally shown based on workflow steps:
- If workflow includes
face_matching,liveness_detection, orage_verification→ Selfie is required - Otherwise → Selfie is skipped
Example workflow response:
{
"workflow": {
"id": "workflow-123",
"name": "KYC Verification",
"steps": [
{
"name": "OCR Extraction",
"order": 1,
"required": true
},
{
"name": "Face Matching",
"order": 2,
"required": false
},
{
"name": "Liveness Detection",
"order": 3,
"required": false
}
]
}
}In this example, selfie would be captured because Face Matching and Liveness Detection are present.
Image Processing
ID Card Cropping
- ✅ Maximum quality capture - 100% quality (quality: 1.0)
- ✅ Actual dimension detection - Uses real photo dimensions from camera
- ✅ Accurate geometric mapping - Maps screen overlay to exact photo coordinates
- ✅ Automatic cropping to green frame overlay
- ✅ Output dimensions: Exactly 590×372 pixels (ISO/IEC 7810 ID-1 standard)
- ✅ High compression: JPEG at 95% quality (COMPRESS_QUALITY: 0.95)
How it works:
- Camera captures photo at native resolution (e.g., 4032×3024)
- Photo dimensions are stored in state
- Geometric algorithm maps green frame position to photo coordinates
- Image cropped to exact frame area
- Resized to standard 590×372px
- Compressed at 95% quality
Selfie Processing
- ✅ Cropping to outline - Crops to the oval outline shown on screen
- ✅ Geometric mapping - Same accurate algorithm as ID cards
- ✅ Resizing to 590px width (maintains aspect ratio)
- ✅ High compression: JPEG at 95% quality
Selfie outline dimensions:
- Screen overlay: 280×350 pixels (aspect ratio 0.8)
- Crops to this shape from full captured photo
Base64 Encoding
All images are converted to base64 data URLs:
data:image/jpeg;base64,/9j/4AAQSkZJRg...Error Handling
<NafsiVerification
token={jwtToken}
onError={(error) => {
switch (error.code) {
case 'INVALID_JWT':
console.error('Invalid JWT token');
break;
case 'TOKEN_REFRESH_FAILED':
console.error('Failed to refresh access token');
break;
case 'WORKFLOW_FETCH_FAILED':
console.error('Failed to fetch workflow');
break;
case 'CAMERA_PERMISSION_DENIED':
console.error('Camera permission denied');
break;
case 'VERIFICATION_FAILED':
console.error('Verification submission failed');
break;
default:
console.error('Unknown error:', error.message);
}
}}
/>API Integration
Initialization Flow
- Decode JWT → Extract
workflowId,clientId,apiUrl,organisationId,refresh_token - Refresh Access Token → POST
/postdatawithmenu: 'auth_config', method: 'refreshAccessToken' - Fetch Workflow → POST
/postdatawithmenu: 'flow', method: 'getWorkflowById' - Parse Steps → Determine if selfie is required
- Show Landing → Start verification flow
Verification Submission
- Build Payload → Include
client_id,work_flow_id, ID images, optional selfie - Compress Payload → Use pako.deflateRaw (level 9) → base64 encode
- Submit → POST
/postdatawithmenu: 'verification', method: 'submitVerification' - Handle Response → Call
onSuccessoronError
TypeScript Support
The SDK is fully typed. Import types as needed:
import type {
NafsiConfig,
VerificationResult,
NafsiError,
Workflow,
DecodedJWT,
} from 'nafsi-react-native';Demo Mode & Marketing Portal
The SDK includes an auto-start demo mode perfect for marketing portals and sales presentations.
Quick Demo Setup
See example/DEMO_SETUP.md for comprehensive guide.
Enable auto-start in example app:
// example/App.tsx
const AUTO_START_DEMO = true; // SDK starts automatically
const DEMO_TOKEN = `your-long-lived-demo-token`;Deploy demo to web:
cd example
npx expo export:web
vercel --prodResult: Instant live demo at https://your-demo.vercel.app
Embedding in Marketing Portal
<iframe
src="https://nafsi-demo.vercel.app"
style="width: 100%; height: 800px; border: 0;"
></iframe>Perfect for showcasing SDK capabilities to prospects without requiring them to configure tokens.
Documentation
- 📚 Distribution Guide - Publishing to npm, CocoaPods, Maven Central
- 🎯 Demo Setup - Marketing portal integration
- 🔄 SDK Initialization Flow - Architecture deep dive
- 📖 API Documentation - Full API reference (coming soon)
- 💡 Examples - Complete working examples
Requirements
- React Native >= 0.60.0
- Expo SDK >= 49.0.0 (if using Expo)
- iOS >= 13.0
- Android >= 6.0 (API level 23)
License
MIT
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 💬 Community: Slack Channel
- 📖 Docs: docs.nafsi.ai
Contributing
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
Changelog
See CHANGELOG.md for release notes.
