π― Learning Path:
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.
You'll enhance your existing data signing app with:
By completing this codelab, you'll master:
authLevel = 4 with authenticatorType = 1 for server biometricBefore starting, ensure you have:
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:
com.uniken.rdnaplugin.RdnaClient.*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)
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.
IDV Workflow 16 is the server biometric step-up authentication flow specifically designed for data signing operations. Unlike other IDV workflows, workflow 16:
authenticateUserAndSignData() is called with authLevel = 4 and authenticatorType = 1IDV 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 |
Unlike notifications (workflow 9) which also use screen-level handlers, data signing uses a screen-level handler in Cordova because:
NavigationService.currentRouteUser 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
Use authLevel = 4 with authenticatorType = 1 for:
Let's understand the technical architecture we'll implement for IDV workflow 16 in DataSigningInputScreen.js.
We'll modify DataSigningInputScreen.js to add three key pieces:
Why NavigationService?
NavigationService.currentRoute// Check current route
if (NavigationService.currentRoute === 'IDVSelfieProcessStart') {
// Close screen by navigating back to DataSigningInput
NavigationService.navigate('DataSigningInput', sessionParams);
}
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();
Why 300ms?
if (selfieScreenWasClosed) {
setTimeout(() => {
processDataSigningResult(data);
}, 300); // Wait for transition
return;
}
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
In this codelab, you'll modify only one file:
File | Purpose | Changes |
| 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.
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
},
Key Points:
NavigationService.currentRoute: Gets the current active screen name'IDVSelfieProcessStart' is currently activeNavigationService.navigate(): Navigates back to DataSigningInput screen (closes selfie screen)true if screen was closed, false otherwise// 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?:
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.
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
});
},
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 });
DataSigningService.getErrorMessage() for readable errorsNow we have error priority processing ready. In the next step, we'll implement the main handler that:
processDataSigningResult functionNow let's implement the main handler that detects and closes the selfie screen before processing results.
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);
}
const selfieScreenWasClosed = this.closeSelfieScreenIfPresent();
What happens:
closeSelfieScreenIfPresent() to check if selfie screen is activetruefalseif (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?:
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
Scenario 1: Server Biometric Success
Scenario 2: Server Biometric Error
Scenario 3: Password/LDA Success
Scenario 4: Password/LDA Error
The implementation handles:
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.
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
Expected Error Messages:
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
Core Functionality:
Error Handling:
Other Authentication Methods:
Edge Cases:
Issue: Selfie screen doesn't close
NavigationService.currentRoute is correctly detecting the screenIssue: Alert appears behind selfie screen
Issue: Results appear for error responses
Issue: Password/LDA delayed unnecessarily
You've successfully added IDV Server Biometric Step-Up Authentication (Workflow 16) to your data signing implementation!
authLevel = 4 with server biometricNavigationService 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);
Before deploying, ensure:
Explore More REL-ID Features:
π 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!