🎯 Learning Path:
Welcome to the REL-ID Identity Verification Biometric Opt-In & Opt-Out Flow codelab! This tutorial teaches you how to implement privacy-compliant biometric consent management including user enrollment, consent withdrawal, and transparent privacy controls.
In this codelab, you'll enhance your existing REL-ID application with:
By completing this codelab, you'll master:
Before starting this codelab, ensure you have:
The code to get started can be found in a GitHub repository.
You can clone the repository using the following command:
git clone https://github.com/uniken-public/codelab-react-native.git
Navigate to the relid-IDV-Optin-OPtout folder in the repository you cloned earlier
The biometric consent implementation requires these mandatory native plugins:
# Navigate to the codelab folder
cd relid-IDV-Optin-OPtout
# Place the required native plugins at root folder of this project:
# - react-native-rdna-client (base REL-ID SDK)
# - react-native-relid-idv-selfie-capture (selfie capture & biometric consent plugin)
# Install dependencies
npm install
# iOS additional setup (required for CocoaPods and biometric plugins)
cd ios && pod install && cd ..
# Run the application
npx react-native run-android
# or
npx react-native run-ios
AndroidManifest.xmlInfo.plistThe biometric consent plugin requires specific configuration:
pod install for biometric processingThis codelab extends your MFA application with core biometric consent components:
The biometric consent implementation adds the following files to your existing React Native project structure:
Component | Purpose | File Location |
Biometric Service Methods | Enhanced service with consent API methods |
|
Consent Event Handlers | Event manager with biometric consent callbacks |
|
Event Provider Integration | Global biometric consent event handling |
|
Navigation Integration | Biometric consent screen navigation |
|
Biometric Opt-In Screen | Primary opt-in interface with benefits |
|
Selfie Start Screen | Selfie capture initiation with guidance |
|
Frame Confirmation Screen | Captured biometric data review |
|
Consent Management Screen | Final consent with privacy controls |
|
After implementing biometric consent functionality, your project structure will include:
src/
├── uniken/
│ ├── services/
│ │ ├── rdnaEventManager.ts # Enhanced with biometric consent event handlers
│ │ └── rdnaService.ts # Enhanced with biometric consent API methods
│ ├── providers/
│ │ └── SDKEventProvider.tsx # Enhanced with biometric consent event integration
│ └── types/
│ └── rdnaEvents.ts # Enhanced with biometric consent event interfaces
├── tutorial/
│ ├── navigation/
│ │ └── AppNavigator.tsx # Enhanced with biometric consent screen registration
│ └── screens/
│ ├── idv/ # NEW: Biometric consent screen components
│ │ ├── IDVSelfieProcessStartConfirmationScreen.tsx
│ │ ├── IDVOptInCapturedFrameConfirmationScreen.tsx
│ │ ├── IDVBiometricOptInConsentScreen.tsx
│ │ └── index.ts
│ ├── mfa/ # Existing MFA authentication screens
│ └── components/ # Shared UI components
├── assets/
└── react-native-plugins/ # Required for biometric consent functionality
└── react-native-relid-idv-selfie-capture/
# Android Permissions (android/app/src/main/AndroidManifest.xml)
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
# iOS Permissions (ios/relidCodelab/Info.plist)
<key>NSCameraUsageDescription</key>
<string>This app needs camera access for biometric enrollment and verification</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access for biometric liveness detection</string>
In today's digital landscape, applications increasingly rely on biometric authentication for enhanced security, but managing user consent for biometric data collection and processing presents significant challenges. Organizations must balance security benefits with privacy requirements while maintaining user trust and regulatory compliance.
📋 Unclear Consent Processes
🔒 Privacy and Security Concerns
⚖️ Regulatory Compliance Gaps
📱 Poor Biometric User Experience
The REL-ID Biometric Consent module provides a comprehensive solution that transforms biometric consent management from a compliance burden into a competitive advantage:
Privacy-First Biometric Enrollment
// Clear biometric consent workflow
biometricOptIn: {
transparentConsent: true, // Clear explanation of data usage
userControl: true, // Easy opt-in/opt-out mechanisms
privacyFirst: true, // Privacy-by-design principles
consentTracking: true, // Comprehensive consent audit trail
dataRetention: "controlled" // Clear data retention policies
}
Intuitive Consent Workflows
SDK-Native Consent Flow
Regulatory Framework Alignment
Event-Driven IDV Workflow
// Complete IDV event chain
const idvWorkflow = [
'getIDVDocumentScanProcessStartConfirmation', // Document capture initiation
'getIDVConfirmDocumentDetails', // Document data validation
'getIDVSelfieProcessStartConfirmation', // Selfie capture initiation
'getIDVSelfieConfirmation', // Selfie validation
'getIDVBiometricOptInConsent' // Template storage consent
];
Operational Efficiency Gains
Security & Compliance Benefits
Enhanced User Experience
Before implementing IDV functionality, let's understand the key SDK events and APIs that power the identity verification workflow.
The biometric consent process follows this privacy-focused event-driven pattern:
User Login → Biometric Consent Trigger → getBiometricOptInStatus → User Decision →
Selfie Capture (if opt-in) → getIDVSelfieProcessStartConfirmation → Captured Frame Review →
getIDVBiometricOptInConsent → Consent Completion → Authentication Available
The REL-ID SDK triggers these main events during the biometric consent flow:
Event Type | Description | User Action Required |
Biometric enrollment initiation | User decides to enable biometric authentication | |
Biometric consent withdrawal | User decides to disable biometric authentication | |
Selfie capture process initiation | User confirms readiness for biometric capture | |
Final biometric consent confirmation | User provides final consent for biometric storage |
Biometric consent functionality requires specific setup conditions:
Requirement | Description | Status Check |
| Server-side biometric consent feature flag | ✅ Required configuration |
| iOS/Android camera access | ✅ Must be granted before biometric capture |
| Clear biometric data usage policy | ✅ Must be accessible to users |
The IDV Biometric Opt-In/Opt-Out module implements these key API methods:
// src/uniken/services/rdnaService.ts (Actual IDV API methods)
/**
* Initiates IDV biometric opt-in process for post-login users
*/
async initiateIDVBiometricOptIn(reason: string): Promise<RDNASyncResponse>
/**
* Initiates IDV biometric opt-out process for post-login users
*/
async initiateIDVBiometricOptOut(reason: string): Promise<RDNASyncResponse>
/**
* Sets IDV selfie process start confirmation
*/
async setIDVSelfieProcessStartConfirmation(
isConfirm: boolean,
useDeviceBackCamera: boolean,
idvWorkflow: number
): Promise<RDNASyncResponse>
/**
* Sets IDV biometric opt-in consent response
*/
async setIDVBiometricOptInConsent(
isOptIn: boolean,
challengeMode: number
): Promise<RDNASyncResponse>
/**
* Sets IDV biometric opt-in confirmation status
*/
async setIDVBiometricOptInConfirmation(status: number): Promise<RDNASyncResponse>
Let's implement the complete biometric consent API methods in your service layer following established REL-ID SDK patterns.
The rdnaService.ts already includes the essential IDV biometric consent methods. Here are the key methods implemented:
// src/uniken/services/rdnaService.ts (IDV methods already implemented)
/**
* Initiates IDV biometric opt-in process for post-login users
*/
async initiateIDVBiometricOptIn(reason: string): Promise<RDNASyncResponse> {
return new Promise((resolve, reject) => {
console.log('RdnaService - Initiating IDV biometric opt-in with reason:', reason);
RdnaClient.initiateIDVBiometricOptIn((response: any) => {
console.log('RdnaService - InitiateIDVBiometricOptIn sync callback received');
const result: RDNASyncResponse = response as any;
if (result.error && result.error.longErrorCode === 0) {
console.log('RdnaService - InitiateIDVBiometricOptIn sync response success, waiting for IDV events');
resolve(result);
} else {
console.error('RdnaService - InitiateIDVBiometricOptIn sync response error:', result);
reject(result);
}
});
});
}
/**
* Initiates IDV biometric opt-out process for post-login users
*/
async initiateIDVBiometricOptOut(reason: string): Promise<RDNASyncResponse> {
return new Promise((resolve, reject) => {
console.log('RdnaService - Initiating IDV biometric opt-out with reason:', reason);
RdnaClient.initiateIDVBiometricOptOut((response: any) => {
console.log('RdnaService - InitiateIDVBiometricOptOut sync callback received');
const result: RDNASyncResponse = response as any;
if (result.error && result.error.longErrorCode === 0) {
console.log('RdnaService - InitiateIDVBiometricOptOut sync response success, waiting for IDV events');
resolve(result);
} else {
console.error('RdnaService - InitiateIDVBiometricOptOut sync response error:', result);
reject(result);
}
});
});
}
/**
* Sets IDV selfie process start confirmation
*/
async setIDVSelfieProcessStartConfirmation(
isConfirm: boolean,
useDeviceBackCamera: boolean = false,
idvWorkflow: number
): Promise<RDNASyncResponse> {
return new Promise((resolve, reject) => {
console.log('RdnaService - Setting IDV selfie process start confirmation:', {
isConfirm, useDeviceBackCamera, idvWorkflow
});
RdnaClient.setIDVSelfieProcessStartConfirmation(isConfirm, useDeviceBackCamera, idvWorkflow, response => {
console.log('RdnaService - SetIDVSelfieProcessStartConfirmation sync callback received');
const result: RDNASyncResponse = response as any;
if (result.error && result.error.longErrorCode === 0) {
console.log('RdnaService - SetIDVSelfieProcessStartConfirmation sync response success, waiting for getIDVSelfieProcessStartConfirmation event');
resolve(result);
} else {
console.error('RdnaService - SetIDVSelfieProcessStartConfirmation sync response error:', result);
reject(result);
}
});
});
}
/**
* Sets IDV biometric opt-in consent
*/
async setIDVBiometricOptInConsent(isOptIn: boolean, challengeMode: number): Promise<RDNASyncResponse> {
return new Promise((resolve, reject) => {
console.log('RdnaService - Setting IDV biometric opt-in consent:', { isOptIn, challengeMode });
RdnaClient.setIDVBiometricOptInConsent(isOptIn, challengeMode, response => {
console.log('RdnaService - SetIDVBiometricOptInConsent sync callback received');
const result: RDNASyncResponse = response as any;
if (result.error && result.error.longErrorCode === 0) {
console.log('RdnaService - SetIDVBiometricOptInConsent sync response success');
resolve(result);
} else {
console.error('RdnaService - SetIDVBiometricOptInConsent sync response error:', result);
reject(result);
}
});
});
}
/**
* Sets IDV biometric opt-in confirmation status
*/
async setIDVBiometricOptInConfirmation(status: number): Promise<RDNASyncResponse> {
console.log('RdnaService - Setting IDV biometric opt-in confirmation with status:', status);
return new Promise((resolve, reject) => {
RdnaClient.setIDVBiometricOptInConfirmation(status, (response: any) => {
console.log('RdnaService - SetIDVBiometricOptInConfirmation sync callback received');
const result: RDNASyncResponse = response as any;
if (result.error && result.error.longErrorCode === 0) {
console.log('RdnaService - SetIDVBiometricOptInConfirmation sync response success, waiting for status events');
resolve(result);
} else {
console.error('RdnaService - SetIDVBiometricOptInConfirmation sync response error:', result);
reject(result);
}
});
});
}
Notice how these implementations follow the exact pattern established by other service methods:
Pattern Element | Implementation Detail |
Promise Wrapper | Wraps native sync SDK callback in Promise for async/await usage |
Parameter Validation | Validates |
Comprehensive Logging | Detailed console logging for debugging IDV flows |
Error Handling | Proper reject/resolve based on sync response patterns |
Configure your event manager to handle the complete IDV event chain with proper navigation coordination.
The IDV event handlers are already implemented in the project. Here are the actual implemented handlers:
// src/uniken/services/rdnaEventManager.ts (Actual IDV event handlers implemented)
/**
* Handles IDV selfie process start confirmation request events
*/
private onGetIDVSelfieProcessStartConfirmation(response: RDNAJsonResponse) {
console.log("RdnaEventManager - Get IDV selfie process start confirmation event received");
try {
const idvSelfieData: RDNAGetIDVSelfieProcessStartConfirmationData = JSON.parse(response.response);
console.log("RdnaEventManager - Get IDV selfie confirmation status:", idvSelfieData);
console.log("RdnaEventManager - UserID:", idvSelfieData.userID, "IDV Workflow:", idvSelfieData.idvWorkflow);
console.log("RdnaEventManager - Use back camera:", idvSelfieData.useDeviceBackCamera);
if (this.getIDVSelfieProcessStartConfirmationHandler) {
this.dispatchToMainThread(() => {
this.getIDVSelfieProcessStartConfirmationHandler?.(idvSelfieData);
});
}
} catch (error) {
console.error("RdnaEventManager - Failed to parse get IDV selfie process start confirmation:", error);
}
}
/**
* Handles IDV biometric opt-in consent events
*/
private onGetIDVBiometricOptInConsent(response: RDNAJsonResponse) {
console.log("RdnaEventManager - Get IDV biometric opt-in consent event received");
try {
const idvBiometricOptInData: RDNAGetIDVBiometricOptInConsentData = JSON.parse(response.response);
console.log("RdnaEventManager - IDV biometric opt-in consent status:", idvBiometricOptInData?.challengeResponse?.status?.statusCode);
console.log("RdnaEventManager - Challenge mode:", idvBiometricOptInData?.challengeMode);
console.log("RdnaEventManager - UserID:", idvBiometricOptInData?.userID);
if (this.getIDVBiometricOptInConsentHandler) {
this.dispatchToMainThread(() => {
this.getIDVBiometricOptInConsentHandler?.(idvBiometricOptInData);
});
}
} catch (error) {
console.error("RdnaEventManager - Failed to parse get IDV biometric opt-in consent:", error);
}
}
/**
* Handles IDV biometric opt-in status events
*/
private onIDVBiometricOptInStatus(response: RDNAJsonResponse) {
console.log("RdnaEventManager - IDV biometric opt-in status event received");
try {
const idvBiometricOptInStatusData: RDNAIDVBiometricOptInStatusData = JSON.parse(response.response);
console.log("RdnaEventManager - IDV biometric opt-in status:", idvBiometricOptInStatusData?.status?.statusCode);
console.log("RdnaEventManager - Error code:", idvBiometricOptInStatusData?.error?.shortErrorCode);
console.log("RdnaEventManager - UserID:", idvBiometricOptInStatusData?.userID);
if (this.idvBiometricOptInStatusHandler) {
this.dispatchToMainThread(() => {
this.idvBiometricOptInStatusHandler?.(idvBiometricOptInStatusData);
});
}
} catch (error) {
console.error("RdnaEventManager - Failed to parse IDV biometric opt-in status:", error);
}
}
/**
* Handles IDV biometric opt-out status events
*/
private onIDVBiometricOptOutStatus(response: RDNAJsonResponse) {
console.log("RdnaEventManager - IDV biometric opt-out status event received");
try {
const idvBiometricOptOutStatusData: RDNAIDVBiometricOptOutStatusData = JSON.parse(response.response);
console.log("RdnaEventManager - IDV biometric opt-out status:", idvBiometricOptOutStatusData?.status?.statusCode);
console.log("RdnaEventManager - Error code:", idvBiometricOptOutStatusData?.error?.shortErrorCode);
console.log("RdnaEventManager - UserID:", idvBiometricOptOutStatusData?.userID);
if (this.idvBiometricOptOutStatusHandler) {
this.dispatchToMainThread(() => {
this.idvBiometricOptOutStatusHandler?.(idvBiometricOptOutStatusData);
});
}
} catch (error) {
console.error("RdnaEventManager - Failed to parse IDV biometric opt-out status:", error);
}
}
/**
* Handles IDV opt-in captured frame confirmation events
*/
private onIDVOptInCapturedFrameConfirmation(response: RDNAJsonResponse | any) {
console.log("RdnaEventManager - IDV opt-in captured frame confirmation event received");
try {
let idvOptInCapturedFrameData: RDNAIDVOptInCapturedFrameConfirmationData;
// Handle different response formats
if (typeof response === 'string') {
idvOptInCapturedFrameData = JSON.parse(response);
} else if (response?.response) {
idvOptInCapturedFrameData = response.response;
} else {
idvOptInCapturedFrameData = response;
}
console.log("RdnaEventManager - IDV opt-in captured frame data:", idvOptInCapturedFrameData);
if (this.idvOptInCapturedFrameConfirmationHandler) {
this.dispatchToMainThread(() => {
this.idvOptInCapturedFrameConfirmationHandler?.(idvOptInCapturedFrameData);
});
}
} catch (error) {
console.error("RdnaEventManager - Failed to parse IDV opt-in captured frame confirmation:", error);
}
}
The event handlers are automatically registered in the event manager initialization. Here are the public handler setter methods available:
// src/uniken/services/rdnaEventManager.ts (Available handler setters)
/**
* IDV Event Handler Setters - Available public methods
*/
// IDV selfie process start confirmation handler
public setGetIDVSelfieProcessStartConfirmationHandler(callback?: RDNAGetIDVSelfieProcessStartConfirmationCallback): void {
this.getIDVSelfieProcessStartConfirmationHandler = callback;
}
// IDV biometric opt-in consent handler
public setGetIDVBiometricOptInConsentHandler(callback?: RDNAGetIDVBiometricOptInConsentCallback): void {
this.getIDVBiometricOptInConsentHandler = callback;
}
// IDV biometric opt-in status handler
public setIDVBiometricOptInStatusHandler(callback?: RDNAIDVBiometricOptInStatusCallback): void {
this.idvBiometricOptInStatusHandler = callback;
}
// IDV biometric opt-out status handler
public setIDVBiometricOptOutStatusHandler(callback?: RDNAIDVBiometricOptOutStatusCallback): void {
this.idvBiometricOptOutStatusHandler = callback;
}
// IDV opt-in captured frame confirmation handler
public setIDVOptInCapturedFrameConfirmationHandler(callback?: RDNAIDVOptInCapturedFrameConfirmationCallback): void {
this.idvOptInCapturedFrameConfirmationHandler = callback;
}
The native event listeners are automatically registered in the constructor. Here are the IDV events that are listened for:
// src/uniken/services/rdnaEventManager.ts (Native event listeners)
private registerEventListeners() {
console.log('RdnaEventManager - Registering native event listeners');
this.listeners.push(
// ... other event listeners ...
// IDV event listeners
this.rdnaEmitter.addListener('getIDVSelfieProcessStartConfirmation', this.onGetIDVSelfieProcessStartConfirmation.bind(this)),
this.rdnaEmitter.addListener('getIDVBiometricOptInConsent', this.onGetIDVBiometricOptInConsent.bind(this)),
// IDV biometric status event listeners
this.rdnaEmitter.addListener('onIDVBiometricOptInStatus', this.onIDVBiometricOptInStatus.bind(this)),
this.rdnaEmitter.addListener('onIDVBiometricOptOutStatus', this.onIDVBiometricOptOutStatus.bind(this)),
this.rdnaEmitter.addListener('onIDVOptInCapturedFrameConfirmation', this.onIDVOptInCapturedFrameConfirmation.bind(this)),
);
console.log('RdnaEventManager - Native event listeners registered');
}
### IDV Event Flow Summary
The actual implemented IDV event flow follows this sequence:
**Biometric Opt-In Flow:**
1. Call `RdnaService.initiateIDVBiometricOptIn(reason)`
2. SDK triggers `getIDVSelfieProcessStartConfirmation` event
3. Navigate to `IDVSelfieProcessStartConfirmationScreen`
4. User confirms and calls `setIDVSelfieProcessStartConfirmation`
5. SDK launches native selfie capture
6. SDK triggers `onIDVOptInCapturedFrameConfirmation` event
7. Navigate to `IDVOptInCapturedFrameConfirmationScreen`
8. User reviews frame and calls `setIDVBiometricOptInConfirmation`
9. SDK triggers `getIDVBiometricOptInConsent` event
10. Navigate to `IDVBiometricOptInConsentScreen`
11. User makes consent decision and calls `setIDVBiometricOptInConsent`
12. SDK triggers `onIDVBiometricOptInStatus` event with final status
**Biometric Opt-Out Flow:**
1. Call `RdnaService.initiateIDVBiometricOptOut(reason)`
2. SDK triggers `onIDVBiometricOptOutStatus` event with completion status
### Event Chain Flow Validation
All IDV events are properly logged and can be monitored for debugging:
```typescript
// src/uniken/services/rdnaEventManager.ts (debugging utility)
const debugIDVEventChain = () => {
console.log('=== IDV Event Chain Debug ===');
console.log('IDV Handler Registration Status:', {
documentScanStart: !!eventManager.onGetIDVDocumentScanProcessStartConfirmationCallback,
documentConfirm: !!eventManager.onGetIDVConfirmDocumentDetailsCallback,
selfieStart: !!eventManager.onGetIDVSelfieProcessStartConfirmationCallback,
selfieConfirm: !!eventManager.onGetIDVSelfieConfirmationCallback,
biometricConsent: !!eventManager.onGetIDVBiometricOptInConsentCallback
});
console.log('Expected IDV Flow:');
console.log('1. Document Scan Start → 2. Document Confirm → 3. Selfie Start → 4. Selfie Confirm → 5. Biometric Consent');
};
Now let's implement the three core biometric consent screen components following established patterns and best practices.
The biometric opt-in & opt-out flow requires these three screens:
All three screens are already implemented in the project and follow the established UI patterns.
This screen guides users through the selfie capture initiation process. The implementation is located at:
// src/tutorial/screens/idv/IDVSelfieProcessStartConfirmationScreen.tsx
Key Features:
setIDVSelfieProcessStartConfirmationThis screen allows users to review their captured biometric frame. The implementation is located at:
// src/tutorial/screens/idv/IDVOptInCapturedFrameConfirmationScreen.tsx
Key Features:
setIDVBiometricOptInConfirmation APIThis is the core consent screen where users make biometric template storage decisions. The implementation is located at:
// src/tutorial/screens/idv/IDVBiometricOptInConsentScreen.tsx
Key Features:
setIDVBiometricOptInConsent APIThe IDV biometric opt-in/opt-out flows are designed for post-login users who want to manage their biometric consent preferences.
// Initiate Biometric Opt-In Flow
// This allows users to enroll in biometric authentication
await RdnaService.initiateIDVBiometricOptIn("User Biometric Enrollment");
// Initiate Biometric Opt-Out Flow
// This allows users to remove their biometric templates and withdraw consent
await RdnaService.initiateIDVBiometricOptOut("User Biometric Removal");
// src/tutorial/screens/components/DrawerContent.tsx (Example implementation)
const handleBiometricOptIn = async () => {
try {
console.log('Dashboard - Starting biometric opt-in flow');
await RdnaService.initiateIDVBiometricOptIn("User requested biometric enrollment");
// SDK will automatically trigger getIDVSelfieProcessStartConfirmation event
// and navigate to IDVSelfieProcessStartConfirmationScreen
} catch (error) {
console.error('Dashboard - Failed to initiate biometric opt-in:', error);
Alert.alert('Error', 'Failed to start biometric enrollment. Please try again.');
}
};
const handleBiometricOptOut = async () => {
try {
console.log('Dashboard - Starting biometric opt-out flow');
await RdnaService.initiateIDVBiometricOptOut("User requested biometric removal");
// SDK will automatically trigger onIDVBiometricOptOutStatus event
// with the opt-out completion status
} catch (error) {
console.error('Dashboard - Failed to initiate biometric opt-out:', error);
Alert.alert('Error', 'Failed to start biometric removal. Please try again.');
}
};
Now let's integrate all IDV screens into your existing navigation structure following the established patterns.
The AppNavigator is already configured with the IDV screens. Here's the actual implementation:
// src/tutorial/navigation/AppNavigator.tsx (Actual IDV screen registration)
// Import IDV screens (already imported)
import IDVSelfieProcessStartConfirmationScreen from '../screens/idv/IDVSelfieProcessStartConfirmationScreen';
import IDVBiometricOptInConsentScreen from '../screens/idv/IDVBiometricOptInConsentScreen';
import IDVOptInCapturedFrameConfirmationScreen from '../screens/idv/IDVOptInCapturedFrameConfirmationScreen';
// Import RDNA types (already imported)
import type {
RDNAGetIDVSelfieProcessStartConfirmationData,
RDNAGetIDVBiometricOptInConsentData,
RDNAIDVOptInCapturedFrameConfirmationData
} from '../../uniken/types/rdnaEvents';
// IDV Screen Parameter Interfaces (already defined)
interface IDVSelfieProcessStartConfirmationScreenParams {
eventName: string;
eventData: RDNAGetIDVSelfieProcessStartConfirmationData;
title: string;
subtitle: string;
responseData?: RDNAGetIDVSelfieProcessStartConfirmationData;
}
interface IDVBiometricOptInConsentScreenParams {
title: string;
userDetails: RDNAGetIDVBiometricOptInConsentData;
}
interface IDVOptInCapturedFrameConfirmationScreenParams {
title: string;
capturedFrameData: RDNAIDVOptInCapturedFrameConfirmationData;
}
// RootStackParamList includes IDV screens (already configured)
export type RootStackParamList = {
// ... existing screens ...
// IDV Screens
IDVSelfieProcessStartConfirmationScreen: IDVSelfieProcessStartConfirmationScreenParams;
IDVBiometricOptInConsentScreen: IDVBiometricOptInConsentScreenParams;
IDVOptInCapturedFrameConfirmationScreen: IDVOptInCapturedFrameConfirmationScreenParams;
};
// Stack Screen Registration (already configured)
{/* IDV Screens */}
<Stack.Screen
name="IDVSelfieProcessStartConfirmationScreen"
component={IDVSelfieProcessStartConfirmationScreen}
options={{
title: 'Selfie Process Confirmation',
headerShown: false,
}}
/>
<Stack.Screen
name="IDVBiometricOptInConsentScreen"
component={IDVBiometricOptInConsentScreen}
options={{
title: 'IDV Biometric Opt-In Consent',
headerShown: false,
}}
/>
<Stack.Screen
name="IDVOptInCapturedFrameConfirmationScreen"
component={IDVOptInCapturedFrameConfirmationScreen}
options={{
title: 'IDV Captured Frame Confirmation',
headerShown: false,
}}
/>
The SDKEventProvider is already configured with IDV event handlers. Here's the actual implementation:
// src/uniken/providers/SDKEventProvider.tsx (Actual IDV handler implementation)
/**
* Event handler for IDV selfie process start confirmation requests
*/
const handleGetIDVSelfieProcessStartConfirmation = useCallback((data: RDNAGetIDVSelfieProcessStartConfirmationData) => {
console.log('SDKEventProvider - Get IDV selfie process start confirmation event received');
console.log('SDKEventProvider - UserID:', data.userID);
console.log('SDKEventProvider - IDV Workflow:', data.idvWorkflow);
console.log('SDKEventProvider - Use back camera:', data.useDeviceBackCamera);
// Navigate to the IDV selfie process start confirmation screen
NavigationService.navigateOrUpdate('IDVSelfieProcessStartConfirmationScreen', {
eventName: 'getIDVSelfieProcessStartConfirmation',
eventData: data,
title: 'Selfie Capture Information',
subtitle: `Prepare to capture your selfie for user: ${data.userID}`,
responseData: data,
});
}, []);
/**
* Event handler for IDV biometric opt-in consent requests
*/
const handleGetIDVBiometricOptInConsent = useCallback((data: RDNAGetIDVBiometricOptInConsentData) => {
console.log('SDKEventProvider - Get IDV biometric opt-in consent event received');
console.log('SDKEventProvider - UserID:', data?.userID);
console.log('SDKEventProvider - Challenge mode:', data?.challengeMode);
// Navigate to the IDV biometric opt-in consent screen
NavigationService.navigateOrUpdate('IDVBiometricOptInConsentScreen', {
title: 'Save Template',
userDetails: data,
});
}, []);
/**
* Event handler for IDV opt-in captured frame confirmation
*/
const handleIDVOptInCapturedFrameConfirmation = useCallback((data: RDNAIDVOptInCapturedFrameConfirmationData) => {
console.log('SDKEventProvider - IDV opt-in captured frame confirmation event received');
console.log('SDKEventProvider - UserID:', data?.userID);
console.log('SDKEventProvider - Challenge mode:', data?.challengeMode);
console.log('SDKEventProvider - Has captured image:', !!data?.capturedImage);
// Navigate to confirmation screen when we receive capturedImage data
if (data) {
console.log('SDKEventProvider - Captured image received, navigating to IDVOptInCapturedFrameConfirmationScreen');
NavigationService.navigateOrUpdate('IDVOptInCapturedFrameConfirmationScreen', {
title: 'Confirm Captured Frame',
capturedFrameData: data,
});
}
}, []);
/**
* Event handler for biometric opt-in status events
*/
const handleBiometricOptInStatus = useCallback((data: RDNAIDVBiometricOptInStatusData) => {
console.log('SDKEventProvider - Biometric opt-in status received:', data);
if (data?.error.shortErrorCode === 0 && data?.status.statusCode === 100) {
// Success - show success message and return to dashboard
Alert.alert('Success', data?.status.statusMessage, [
{
text: "OK",
onPress: () => {
NavigationService.navigate('DrawerNavigator', {
screen: 'Dashboard',
params: ''
});
}
}
], { cancelable: false });
} else {
// Handle error or other status codes
Alert.alert('Error', data?.error?.errorString, [
{
text: "OK",
onPress: () => {
NavigationService.navigate('DrawerNavigator', {
screen: 'Dashboard',
params: ''
});
}
}
], { cancelable: false });
}
}, []);
/**
* Event handler for biometric opt-out status events
*/
const handleBiometricOptOutStatus = useCallback((data: RDNAIDVBiometricOptOutStatusData) => {
console.log('SDKEventProvider - Biometric opt-out status received:', data);
Alert.alert('Success', data?.status.statusMessage, [
{
text: "OK",
onPress: () => {
NavigationService.navigate('DrawerNavigator', {
screen: 'Dashboard',
params: ''
});
}
}
], { cancelable: false });
}, []);
// Register IDV Event Handlers (in useEffect)
useEffect(() => {
const eventManager = rdnaService.getEventManager();
// ... existing MFA event handler registrations ...
// IDV event handlers (already registered)
eventManager.setGetIDVSelfieProcessStartConfirmationHandler(handleGetIDVSelfieProcessStartConfirmation);
eventManager.setGetIDVBiometricOptInConsentHandler(handleGetIDVBiometricOptInConsent);
eventManager.setIDVOptInCapturedFrameConfirmationHandler(handleIDVOptInCapturedFrameConfirmation);
eventManager.setIDVBiometricOptInStatusHandler(handleBiometricOptInStatus);
eventManager.setIDVBiometricOptOutStatusHandler(handleBiometricOptOutStatus);
console.log('SDKEventProvider - All IDV event handlers registered');
return () => {
console.log('SDKEventProvider - Component unmounting, cleaning up event handlers');
eventManager.cleanup();
};
}, []);
Ensure proper type safety across the IDV navigation flow:
// src/tutorial/navigation/NavigationService.ts (IDV navigation support)
export const NavigationService = {
navigationRef,
navigate: (name: keyof RootStackParamList, params?: any) => {
if (navigationRef.isReady()) {
navigationRef.navigate(name as never, params as never);
}
},
navigateOrUpdate: (name: keyof RootStackParamList, params?: any) => {
if (navigationRef.isReady()) {
// Check if already on the target screen to prevent duplicate navigation
const currentRoute = navigationRef.getCurrentRoute();
if (currentRoute?.name === name) {
console.log(`NavigationService - Already on ${name}, updating params`);
navigationRef.setParams(params);
} else {
console.log(`NavigationService - Navigating to ${name}`);
navigationRef.navigate(name as never, params as never);
}
}
}
};
Let's test your IDV implementation with comprehensive scenarios to ensure proper functionality across the entire identity verification flow.
Setup Requirements:
Test Steps:
RdnaService.initiateIDVBiometricOptIn("User Biometric Enrollment")getIDVSelfieProcessStartConfirmation eventIDVSelfieProcessStartConfirmationScreen appearssetIDVSelfieProcessStartConfirmationIDVOptInCapturedFrameConfirmationScreen appearssetIDVBiometricOptInConfirmationIDVBiometricOptInConsentScreen appearssetIDVBiometricOptInConsentTest Steps:
RdnaService.initiateIDVBiometricOptOut("User Biometric Removal")IDVBiometricOptInConsentScreen appearsExpected Results:
Test Steps:
Expected Results:
Test Steps:
IDVDocumentProcessStartConfirmationScreenIDVConfirmDocumentDetailsScreenIDVSelfieProcessStartConfirmationScreenExpected Results:
Test Steps:
Expected Results:
Test Requirements:
Prepare your IDV implementation for production deployment with these essential security, performance, and user experience considerations.
Here's your complete reference implementation combining all the patterns and best practices covered in this codelab.
The following screens are implemented in this project demonstrating the complete biometric consent workflow:

Users prepare for facial recognition capture with camera selection options and workflow-specific guidance for optimal selfie quality. Located at:
src/tutorial/screens/idv/IDVSelfieProcessStartConfirmationScreen.tsx

Review captured biometric frame with approve/retake options before proceeding to consent. Located at:
src/tutorial/screens/idv/IDVOptInCapturedFrameConfirmationScreen.tsx

Users provide consent for biometric template storage with clear privacy information and opt-out options following compliance requirements. Located at:
src/tutorial/screens/idv/IDVBiometricOptInConsentScreen.tsx
🔐 API Methods Implemented:
RdnaService.initiateIDVBiometricOptIn(reason) - Start biometric enrollment flowRdnaService.initiateIDVBiometricOptOut(reason) - Start biometric removal flowRdnaService.setIDVSelfieProcessStartConfirmation() - Confirm selfie capture startRdnaService.setIDVBiometricOptInConsent() - Submit consent decisionRdnaService.setIDVBiometricOptInConfirmation() - Confirm captured frame📱 Event Handlers Implemented:
getIDVSelfieProcessStartConfirmation → Navigate to selfie start screenonIDVOptInCapturedFrameConfirmation → Navigate to frame confirmation screengetIDVBiometricOptInConsent → Navigate to consent screenonIDVBiometricOptInStatus → Handle opt-in completiononIDVBiometricOptOutStatus → Handle opt-out completion🎯 Navigation Integration:
Congratulations! You've successfully implemented comprehensive biometric consent management functionality with the REL-ID SDK.
✅ Complete Biometric Consent Integration - Full biometric opt-in/opt-out workflow implementing 3 core screens
✅ Event-Driven Architecture - Proper handling of biometric consent event chains with seamless navigation
✅ Privacy-First Implementation - Production-ready biometric consent with comprehensive privacy protection
✅ User Experience Excellence - Intuitive biometric enrollment with clear guidance and transparent consent options
✅ Regulatory Compliance - GDPR/CCPA compliant consent management with proper opt-out mechanisms
Consider implementing these advanced features to enhance your biometric consent capabilities:
Your biometric consent implementation seamlessly integrates with:
🔐 You've mastered secure biometric consent management with REL-ID SDK!
Your implementation provides users with transparent, privacy-first biometric consent management while maintaining the highest security and compliance standards. Use this foundation to build trust and enhance security in your applications with confidence.