🎯 Learning Path:

  1. Complete REL-ID Complete Activation & Login Flow Codelab
  2. Complete REL-ID Data Signing Flow Codelab
  3. You are here β†’ Add IDV Server Biometric Step-Up (Workflow 16)

Welcome to the REL-ID IDV Server Biometric Step-Up for Data Signing codelab! This tutorial shows you how to add high-security biometric authentication with liveness detection to your existing data signing implementation.

What You'll Build

You'll enhance your existing data signing app with:

What You'll Learn

By completing this codelab, you'll master:

  1. IDV Workflow 16 Integration: Implementing authLevel = 4 with authenticatorType = 1 for server biometric
  2. NavigationService Pattern: SPA-based navigation for cross-screen management
  3. Selfie Screen Detection: Detecting and closing IDVSelfieProcessStart screen in screen-level handler
  4. Screen Transition Timing: Adding 300ms delay after selfie closure for smooth UI updates
  5. Error Priority Checking: Implementing proper error handling (error code first, then status code)
  6. Screen-Level Architecture: Understanding data signing screen-level event handling in Cordova

Prerequisites

Before starting, ensure you have:

IDV Asset Configuration

The IDV feature requires the Aware FaceCapture library to be configured in your local Cordova plugin. This library provides liveness detection capabilities for server biometric authentication.

Location: ./RdnaClient/ (local plugin directory in your project root)

Key Integration Points:

Verification:

# Verify local plugin structure
ls -la ./RdnaClient

# Check if plugin is installed
cordova plugin ls | grep rdna

Expected output:

cordova-plugin-rdna (installed from local path: ./RdnaClient)

Get the Code

Navigate to the relid-IDV-post-login-data-signing folder in your cloned repository:

cd relid-IDV-post-login-data-signing

Before implementing, let's understand what makes IDV Workflow 16 unique and why server biometric step-up requires special handling.

What is IDV Workflow 16?

IDV Workflow 16 is the server biometric step-up authentication flow specifically designed for data signing operations. Unlike other IDV workflows, workflow 16:

IDV Workflow Comparison

IDV Workflow

Scenario

When Used

Handler Location

16

Data Signing Step-Up Auth

High-security data signing

Screen-level (DataSigningInputScreen)

9

Notification Step-Up Auth

Sensitive notification actions

Screen-level (GetNotificationsScreen)

10

Biometric Opt-In

User enrolling in biometric

Standalone flow

6

Post-Login KYC

Know Your Customer verification

Standalone flow

Why Screen-Level Handler?

Unlike notifications (workflow 9) which also use screen-level handlers, data signing uses a screen-level handler in Cordova because:

  1. SPA Navigation Pattern: Cordova uses template-based navigation where screens persist
  2. Centralized State: All data signing state lives in DataSigningInputScreen object
  3. Clean Architecture: One place for all data signing event handling
  4. Reusability: Can be used from any screen that implements data signing
  5. Simpler Implementation: Direct navigation detection via NavigationService.currentRoute

Server Biometric Event Flow

User Submits Form (authLevel=4, authenticatorType=1) β†’
authenticateUserAndSignData(payload, 4, 1, reason) β†’
SDK Returns Sync Response β†’
SDK Triggers: getIDVSelfieProcessStartConfirmation (idvWorkflow: 16) β†’
  [Global Handler] Navigate to IDVSelfieProcessStart β†’
User Starts Selfie Capture β†’
SDK Captures Selfie with Liveness Detection β†’
SDK Verifies Biometric on Server β†’
SDK Signs Data β†’
SDK Triggers: onAuthenticateUserAndSignData β†’
  [Screen Handler] Close selfie screen if present β†’
  [Screen Handler] Wait 300ms for transition β†’
  [Screen Handler] Check errors (priority: error code β†’ status code) β†’
  [Screen Handler] Navigate to result screen or show error

When to Use Server Biometric (AuthLevel 4)?

Use authLevel = 4 with authenticatorType = 1 for:

Let's understand the technical architecture we'll implement for IDV workflow 16 in DataSigningInputScreen.js.

Screen-Level Handler Pattern

We'll modify DataSigningInputScreen.js to add three key pieces:

  1. NavigationService Access: Direct access to current route detection
  2. Error Priority Processing: Function to check errors before showing results
  3. Selfie Screen Auto-Closure: Logic to detect and close selfie screen

Key Technical Concepts

1. NavigationService Pattern

Why NavigationService?

// Check current route
if (NavigationService.currentRoute === 'IDVSelfieProcessStart') {
  // Close screen by navigating back to DataSigningInput
  NavigationService.navigate('DataSigningInput', sessionParams);
}

2. Error Priority Checking

Priority 1: Check error.longErrorCode !== 0

Priority 2: Check status.statusCode !== 100

// PRIORITY 1: Check error code first
if (data.error && data.error.longErrorCode !== 0) {
  alert('Data Signing Failed\n\n' + errorMessage);
  return; // Stop processing
}

// PRIORITY 2: Check status code
if (data.status && data.status.statusCode !== 100) {
  alert('Data Signing Failed\n\n' + statusMessage);
  return; // Stop processing
}

// SUCCESS: Both checks passed
navigateToResults();

3. Screen Transition Delay

Why 300ms?

if (selfieScreenWasClosed) {
  setTimeout(() => {
    processDataSigningResult(data);
  }, 300); // Wait for transition
  return;
}

Implementation Flow

onAuthenticateUserAndSignData Event Triggered β†’
  ↓
handleDataSigningResponse Called β†’
  ↓
Check if IDVSelfieProcessStart screen is active β†’
  ↓
If Active:
  β”œβ”€ Navigate back to DataSigningInput screen
  β”œβ”€ Wait 300ms for transition
  └─ Process result with error priority checking
  ↓
If Not Active:
  └─ Process result immediately
  ↓
processDataSigningResult:
  β”œβ”€ Check error.longErrorCode !== 0 (Priority 1)
  β”œβ”€ Check status.statusCode !== 100 (Priority 2)
  └─ If success: Navigate to result screen

Files You'll Modify

In this codelab, you'll modify only one file:

File

Purpose

Changes

www/src/tutorial/screens/dataSigning/DataSigningInputScreen.js

Main data signing screen

Add selfie screen detection, error priority processing, auto-closure logic

All other files (services, managers, templates) remain unchanged from the base data signing codelab.

Let's add the function that detects whether the IDV selfie screen is currently active and closes it if present.

Add closeSelfieScreenIfPresent Function

Open www/src/tutorial/screens/dataSigning/DataSigningInputScreen.js and locate the handleDataSigningResponse function (around line 479 in the previous implementation).

Add this new function before handleDataSigningResponse (around line 362):

/**
 * Check if IDV selfie screen is active and close it if present
 * For IDV server biometric step-up (workflow 16), the selfie screen may still be
 * visible when data signing response arrives. This function detects and closes it.
 *
 * @returns {boolean} True if selfie screen was closed, false otherwise
 */
closeSelfieScreenIfPresent() {
  console.log('DataSigningInputScreen - Checking if selfie screen needs to be closed for data signing');

  try {
    // Check if IDVSelfieProcessStart screen is currently active
    if (NavigationService.currentRoute === 'IDVSelfieProcessStart') {
      console.log('DataSigningInputScreen - IDVSelfieProcessStart screen detected for data signing, closing it');

      // Get session params to pass when navigating back
      const sessionParams = {
        userID: this.userID,
        sessionID: this.sessionID
      };

      // Navigate back to DataSigningInput screen
      NavigationService.navigate('DataSigningInput', sessionParams);

      console.log('DataSigningInputScreen - Selfie screen closed');
      return true; // Screen was present and closed
    } else {
      console.log('DataSigningInputScreen - No IDVSelfieProcessStart screen on top, current route:', NavigationService.currentRoute);
    }
  } catch (error) {
    console.error('DataSigningInputScreen - Error checking/closing selfie screen:', error);
  }

  return false; // Screen not present or couldn't close
},

Understanding the Implementation

Key Points:

Cordova Navigation Pattern

// Check current screen
if (NavigationService.currentRoute === 'IDVSelfieProcessStart') {
  // Navigate back to close selfie screen
  NavigationService.navigate('DataSigningInput', sessionParams);
  return true; // Screen was closed
}

Why This Pattern?:

What's Next?

Now that we have selfie screen detection, we'll add error priority processing to handle results properly before showing them to the user.

Now let's add a dedicated function to process data signing results with proper error priority checking.

Add processDataSigningResult Function

In DataSigningInputScreen.js, add this new function after closeSelfieScreenIfPresent (around line 397):

/**
 * Process data signing result and update state
 * Extracted as separate function to support delayed processing after selfie screen closure
 *
 * @param {Object} data - Signing response data from SDK
 */
processDataSigningResult(data) {
  console.log('DataSigningInputScreen - Processing data signing result');

  this.setLoading(false);

  // Hide password modal (do this for all cases)
  if (window.PasswordChallengeModal) {
    PasswordChallengeModal.hide();
  }

  // PRIORITY 1: Check error code first
  if (data.error && data.error.longErrorCode !== 0) {
    const errorMessage = DataSigningService.getErrorMessage(data.error.longErrorCode);

    console.error('DataSigningInputScreen - Data signing error:', JSON.stringify({
      errorCode: data.error.longErrorCode,
      errorMessage: errorMessage
    }, null, 2));

    // Show error alert on DataSigningInputScreen
    window.alert('Data Signing Failed\n\n' + errorMessage);

    // Also show in error div for visibility
    this.showError(errorMessage);

    // Notify manager of error
    DataSigningSetupAuthManager.handleError(data.error);
    return; // Stop processing
  }

  // PRIORITY 2: Check status code
  if (data.status && data.status.statusCode !== 100) {
    const statusMessage = data.status.statusMessage || 'Operation failed with an unknown error';

    console.error('DataSigningInputScreen - Data signing status error:', JSON.stringify({
      statusCode: data.status.statusCode,
      statusMessage: statusMessage
    }, null, 2));

    // Show status error alert on DataSigningInputScreen
    window.alert('Data Signing Failed\n\n' + statusMessage);

    // Also show in error div
    this.showError(`Signing failed: ${statusMessage}`);

    // Notify manager of error
    DataSigningSetupAuthManager.handleError(data);
    return; // Stop processing
  }

  // SUCCESS: Both error and status checks passed
  console.log('DataSigningInputScreen - Data signing completed successfully');

  // Store response
  this.signingResponse = data;

  // Notify manager of success
  DataSigningSetupAuthManager.handleSuccess(data);

  // Navigate to result screen with data
  NavigationService.navigate('DataSigningResult', {
    resultData: data
  });
},

Understanding Error Priority

Priority 1 - Error Code Check:

if (data.error && data.error.longErrorCode !== 0) {
  // Authentication failed, biometric mismatch, user cancelled, etc.
  window.alert('Data Signing Failed\n\n' + errorMessage);
  return; // Stop - don't check status code
}

Priority 2 - Status Code Check:

if (data.status && data.status.statusCode !== 100) {
  // Operation failed at SDK level
  window.alert('Data Signing Failed\n\n' + statusMessage);
  return; // Stop - don't navigate
}

Success Path:

// Both checks passed - safe to show results
this.signingResponse = data;
DataSigningSetupAuthManager.handleSuccess(data);
NavigationService.navigate('DataSigningResult', { resultData: data });

Key Features

  1. State Updates: Clears loading state, hides password modal
  2. Priority Checking: Error code checked before status code
  3. Early Return: Stops processing on error (prevents navigation behind alerts)
  4. User-Friendly Messages: Uses DataSigningService.getErrorMessage() for readable errors
  5. Form Preservation: Keeps form data for retry on error (screen doesn't reload)

What's Next?

Now we have error priority processing ready. In the next step, we'll implement the main handler that:

  1. Detects if selfie screen is active
  2. Closes it if present
  3. Waits 300ms for transition
  4. Calls our processDataSigningResult function

Now let's implement the main handler that detects and closes the selfie screen before processing results.

Replace handleDataSigningResponse Function

In DataSigningInputScreen.js, replace your existing

handleDataSigningResponse

function with this enhanced version (around line 469):

/**
 * Handle data signing response event
 *
 * For IDV server biometric step-up (workflow 16), this handler checks if the
 * selfie screen is currently active and closes it before processing the result.
 * This ensures smooth UI transitions and prevents alerts from appearing behind
 * the selfie screen.
 *
 * @param {Object} data - Signing response data from SDK
 */
handleDataSigningResponse(data) {
  console.log('DataSigningInputScreen - Data signing response received:', JSON.stringify({
    statusCode: data.status?.statusCode,
    errorCode: data.error?.longErrorCode,
    signatureLength: data.payloadSignature?.length
  }, null, 2));

  // Check and close selfie screen if present
  const selfieScreenWasClosed = this.closeSelfieScreenIfPresent();

  if (selfieScreenWasClosed) {
    // Add 300ms delay for screen transition
    console.log('DataSigningInputScreen - Delaying result processing for screen transition (300ms)');

    setTimeout(() => {
      console.log('DataSigningInputScreen - Processing data signing result after screen close');
      this.processDataSigningResult(data);
    }, 300);

    return; // Exit early, let setTimeout handle the rest
  }

  // Selfie screen not present (password/LDA step-up), process immediately
  console.log('DataSigningInputScreen - Processing data signing result immediately');
  this.processDataSigningResult(data);
}

Understanding the Implementation

Part 1: Screen Detection and Closure

const selfieScreenWasClosed = this.closeSelfieScreenIfPresent();

What happens:

Part 2: Conditional Processing

if (selfieScreenWasClosed) {
  // Screen was closed - add delay for smooth transition
  setTimeout(() => {
    this.processDataSigningResult(data);
  }, 300);
  return;
}

// No screen to close - process immediately
this.processDataSigningResult(data);

Why This Pattern?:

Complete Event Flow

1. onAuthenticateUserAndSignData event fires
   ↓
2. handleDataSigningResponse called with response
   ↓
3. closeSelfieScreenIfPresent() executes:
   β”œβ”€ Check NavigationService.currentRoute β†’ 'IDVSelfieProcessStart'
   β”œβ”€ Call NavigationService.navigate('DataSigningInput') β†’ Screen closes
   └─ Return true
   ↓
4. selfieScreenWasClosed === true
   ↓
5. setTimeout 300ms
   ↓
6. processDataSigningResult(data):
   β”œβ”€ Check error.longErrorCode !== 0 β†’ βœ“ (0 = success)
   β”œβ”€ Check status.statusCode !== 100 β†’ βœ“ (100 = success)
   └─ Navigate to DataSigningResultScreen
   ↓
7. User sees results on clean screen

Common Scenarios

Scenario 1: Server Biometric Success

Scenario 2: Server Biometric Error

Scenario 3: Password/LDA Success

Scenario 4: Password/LDA Error

Error Handling

The implementation handles:

What's Next?

Your implementation is complete! Let's test it to verify everything works correctly.

Let's verify your IDV workflow 16 implementation works correctly across all scenarios.

Test 1: Server Biometric Success Flow

  1. Fill Data Signing Form:
    • Payload: "Transfer $10,000 to account 123456"
    • Auth Level: "4 - RDNA_AUTH_LEVEL_4"
    • Authenticator Type: "1 - RDNA_IDV_SERVER_BIOMETRIC"
    • Reason: "High-value financial transaction"
  2. Click "Sign Data" β†’ Loading state activates
  3. Verify Selfie Screen:
    • βœ… IDVSelfieProcessStart screen appears
    • βœ… Screen shows data signing specific guidelines
    • βœ… "Start Selfie Capture" button visible
  4. Complete Biometric Capture:
    • Tap "Start Selfie Capture"
    • Complete selfie with liveness detection
    • Wait for server verification
  5. Verify Auto-Closure:
    • βœ… Selfie screen automatically closes (navigates back to DataSigningInput)
    • βœ… DataSigningResultScreen appears (not behind selfie screen)
    • βœ… No flicker or UI glitches
    • βœ… Signature details all present

Expected Logs:

DataSigningInputScreen - Data signing response received
DataSigningInputScreen - Checking if selfie screen needs to be closed for data signing
DataSigningInputScreen - IDVSelfieProcessStart screen detected for data signing, closing it
DataSigningInputScreen - Selfie screen closed
DataSigningInputScreen - Delaying result processing for screen transition (300ms)
DataSigningInputScreen - Processing data signing result after screen close

Test 2: Server Biometric Error Handling

  1. Trigger Error Scenario:
    • Use same form values as Test 1
    • Cancel biometric capture OR
    • Fail liveness detection
  2. Verify Error Handling:
    • βœ… Selfie screen closes automatically
    • βœ… Alert appears on DataSigningInputScreen (not behind selfie screen)
    • βœ… Alert shows user-friendly error message
    • βœ… Form data preserved for retry

Expected Error Messages:

Test 3: Password/LDA Flow (No Selfie Screen)

  1. Test Password Flow:
    • Auth Level: "3 - RDNA_AUTH_LEVEL_3"
    • Authenticator Type: "2 - RDNA_AUTH_PASS"
    • Complete password entry
    • βœ… Results appear immediately (no 300ms delay)
    • βœ… No selfie screen involved
  2. Test LDA Flow:
    • Auth Level: "3 - RDNA_AUTH_LEVEL_3"
    • Authenticator Type: "3 - RDNA_AUTH_LDA"
    • Complete biometric prompt
    • βœ… Results appear immediately
    • βœ… No screen closure logic triggered

Expected Logs:

DataSigningInputScreen - Data signing response received
DataSigningInputScreen - Checking if selfie screen needs to be closed for data signing
DataSigningInputScreen - No IDVSelfieProcessStart screen on top, current route: DataSigningInput
DataSigningInputScreen - Processing data signing result immediately

Test 4: Navigation State

  1. Test from Different Screens:
    • Launch from dashboard
    • Launch from drawer menu
    • Launch after deep link
    • βœ… All cases should detect and close selfie screen correctly
  2. Test Navigation State:
    • Go back during selfie screen (before capture)
    • βœ… No errors in logs
    • βœ… State resets properly

Verification Checklist

Core Functionality:

Error Handling:

Other Authentication Methods:

Edge Cases:

Common Issues & Solutions

Issue: Selfie screen doesn't close

Issue: Alert appears behind selfie screen

Issue: Results appear for error responses

Issue: Password/LDA delayed unnecessarily

πŸŽ‰ You Did It!

You've successfully added IDV Server Biometric Step-Up Authentication (Workflow 16) to your data signing implementation!

What You Accomplished

Key Implementation Patterns

NavigationService for Screen Management:

// Check current screen
if (NavigationService.currentRoute === 'IDVSelfieProcessStart') {
  // Navigate back to close selfie screen
  NavigationService.navigate('DataSigningInput', sessionParams);
}

Error Priority Checking:

// Priority 1: Error code
if (data.error && data.error.longErrorCode !== 0) {
  alert('Data Signing Failed\n\n' + errorMessage);
  return;
}

// Priority 2: Status code
if (data.status && data.status.statusCode !== 100) {
  alert('Data Signing Failed\n\n' + statusMessage);
  return;
}

Conditional Delay for Smooth Transitions:

if (selfieScreenWasClosed) {
  setTimeout(() => {
    processDataSigningResult(data);
  }, 300);
  return;
}
processDataSigningResult(data);

Production Checklist

Before deploying, ensure:

Next Steps

Explore More REL-ID Features:

Additional Resources

πŸ” Congratulations! You've mastered IDV Server Biometric Step-Up Authentication for Data Signing with REL-ID SDK!

Your app now provides high-security biometric authentication with liveness detection for sensitive data signing operations, with a smooth, professional user experience!