React Native Implementation

React Native Project Requirements

Before React Native SDK integration, ensure your environment meets these requirements:

  • Node.js: v18+
  • npm: v9+ (or yarn v1.22+)
  • React: v17+
  • React Native: v0.72+
  • TypeScript (optional, but recommended): v5+
  • Android: 7.1+ (API level 25+)
  • iOS: 12.0+

React Native Installation

For React Native Users:

The code snippet below installs the SDK alongside its compulsory peer dependencies required to work seamlessly:

Install the SDK

npm install kora-identity-react-native-sdk

or

yarn add kora-identity-react-native-sdk

For iOS, install pods

cd ios && pod install && cd ..

Rebuild your App:

npx react-native run-android 
npx react-native run-ios

For Expo Users:

The code snippet below installs the SDK:

npm install kora-identity-react-native-sdk

or

yarn add kora-identity-react-native-sdk

These peer dependencies must be installed for the SDK to function properly. The code snippet below automatically selects the versions compatible with your Expo SDK version:

npx expo install react-native-webview

Rebuild your App

For Android:

npx expo prebuild --cleannpx expo run:android

For iOS:

npx expo prebuild --cleannpx expo run:ios
📘

NOTE: We recommend always fetching the latest stable version from npm.

React Native Permissions

Android

Add camera permission to your Android manifest:

<uses-permission android:name="android.permission.CAMERA" />

If your app flow requires audio support, you may also need microphone permission depending on your setup.

iOS

Add camera usage description to your Info.plist:

<key>NSCameraUsageDescription</key>
<string>This app needs camera access for liveness verification.</string>

If your app flow requires microphone access, add the appropriate microphone usage description as well.

React Native Import and Setup

import { koraLivenessService } from 'kora-identity-react-native-sdk';
import type {
  LivenessConfig,
  LivenessResult,
} from 'kora-identity-react-native-sdk';

You can also use the component wrapper:

import { LivenessCheck } from 'kora-identity-react-native-sdk';

React Native Configuration Example

const verificationConfig = {
  publicKey: 'your_public_key_here',
  user: {
    firstName: 'John',
    lastName: 'Doe',
    email: '[email protected]',
  },
  branding: {
    color: '#2376F3',
    name: 'Your Company',
    logo: 'https://your-domain.com/logo.png',
  },
  presentation: 'modal',
  allowAudio: false,
};

React Native Start Verification

Auto or Platform-Determined Flow

const startVerification = async () => {
  try {
    const component = await koraLivenessService.checkLiveness({
      ...verificationConfig,
      onSuccess: (result: LivenessResult) => {
        console.log('Verification successful:', result);
      },
      onFailure: (result: LivenessResult) => {
        console.log('Verification failed:', result);
      },
      onClose: () => {
        console.log('User closed verification');
      },
    });

    setLivenessComponent(component);
  } catch (error) {
    console.error('Failed to start verification:', error);
  }
};

Explicit Passive Liveness

const startPassiveVerification = async () => {
  const component = await koraLivenessService.checkLiveness({
    ...verificationConfig,
    type: 'passive',
    onSuccess: (result: LivenessResult) => {
      console.log('Passive verification successful:', result);
    },
    onFailure: (result: LivenessResult) => {
      console.log('Passive verification failed:', result);
    },
    onClose: () => {
      console.log('Verification closed');
    },
  });

  setLivenessComponent(component);
};

Explicit Active Liveness

const startActiveVerification = async () => {
  const component = await koraLivenessService.checkLiveness({
    ...verificationConfig,
    type: 'active',
    tasks: [
      {
        id: 'complete_the_circle',
        difficulty: 'medium',
        timeout: 30000,
      },
      {
        id: 'blink',
        difficulty: 'easy',
        maxBlinks: 3,
        timeout: 15000,
      },
    ],
    onSuccess: (result: LivenessResult) => {
      console.log('Active verification successful:', result);
    },
    onFailure: (result: LivenessResult) => {
      console.log('Active verification failed:', result);
    },
    onClose: () => {
      console.log('Verification closed');
    },
  });

  setLivenessComponent(component);
};

React Native Render Example

import React, { useState } from 'react';
import { View, Button } from 'react-native';
import { koraLivenessService } from 'kora-identity-react-native-sdk';
import type { LivenessResult } from 'kora-identity-react-native-sdk';

const App = () => {
  const [livenessComponent, setLivenessComponent] =
    useState<React.ReactElement | null>(null);
  const [result, setResult] = useState<LivenessResult | null>(null);

  const handleVerification = async () => {
    try {
      const component = await koraLivenessService.checkLiveness({
        publicKey: 'your_public_key',
        user: {
          firstName: 'John',
          lastName: 'Doe',
          email: '[email protected]',
        },
        onSuccess: (response) => {
          setResult(response);
          setLivenessComponent(null);
        },
        onFailure: (response) => {
          setResult(response);
          setLivenessComponent(null);
        },
        onClose: () => {
          setLivenessComponent(null);
        },
      });

      setLivenessComponent(component);
    } catch (error) {
      console.error('Verification error:', error);
    }
  };

  if (livenessComponent) {
    return <View style={{ flex: 1 }}>{livenessComponent}</View>;
  }

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button title="Start Verification" onPress={handleVerification} />
    </View>
  );
};

export default App;

React Native Alternative 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"
          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 Liveness Check"
        onPress={() => setShowLiveness(true)}
      />
    </View>
  );
};

export default App;

React Native Active Liveness Task Example

const customTasks = [
  {
    id: 'yes_or_no',
    difficulty: 'medium',
    questions: [
      {
        question: 'Are you ready to proceed?',
        answer: true,
        errorMessage: 'Please respond as instructed on screen',
      },
    ],
  },
  {
    id: 'motions',
    difficulty: 'hard',
    maxNods: 3,
    maxBlinks: 2,
    timeout: 45000,
  },
];

Universal Configuration Schema

The liveness verification service across all platforms uses a consistent configuration object schema:

📘

The following schema represents the universal configuration format. Platform-specific implementations may have slight variations in method names or import paths, but the core configuration structure remains consistent across React Native, Web, and Flutter.


{
  publicKey: string;                              // Your merchant API public key
  type?: 'active' | 'passive';                   // Liveness type (optional for auto-detection)
  debugMode?: boolean;                           // Enable debug logging
  sandboxEnvironment?: boolean;                  // Use sandbox environment for react native and flutter. Web gets it from the key.
  
  user: {                                        // User information (required)
    firstName: string;
    lastName?: string;
    email?: string;
  };
  
  branding?: {                                   // UI customization (optional)
    color?: string;                              // Primary brand color (hex)
    logo?: string;                               // Logo URL
    logoAlt?: string;
  };
  
  presentation?: 'modal' | 'page';               // Presentation mode
  allowAudio?: boolean;                          // Allow audio instructions
  
  tasks?: Array<{                                // Custom tasks for active liveness
    id: string;                                  // Task identifier
    difficulty?: 'easy' | 'medium' | 'hard';
    timeout?: number;                            // Task timeout in milliseconds
    maxBlinks?: number;                          // For blink tasks
    maxNods?: number;                            // For motion tasks
    questions?: Array<{                          // For yes/no tasks
      question: string;
      answer: boolean;
      errorMessage?: string;
    }>;
  }>;
  
idLookup?: {                                   // Optional ID lookup and facial match
    idType: string;
    fields: Record<string, unknown>;
    verificationConsent: boolean;
    validation?: {
      firstName?: string;
      lastName?: string;
      dateOfBirth?: string;
    };
  };

  // Callback functions
  onSuccess?: (result: LivenessResult) => void;
  onFailure?: (result: LivenessResult) => void;
  onClose?: () => void;
  onStart?: () => void;                   // available for RN and Flutter only 
}

Parameter Definitions

  • publicKey: (Required) Your merchant account's API public key
  • type: (Optional) Specify 'active' or 'passive' liveness check:
    • Active Liveness: User performs prompted actions (blink, turn head, say phrases). Best for higher-assurance checks where interaction is acceptable
    • Passive Liveness: System detects signs of life from natural video/image streams. Best for frictionless, low-to-medium risk flows.
      If omitted, the backend will auto-determine the best type based on risk assessment
  • user: (Required) Customer information including first name (required), last name, and email
  • branding: (Optional) Customize the user interface (UI) by configuring your brand color and logo. Note that the "Powered by Kora" footer remains fixed and cannot be modified or hidden.
  • presentation:(Optional) Choose between 'modal' (overlay) or 'page' (full screen) presentation
  • tasks: (Optional) For active liveness, specify custom tasks like blink, motion detection, or yes/no questions
  • idLookup: (Optional) Run an ID lookup and facial match alongside the liveness check. Requires explicit customer consent. See "ID lookup" below
  • onIdLookupResult: (Optional) Callback function executed when the ID lookup result is ready. Always fires after onSuccess, never before
  • allowAudio: (Optional) Enable audio instructions during verification
  • onSuccess: (Optional) Callback function executed when verification succeeds
  • onFailure: (Optional) Callback function executed when verification fails
  • onClose:(Optional) Callback function executed when the user closes the verification

The result object

onSuccess and onFailure both receive a LivenessResult:

{
  success: boolean;
  data?: any;
  error?: string;
  metadata?: Record<string, any>;
}

On a successful check, data carries the session reference and status:

{
  success: true,
  data: { reference: 'LC-...', status: 'passed' },
  metadata: { type: 'active' }
}

ID Lookup and Facial Match

A liveness check can carry an ID lookup. The customer's ID details are sent with the session, and the backend matches the captured face against the photo held for that ID. The liveness check runs exactly as it otherwise would — the customer sees no extra screens, and there is no ID entry UI in the SDK. You pass the details you already hold.
ID lookup is optional. Omit idLookup and the check behaves exactly as before.

Requesting a lookup

await koraLivenessService.checkLiveness({
  ...verificationConfig,
  idLookup: {
    idType: 'ng_bvn',
    fields: { id: '12345678901' },
    verificationConsent: true,
    validation: {
      firstName: 'Ada',
      lastName: 'Lovelace',
      dateOfBirth: '1990-12-10',
    },
  },
  onSuccess: (result) => {
    // Liveness passed. The ID lookup result is not ready yet.
  },
  onIdLookupResult: (result) => {
    if (result.verified) {
      // Face matched the ID on record.
    }
  },
});

idLookup fields

FieldRequiredDescription
idTypeYesThe ID type to look up, for example ng_bvn, ng_nin, ng_passport
fieldsYesThe ID values themselves, flat. { id: '12345678901' } for most types; extra keys where the type needs them. Must not be empty
verificationConsentYesMust be true. Collect explicit customer consent before starting a lookup — the SDK rejects the call otherwise
validationNoOptional cross-checks: firstName, lastName, dateOfBirth (YYYY-MM-DD)
ℹ️

Fields must not contain the keys id_type, verification_consent or validation

They are reserved keys and the SDK will reject them.

Receiving the result

The ID lookup finishes after the liveness check does, so the two results arrive separately. onSuccess fires as soon as liveness passes; onIdLookupResult fires once the lookup completes. The order is guaranteed: the ID result never arrives before onSuccess.

FieldDescription
referenceThe liveness session reference
statusThe liveness status for the session
workflowStatusWhether the full workflow completed
verifiedtrue only when liveness passed, the workflow completed, and the verification result was valid
verificationThe verification detail, when present
timedOuttrue if the result was still pending when polling gave up
errorThe reason the lookup could not be completed

Treat verified as the single answer. A failed liveness check can never report as verified, even where the ID itself was found.

Handling Result Timeouts

The SDK polls for the ID lookup result for approximately 90 seconds. If the result does not arrive within this window, the following behavior occurs:

Event Trigger: The onIdLookupResult callback fires with timedOut: true and no verification data.

Liveness Status: The liveness check is unaffected and operates independently onSuccess has already fired).

Fallback Resolution: Query the session reference server-side to retrieve the final outcome.



Did this page help you?