kora-identity-react-native-sdk
v0.3.0
Published
Kora liveness check react native sdk
Readme
Kora Liveness Check React Native SDK
A modern React Native SDK for face liveness detection powered by a service-based architecture. Provides seamless integration with backend APIs and supports both passive and active liveness detection.
Features
- 🎯 Auto-Initialization: SDK initializes automatically when needed
- 🤖 Auto-Determined Liveness Type: Let the backend choose optimal liveness type automatically
- 🔄 Provider Agnostic: Currently supports YouVerify with easy extensibility
- 🛠️ TypeScript Support: Full type safety and IntelliSense
- 📱 React Native Optimized: Designed specifically for mobile apps
- 🎨 Customizable: Configurable branding, user info, and settings
- 🔒 Secure: Sandbox and production mode support
- 📊 Production Error Tracking: Automatic error reporting to Kora's monitoring system
- 🏗️ Modular Architecture: Clean separation of concerns with provider pattern
- 🚀 Future-Ready: Easily extensible for multiple liveness providers
Installation
npm install kora-identity-react-native-sdk
# or
yarn add kora-identity-react-native-sdkDependencies
Make sure you have react-native-webview installed:
npm install react-native-webview
# or
yarn add react-native-webviewFor iOS, run pod install:
cd ios && pod installCamera Permissions Setup
The SDK requires camera access for liveness verification. Follow these steps:
Android: Add to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />iOS: Add to ios/YourApp/Info.plist:
<key>NSCameraUsageDescription</key>
<string>This app needs camera access for liveness verification</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app may need microphone access for liveness verification</string>For detailed permission setup, see CAMERA_PERMISSIONS.md.
Security Features
✅ Built-in Security: The SDK automatically includes a secure FileProvider configuration that overrides the default react-native-webview configuration, protecting against potential file access vulnerabilities.
The SDK includes secure file provider paths that restrict access to only app-specific directories, preventing unauthorized access to external storage or system files.
Quick Start
Auto-Determined Liveness Type (Recommended)
The SDK can automatically determine whether to use active or passive liveness based on your backend configuration, eliminating the need to specify the type manually:
import React, { useState } from 'react';
import { View, Button, Alert } from 'react-native';
import { koraLivenessService } from 'kora-identity-react-native-sdk';
const App = () => {
const [livenessComponent, setLivenessComponent] =
useState<React.ReactElement | null>(null);
const startAutoLivenessCheck = async () => {
try {
// No type specified - backend will determine optimal liveness type
const component = await koraLivenessService.checkLiveness({
// SDK Configuration (auto-initialization)
publicKey: 'your-kora-public-key',
// User Configuration
user: {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
},
branding: {
color: '#2376F3',
},
onSuccess: (result) => {
Alert.alert('Success', 'Liveness check passed!');
console.log('Success result:', result);
setLivenessComponent(null);
},
onFailure: (result) => {
Alert.alert('Failed', `Liveness check failed: ${result.error}`);
setLivenessComponent(null);
},
onClose: () => {
setLivenessComponent(null);
},
});
setLivenessComponent(component);
} catch (error) {
Alert.alert('Error', `Failed to start: ${error}`);
}
};
if (livenessComponent) {
return <View style={{ flex: 1 }}>{livenessComponent}</View>;
}
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
<Button
title="Start Auto-Determined Liveness"
onPress={startAutoLivenessCheck}
/>
</View>
);
};Explicit Liveness Type (Backward Compatible)
You can still specify the liveness type explicitly if needed:
const App = () => {
const [livenessComponent, setLivenessComponent] =
useState<React.ReactElement | null>(null);
const startLivenessCheck = async () => {
try {
// Explicit type specification
const component = await koraLivenessService.checkLiveness({
type: 'passive', // Explicitly specify the type
publicKey: 'your-kora-public-key',
user: {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
},
branding: {
color: '#2376F3',
},
onSuccess: (result) => {
Alert.alert('Success', 'Liveness check passed!');
setLivenessComponent(null);
},
onFailure: (result) => {
Alert.alert('Failed', `Liveness check failed: ${result.error}`);
setLivenessComponent(null);
},
onClose: () => {
setLivenessComponent(null);
},
});
setLivenessComponent(component);
} catch (error) {
Alert.alert('Error', `Failed to start: ${error}`);
}
};
if (livenessComponent) {
return <View style={{ flex: 1 }}>{livenessComponent}</View>;
}
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
<Button title="Start Passive Liveness" onPress={startLivenessCheck} />
</View>
);
};Component Wrapper (Alternative)
For easier integration, you can use the LivenessCheck component wrapper:
import React, { useState } from 'react';
import { View, Button } from 'react-native';
import { LivenessCheck } from 'kora-identity-react-native-sdk';
const App = () => {
const [showLiveness, setShowLiveness] = useState(false);
if (showLiveness) {
return (
<View style={{ flex: 1 }}>
<LivenessCheck
publicKey="your-kora-public-key"
// type prop is optional - omit for auto-determination
config={{
user: {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
},
branding: {
color: '#2376F3',
},
}}
onSuccess={(result) => {
console.log('Success:', result);
setShowLiveness(false);
}}
onFailure={(result) => {
console.log('Failed:', result);
setShowLiveness(false);
}}
onClose={() => setShowLiveness(false)}
/>
</View>
);
}
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
<Button
title="Start Auto Liveness Check"
onPress={() => setShowLiveness(true)}
/>
</View>
);
};Manual Initialization (Alternative)
You can still manually initialize the service if preferred:
const App = () => {
const [livenessComponent, setLivenessComponent] =
useState<React.ReactElement | null>(null);
const startLivenessCheck = async () => {
try {
// Manual initialization (optional if using auto-initialization above)
await koraLivenessService.initialize({
publicKey: 'your-kora-public-key',
});
// Start liveness check with auto-determined type
const component = await koraLivenessService.checkLiveness({
user: { firstName: 'John', lastName: 'Doe' },
branding: { color: '#2376F3' },
onSuccess: (result) => {
/* handle success */
},
onFailure: (result) => {
/* handle failure */
},
onClose: () => {
/* handle close */
},
});
setLivenessComponent(component);
} catch (error) {
Alert.alert('Error', `Failed to start: ${error}`);
}
};
// ... rest of component
};Configuration Options
User Configuration
user: {
firstName: string; // Required
lastName?: string; // Optional
email?: string; // Optional
}Branding Configuration
branding: {
color?: string; // Primary color (hex code) - defaults to Kora blue (#2376F3) if not provided
logo?: string; // Custom logo URL or base64 string - optional
}Advanced Configuration
{
// Environment is automatically determined from public key:
// pk_test_* = sandbox, pk_live_* = live environment
apiBaseUrl?: string; // Override API endpoint (for internal testing only)
allowAudio?: boolean; // Allow audio prompts (default: false)
presentation?: 'modal' | 'page'; // Presentation mode
tasks?: Array<{ // Custom tasks
id: string;
difficulty?: 'easy' | 'medium' | 'hard';
timeout?: number;
maxBlinks?: number;
maxNods?: number;
}>;
}Note: The SDK automatically connects to Kora's production API. Backend URL configuration has been removed to ensure consistent service connectivity.
Architecture
Modular Design
The SDK is built with a modular, provider-agnostic architecture that ensures maintainability and extensibility:
// Core Architecture Layers
import { Logger } from 'kora-liveness-check-rn-sdk/utils';
import { BaseLivenessProvider } from 'kora-liveness-check-rn-sdk/providers';
import { ProviderFactory } from 'kora-liveness-check-rn-sdk/providers';Error Tracking & Logging
The SDK includes production-ready error tracking that automatically reports issues to Kora's monitoring system:
// Automatic error tracking (no setup required)
const component = await koraLivenessService.checkLiveness({
type: 'passive',
// ... other config
});
// Manual logging (for custom implementations)
import { Logger } from 'kora-liveness-check-rn-sdk/utils';
const logger = Logger.getInstance();
logger.logError('Custom error message', error, { userId: '123' });Provider System
The SDK supports multiple liveness providers through a factory pattern:
import {
ProviderFactory,
LivenessProvider,
} from 'kora-liveness-check-rn-sdk/providers';
// Get the current provider
const provider: LivenessProvider = ProviderFactory.getProvider('youverify');
// Extend for custom providers
class CustomProvider extends BaseLivenessProvider {
async initialize(config: any): Promise<void> {
// Custom initialization logic
}
async checkLiveness(type: string, config: any): Promise<any> {
// Custom liveness check implementation
}
}API Reference
Service API
koraLivenessService.checkLiveness(config)
Start a liveness check and return a React component with auto-initialization.
const component = await koraLivenessService.checkLiveness({
// Type Configuration (optional)
type?: 'passive' | 'active', // Optional: Omit for auto-determination
// SDK Configuration (auto-initialization)
publicKey: string; // Required: Your Kora public key
// User Configuration
user?: {
firstName: string;
lastName?: string;
email?: string;
};
// Branding Configuration
branding?: {
color?: string;
logo?: string;
name?: string;
};
// Task Configuration (optional)
allowAudio?: boolean;
tasks?: Array<{
id: string;
difficulty?: 'easy' | 'medium' | 'hard';
}>;
// Callbacks
onSuccess?: (result) => void;
onFailure?: (result) => void;
onClose?: () => void;
});Auto-Determination vs Explicit Type
// Auto-determined type (recommended)
const autoComponent = await koraLivenessService.checkLiveness({
publicKey: 'your-key',
user: { firstName: 'John' },
// No type or tasks - backend determines optimal settings
});
// Explicit type (backward compatible)
const explicitComponent = await koraLivenessService.checkLiveness({
type: 'active',
publicKey: 'your-key',
user: { firstName: 'John' },
tasks: [{ id: 'complete_the_circle' }], // Custom tasks
});koraLivenessService.initialize(options) (Optional)
Manually initialize the Kora Liveness service.
await koraLivenessService.initialize({
publicKey: string; // Required: Your Kora public key
});Component API
<LivenessCheck />
React component for liveness detection.
Props:
publicKey: Your Kora public key (required)type?: Liveness type - 'passive' | 'active' (optional - omit for auto-determination)config?: Configuration object (optional)onSuccess: Success callback function (required)onFailure: Failure callback function (required)onClose: Close callback function (required)onStart?: Optional start callbackstyle?: Optional style objectloadingComponent?: Optional loading componenterrorComponent?: Optional error component
useLivenessCheck(props)
Hook for managing liveness check state.
const {
result,
isLoading,
error,
startLivenessCheck,
handleSuccess,
handleFailure,
handleClose,
LivenessComponent,
} = useLivenessCheck({
publicKey: 'your-key',
type?: 'passive', // Optional - omit for auto-determination
config: { /* ... */ }
});Auto-Determined Liveness Type
Overview
The SDK now supports automatic liveness type determination, where the backend chooses the optimal liveness verification method based on your business configuration and requirements. This eliminates the need for developers to decide between 'active' and 'passive' liveness checks.
How It Works
- Omit the type parameter when calling
checkLiveness() - Backend determines optimal type based on your account configuration
- SDK extracts the type from the backend response (
liveness_check_type) - Component renders with the backend-determined settings
Benefits
- 🤖 Smart Defaults: Backend chooses the best liveness type for your use case
- 🔧 Simplified Integration: No need to decide between active/passive
- 🎯 Business Logic: Type determination based on your account settings
- 🔄 Backward Compatible: Explicit type specification still works
- ⚡ Optimized Flow: Conditional API payloads for better performance
Usage Examples
Auto-Determined Type (Recommended)
// Backend determines the optimal liveness type
const component = await koraLivenessService.checkLiveness({
publicKey: 'your-kora-public-key',
user: {
firstName: 'John',
lastName: 'Doe',
},
onSuccess: (result) => {
console.log('Liveness type was:', result.metadata?.type);
console.log('Success:', result);
},
onFailure: (result) => console.log('Failed:', result),
});Explicit Type (Backward Compatible)
// Explicitly specify the liveness type
const component = await koraLivenessService.checkLiveness({
type: 'active', // Force active liveness
publicKey: 'your-kora-public-key',
user: {
firstName: 'John',
lastName: 'Doe',
},
tasks: [{ id: 'complete_the_circle' }], // Custom tasks for active
onSuccess: (result) => console.log('Success:', result),
onFailure: (result) => console.log('Failed:', result),
});Comparison
| Feature | Auto-Determined | Explicit Type |
| -------------------- | ------------------------------ | ------------------------- |
| Type Parameter | Omitted | 'active' or 'passive' |
| Tasks Parameter | Omitted (backend decides) | Optional (custom tasks) |
| API Payload | Minimal (no type/tasks) | Full (includes type) |
| Backend Response | Contains liveness_check_type | Mirrors provided type |
| Use Case | Most applications | Custom requirements |
Configuration Details
When using auto-determined type:
- ✅ No type field is sent to the backend
- ✅ No tasks field is sent to the backend
- ✅ Backend responds with
liveness_check_type - ✅ SDK configures component with backend-determined settings
When using explicit type:
- ✅ Type field is sent to the backend
- ✅ Tasks field is sent if provided
- ✅ Backend mirrors the provided type
- ✅ SDK uses the explicitly provided settings
Liveness Types
Passive Liveness
- No user interaction required
- User just needs to look at the camera
- Faster and easier for users
- Good for most use cases
Active Liveness
- Requires user interaction (head movement, blinking, etc.)
- More secure against spoofing attacks
- Takes longer to complete
- Better for high-security applications
Available Tasks
The SDK supports various liveness verification tasks that can be configured based on your requirements:
Passive Liveness Tasks
passive- Default passive liveness detection- No user interaction required
- User simply looks at the camera
- Automatically set when using passive liveness type
Active Liveness Tasks
Active liveness tasks require user interaction and can be customized with additional parameters:
complete_the_circle (Recommended)
{
id: 'complete_the_circle', // or 'complete-the-circle'
difficulty: 'medium',
timeout: 30000
}- User traces a circle on screen
- Most commonly used active task
- Default task for active liveness detection
blink
{
id: 'blink',
difficulty: 'easy',
maxBlinks: 3
}- User performs blinking actions
- Configurable with
maxBlinksparameter
motions
{
id: 'motions',
difficulty: 'medium',
maxBlinks: 2,
maxNods: 2
}- Combined head movements and blinking
- Supports both
maxBlinksandmaxNodsparameters - More comprehensive interaction task
yes_or_no
{
id: 'yes_or_no', // or 'yes-or-no'
questions: [
{
question: 'Are you ready to proceed?',
answer: true,
errorMessage: 'Please nod your head to the right for yes'
}
]
}- User responds to yes/no questions by nodding
- Supports custom questions with configurable error messages
Task Configuration Parameters
Each task supports these optional configuration parameters:
interface LivenessTask {
id: string; // Task identifier (required)
difficulty?: 'easy' | 'medium' | 'hard'; // Task difficulty level
timeout?: number; // Task timeout in milliseconds
maxBlinks?: number; // Max blinks (for blink/motions tasks)
maxNods?: number; // Max nods (for motions/yes_or_no tasks)
questions?: Array<{ // For yes_or_no tasks
question: string;
answer: boolean;
errorMessage?: string;
}>;
}Usage Examples
Single Task
const component = await koraLivenessService.checkLiveness({
type: 'active',
publicKey: 'your-key',
tasks: [{
id: 'complete_the_circle',
difficulty: 'medium'
}],
// ... other config
});Multiple Tasks
const component = await koraLivenessService.checkLiveness({
type: 'active',
publicKey: 'your-key',
tasks: [
{ id: 'blink', maxBlinks: 3 },
{ id: 'motions', maxNods: 2, maxBlinks: 2 }
],
// ... other config
});Auto-Determined Tasks (Recommended)
// Let backend choose optimal tasks automatically
const component = await koraLivenessService.checkLiveness({
publicKey: 'your-key',
user: { firstName: 'John' },
// No type or tasks specified - backend determines everything
});Task Recommendations
- For simplicity: Use auto-determination (omit
typeandtasks) - For basic security: Use
complete_the_circle - For enhanced security: Combine multiple tasks like
['blink', 'motions'] - For custom flows: Use
yes_or_nowith custom questions - For user experience: Stick to 1-2 tasks maximum to avoid fatigue
Error Handling
The SDK provides comprehensive error handling:
// Service API
try {
const component = await koraLivenessService.checkLiveness({
type: 'passive',
publicKey: 'your-key',
onFailure: (result) => {
console.log('Error code:', result.error);
console.log('Error data:', result.data);
console.log('Metadata:', result.metadata);
},
});
} catch (error) {
console.log('Initialization error:', error.message);
}
// Component API
<LivenessCheck
publicKey="your-key"
onFailure={(result) => {
if (result.error) {
// Handle specific error
}
}}
errorComponent={<YourCustomErrorComponent />}
/>;Session Management
// Get current session information
const session = koraLivenessService.getCurrentSession();
console.log('Session ID:', session.sessionId);
console.log('Session Token:', session.sessionToken);
// Clear session data
koraLivenessService.clearCache();
// Reset service completely
koraLivenessService.reset();Debug Information
// Get debug information
const debug = koraLivenessService.getDebugInfo();
console.log('Debug info:', debug);Types
LivenessResult
interface LivenessResult {
success: boolean;
data?: any;
error?: string;
metadata?: Record<string, any>;
}KoraLivenessConfig
interface KoraLivenessConfig {
type?: 'active' | 'passive'; // Optional: Omit for auto-determination
publicKey?: string; // Environment auto-detected: pk_test_* = sandbox, pk_live_* = live
apiBaseUrl?: string; // Override API endpoint (for internal testing)
user?: LivenessUser;
branding?: LivenessBranding;
presentation?: 'modal' | 'page';
allowAudio?: boolean;
tasks?: LivenessTask[]; // Optional: Only for explicit type
onSuccess?: (result: LivenessResult) => void;
onFailure?: (result: LivenessResult) => void;
onClose?: () => void;
onStart?: () => void;
}KoraLivenessOptions (New Interface)
interface KoraLivenessOptions extends KoraLivenessConfig {
type?: LivenessType; // 'active' | 'passive' | undefined
}Migration Guide
From v1.x to v2.x (Auto-Determined Liveness)
Before (v1.x - Old Pattern)
// Step 1: Initialize
await koraLivenessService.initialize({
publicKey: 'your-key',
});
// Step 2: Start liveness with explicit type
const component = await koraLivenessService.checkLiveness('passive', {
user: { firstName: 'John' },
onSuccess: (result) => {
/* success */
},
});After (v2.x - New Pattern)
Option 1: Auto-Determined Type (Recommended)
// Single step: Auto-initialize and auto-determine type
const component = await koraLivenessService.checkLiveness({
publicKey: 'your-key',
user: { firstName: 'John' },
onSuccess: (result) => {
console.log('Backend chose type:', result.metadata?.type);
/* success */
},
});Option 2: Explicit Type (Backward Compatible)
// Single step: Auto-initialize with explicit type
const component = await koraLivenessService.checkLiveness({
type: 'passive', // Explicitly specify type
publicKey: 'your-key',
user: { firstName: 'John' },
onSuccess: (result) => {
/* success */
},
});Breaking Changes
- ✅ New Method Signature:
checkLiveness()now accepts a single config object instead of(type, config) - ✅ Type Parameter: Now optional - omit for auto-determination
- ✅ Backward Compatibility: Old pattern still supported via explicit
typeproperty - ✅ Enhanced Response: Success callback includes determined type in metadata
Migration Steps
- Update method calls from
checkLiveness(type, config)tocheckLiveness(config) - Add type to config if you want explicit type:
{ type: 'passive', ...config } - Remove type from config if you want auto-determination
- Update TypeScript types if using custom interfaces
Troubleshooting
Common Issues
WebView not loading: Ensure
react-native-webviewis properly installed and linked.Network errors: Check your API key and network connectivity.
iOS build issues: Run
cd ios && pod installafter installation.Android build issues: Ensure your Android project supports WebView.
Getting Help
- Check the example app in the repository for comprehensive configuration options
- Review the API documentation
- Contact Kora support for API key issues
Platform Support
- iOS: 11.0+
- Android: API 21+ (Android 5.0)
- React Native: 0.60+
License
This SDK is proprietary software. See LICENSE for details.
