๐ŸŽฏ Learning Path:

  1. Complete REL-ID Complete Activation & Login Flow Codelab
  2. You are here โ†’ Data Signing Flow Implementation

Welcome to the REL-ID Data Signing codelab! This tutorial builds upon your existing MFA implementation to add secure cryptographic data signing capabilities using REL-ID SDK's authentication and signing infrastructure.

What You'll Build

In this codelab, you'll enhance your existing MFA application with:

What You'll Learn

By completing this codelab, you'll master:

  1. Data Signing API Integration: Implementing authenticateUserAndSignData() with proper parameter handling
  2. Authentication Levels & Types: Understanding and implementing RDNAAuthLevel and RDNAAuthenticatorType enums
  3. Step-Up Authentication: Handling password challenges during sensitive signing operations
  4. Event-Driven Architecture: Managing onAuthenticateUserAndSignData callbacks and state updates

Prerequisites

Before starting this codelab, ensure you have:

Get the Code from GitHub

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-data-signing folder in the repository you cloned earlier

Codelab Architecture Overview

This codelab extends your MFA application with comprehensive data signing functionality:

  1. REL-ID SDK Integration: Direct integration with REL-ID data signing APIs
  2. Multi-Level Authentication: Support for REL-ID authentication levels 0, 1, and 4 only
  3. Step-Up Authentication Flow: Password challenges and biometric authentication
  4. Event-Driven Integration: Seamless integration with REL-ID SDK's callback architecture
  5. State Management: Proper cleanup using resetAuthenticateUserAndSignDataState API

Before implementing data signing functionality, let's understand the cryptographic concepts and security architecture that powers REL-ID's data signing capabilities.

What is Cryptographic Data Signing?

Data signing is a cryptographic process that creates a digital signature for a piece of data, providing:

REL-ID Data Signing Architecture

REL-ID's data signing implementation follows enterprise security standards:

User Data Input โ†’ Authentication Challenge โ†’ Biometric/LDA/Password Verification โ†’
Cryptographic Signing โ†’ Signed Payload โ†’ Verification

Key Security Features

  1. Multi-Level Authentication: 3 supported authentication levels (0, 1, 4) for different security requirements
  2. Supported Authenticator Types: NONE (auto-selection) and IDV Server Biometric only
  3. Step-Up Authentication: Additional password challenges for sensitive operations
  4. Secure State Management: Proper cleanup and reset to prevent authentication bypass
  5. Comprehensive Error Handling: Detailed error codes and recovery mechanisms

When to Use Data Signing

Data signing is ideal for:

Security Considerations

Key security guidelines:

  1. Authentication Level Selection: Match authentication level to data sensitivity
  2. Proper State Cleanup: Always reset authentication state after completion or cancellation
  3. Error Handling: Never expose sensitive error details to end users
  4. Audit Logging: Log all signing attempts for security monitoring
  5. User Experience: Balance security with usability for optimal user adoption

Let's explore the three core APIs that power REL-ID's data signing functionality, understanding their parameters, responses, and integration patterns.

authenticateUserAndSignData API

This is the primary API for initiating cryptographic data signing with user authentication.

API Signature

authenticateUserAndSignData(
  payload: string,
  authLevel: RDNAAuthLevel,
  authenticatorType: RDNAAuthenticatorType,
  reason: string
): Promise<RDNASyncResponse>

Parameters Deep Dive

Parameter

Type

Required

Description

payload

string

โœ…

The data to be cryptographically signed (max 500 characters)

authLevel

RDNAAuthLevel

โœ…

Authentication security level (0, 1, or 4 only)

authenticatorType

RDNAAuthenticatorType

โœ…

Type of authentication method (0 or 1 only)

reason

string

โœ…

Human-readable reason for signing (max 100 characters)

Official REL-ID Data Signing Authentication Mapping

RDNAAuthLevel

RDNAAuthenticatorType

Supported Authentication

Description

NONE (0)

NONE (0)

No Authentication

No authentication required - NOT RECOMMENDED for production

RDNA_AUTH_LEVEL_1 (1)

NONE (0)

Device biometric, Device passcode, or Password

Priority: Device biometric โ†’ Device passcode โ†’ Password

RDNA_AUTH_LEVEL_2 (2)

NOT SUPPORTED

โŒ SDK will error out

Level 2 is not supported for data signing

RDNA_AUTH_LEVEL_3 (3)

NOT SUPPORTED

โŒ SDK will error out

Level 3 is not supported for data signing

RDNA_AUTH_LEVEL_4 (4)

RDNA_IDV_SERVER_BIOMETRIC (1)

IDV Server Biometric

Maximum security - Any other authenticator type will cause SDK error

How to Use AuthLevel and AuthenticatorType

REL-ID data signing supports three authentication modes:

1. No Authentication (Level 0)

authLevel: RDNAAuthLevel.NONE,
authenticatorType: RDNAAuthenticatorType.NONE

2. Re-Authentication (Level 1)

authLevel: RDNAAuthLevel.RDNA_AUTH_LEVEL_1,
authenticatorType: RDNAAuthenticatorType.NONE

3. Step-up Authentication (Level 4)

authLevel: RDNAAuthLevel.RDNA_AUTH_LEVEL_4,
authenticatorType: RDNAAuthenticatorType.RDNA_IDV_SERVER_BIOMETRIC

Sync Response Pattern

// Success Response
{
  error: {
    longErrorCode: 0,
    shortErrorCode: 0,
    errorString: ""
  },
  // Additional response fields...
}

// Error Response
{
  error: {
    longErrorCode: 123,
    shortErrorCode: 45,
    errorString: "Authentication failed"
  }
}

Implementation Example

// Service layer implementation
async signData(request: DataSigningRequest): Promise<void> {
  console.log('DataSigningService - Starting data signing process');

  try {
    const response = await rdnaService.authenticateUserAndSignData(
      request.payload,
      request.authLevel,
      request.authenticatorType,
      request.reason
    );

    console.log('DataSigningService - Data signing initiated successfully');
    return Promise.resolve();
  } catch (error) {
    console.error('DataSigningService - Data signing failed:', error);
    throw error;
  }
}

resetAuthenticateUserAndSignDataState API

This API cleans up authentication state after signing completion or cancellation.

API Signature

resetAuthenticateUserAndSignDataState(): Promise<RDNASyncResponse>

When to Use

Implementation Example

// Service layer cleanup implementation
async resetState(): Promise<void> {
  console.log('DataSigningService - Resetting data signing state');

  try {
    await rdnaService.resetAuthenticateUserAndSignDataState();
    console.log('DataSigningService - State reset successfully');
    return Promise.resolve();
  } catch (error) {
    console.error('DataSigningService - State reset failed:', error);
    throw error;
  }
}

onAuthenticateUserAndSignData Event

This callback event delivers the final signing results after authentication completion.

Event Response Structure

interface DataSigningResponse {
  dataPayload: string;           // Original payload that was signed
  dataPayloadLength: number;     // Length of the payload
  reason: string;                // Reason provided for signing
  payloadSignature: string;      // Cryptographic signature
  dataSignatureID: string;       // Unique signature identifier
  authLevel: number;             // Authentication level used
  authenticationType: number;    // Authentication type used
  status: ResponseStatus;        // Operation status
  error: ResponseError;          // Error details (if any)
}

Event Handler Implementation

const handleDataSigningResponse = useCallback((response: RDNADataSigningResponse) => {
  console.log('SDKEventProvider - Data signing response received:', response);

  // Convert SDK response to internal format
  const internalResponse: DataSigningResponse = {
    dataPayload: response.dataPayload,
    dataPayloadLength: response.dataPayloadLength,
    reason: response.reason,
    payloadSignature: response.payloadSignature,
    dataSignatureID: response.dataSignatureID,
    authLevel: response.authLevel,
    authenticationType: response.authenticationType,
    status: response.status,
    error: response.error,
  };

  // Update state
  setSigningResult(internalResponse);
  setFormState(prev => ({ ...prev, isLoading: false }));

  // Check if signing was successful
  if (response.error.shortErrorCode === 0 && response.status.statusCode === 100) {
    // Format result for display
    const displayData = DataSigningService.formatSigningResultForDisplay(internalResponse);
    setResultDisplay(displayData);

    console.log('SDKEventProvider - Data signing successful');
  } else {
    console.error('SDKEventProvider - Data signing failed:', response.error);
    // Handle error appropriately
  }
}, []);

Success vs Error Handling

Success Indicators:

Error Handling:

Now let's implement the service layer architecture that provides clean abstraction over REL-ID SDK data signing APIs. This follows the established patterns from your MFA implementation.

RdnaService Integration

First, let's examine the core SDK service implementation. This should already exist in your codelab:

// src/uniken/services/rdnaService.ts

/**
 * Authenticates user and signs data payload
 *
 * This method initiates the data signing flow with step-up authentication.
 * It requires user authentication and cryptographically signs the provided
 * payload upon successful authentication.
 */
async authenticateUserAndSignData(
  payload: string,
  authLevel: RDNAAuthLevel,
  authenticatorType: RDNAAuthenticatorType,
  reason: string
): Promise<RDNASyncResponse> {
  return new Promise((resolve, reject) => {
    console.log('RdnaService - Initiating data signing:', {
      payloadLength: payload.length,
      authLevel,
      authenticatorType,
      reason,
    });

    RdnaClient.authenticateUserAndSignData(
      payload,
      authLevel,
      authenticatorType,
      reason,
      response => {
        console.log('RdnaService - AuthenticateUserAndSignData sync callback received');
        console.log('RdnaService - AuthenticateUserAndSignData sync raw response:', response);

        const result: RDNASyncResponse = response;

        if (result.error && result.error.longErrorCode === 0) {
          console.log('RdnaService - AuthenticateUserAndSignData sync response success');
          resolve(result);
        } else {
          console.error('RdnaService - AuthenticateUserAndSignData sync response error:', result);
          reject(result);
        }
      }
    );
  });
}

/**
 * Resets the data signing authentication state
 */
async resetAuthenticateUserAndSignDataState(): Promise<RDNASyncResponse> {
  return new Promise((resolve, reject) => {
    console.log('RdnaService - Resetting data signing authentication state');

    RdnaClient.resetAuthenticateUserAndSignDataState(response => {
      console.log('RdnaService - ResetAuthenticateUserAndSignDataState sync callback received');

      const result: RDNASyncResponse = response;

      if (result.error && result.error.longErrorCode === 0) {
        console.log('RdnaService - ResetAuthenticateUserAndSignDataState sync response success');
        resolve(result);
      } else {
        console.error('RdnaService - ResetAuthenticateUserAndSignDataState sync response error:', result);
        reject(result);
      }
    });
  });
}

DataSigningService - High-Level Service Wrapper

Create a high-level service that combines multiple concerns:

// src/tutorial/services/DataSigningService.ts

import { RDNAAuthLevel, RDNAAuthenticatorType } from 'react-native-rdna-client';
import rdnaService from '../../uniken/services/rdnaService';
import { DropdownDataService } from './DropdownDataService';
import type {
  DataSigningRequest,
  DataSigningResponse,
  DataSigningResultDisplay,
  ResultInfoItem,
} from '../types/DataSigningTypes';

/**
 * High-level service for data signing operations
 * Combines RdnaService and DropdownDataService for complete functionality
 */
class DataSigningServiceClass {

  /**
   * Get dropdown data service instance
   */
  get dropdownService() {
    return DropdownDataService;
  }

  /**
   * Get RDNA service instance
   */
  get rdnaService() {
    return rdnaService;
  }

  /**
   * Initiates data signing with proper enum conversion
   *
   * @param request Data signing request with display values
   * @returns Promise that resolves when sync response is received
   */
  async signData(request: DataSigningRequest): Promise<void> {
    console.log('DataSigningService - Starting data signing process');

    try {
      const response = await rdnaService.authenticateUserAndSignData(
        request.payload,
        request.authLevel,
        request.authenticatorType,
        request.reason
      );

      console.log('DataSigningService - Data signing initiated successfully');
      return Promise.resolve();
    } catch (error) {
      console.error('DataSigningService - Data signing failed:', error);
      throw error;
    }
  }

  /**
   * Submits password for step-up authentication during data signing
   *
   * @param password User's password
   * @param challengeMode Challenge mode from getPassword callback
   * @returns Promise that resolves when sync response is received
   */
  async submitPassword(password: string, challengeMode: number): Promise<void> {
    console.log('DataSigningService - Submitting password for data signing');

    try {
      const response = await rdnaService.setPassword(password, challengeMode);
      console.log('DataSigningService - Password submitted successfully');
      return Promise.resolve();
    } catch (error) {
      console.error('DataSigningService - Password submission failed:', error);
      throw error;
    }
  }

  /**
   * Resets data signing state (cleanup)
   *
   * @returns Promise that resolves when state is reset
   */
  async resetState(): Promise<void> {
    console.log('DataSigningService - Resetting data signing state');

    try {
      await rdnaService.resetAuthenticateUserAndSignDataState();
      console.log('DataSigningService - State reset successfully');
      return Promise.resolve();
    } catch (error) {
      console.error('DataSigningService - State reset failed:', error);
      throw error;
    }
  }

  /**
   * Convert dropdown values to SDK enums for API call
   */
  convertDropdownToEnums(
    authLevelDisplay: string,
    authenticatorTypeDisplay: string
  ): {
    authLevel: RDNAAuthLevel;
    authenticatorType: RDNAAuthenticatorType;
  } {
    return {
      authLevel: DropdownDataService.convertAuthLevelToEnum(authLevelDisplay),
      authenticatorType: DropdownDataService.convertAuthenticatorTypeToEnum(authenticatorTypeDisplay),
    };
  }

  /**
   * Validates form input before submission
   */
  validateSigningInput(
    payload: string,
    authLevel: string,
    authenticatorType: string,
    reason: string
  ): {
    isValid: boolean;
    errors: string[];
  } {
    const errors: string[] = [];

    // Validate payload
    if (!payload || payload.trim().length === 0) {
      errors.push('Payload is required');
    } else if (payload.length > 500) {
      errors.push('Payload must be less than 500 characters');
    }

    // Validate auth level
    if (!authLevel || !DropdownDataService.isValidAuthLevel(authLevel)) {
      errors.push('Please select a valid authentication level');
    }

    // Validate authenticator type
    if (!authenticatorType || !DropdownDataService.isValidAuthenticatorType(authenticatorType)) {
      errors.push('Please select a valid authenticator type');
    }

    // Validate reason
    if (!reason || reason.trim().length === 0) {
      errors.push('Reason is required');
    } else if (reason.length > 100) {
      errors.push('Reason must be less than 100 characters');
    }

    return {
      isValid: errors.length === 0,
      errors,
    };
  }

  /**
   * Converts raw data signing response to display format
   * Excludes status and error fields as per requirements
   */
  formatSigningResultForDisplay(response: DataSigningResponse): DataSigningResultDisplay {
    return {
      authLevel: response.authLevel?.toString() || 'N/A',
      authenticationType: response.authenticationType?.toString() || 'N/A',
      dataPayloadLength: response.dataPayloadLength?.toString() || 'N/A',
      dataPayload: response.dataPayload || 'N/A',
      payloadSignature: response.payloadSignature || 'N/A',
      dataSignatureID: response.dataSignatureID || 'N/A',
      reason: response.reason || 'N/A',
    };
  }

  /**
   * Converts display format to info items for results screen
   */
  convertToResultInfoItems(displayData: DataSigningResultDisplay): ResultInfoItem[] {
    return [
      {
        name: 'Payload Signature',
        value: displayData.payloadSignature,
      },
      {
        name: 'Data Signature ID',
        value: displayData.dataSignatureID,
      },
      {
        name: 'Reason',
        value: displayData.reason,
      },
      {
        name: 'Data Payload',
        value: displayData.dataPayload,
      },
      {
        name: 'Auth Level',
        value: displayData.authLevel,
      },
      {
        name: 'Authentication Type',
        value: displayData.authenticationType,
      },
      {
        name: 'Data Payload Length',
        value: displayData.dataPayloadLength,
      },
    ];
  }

  /**
   * Gets user-friendly error message for error codes
   */
  getErrorMessage(errorCode: number): string {
    switch (errorCode) {
      case 0:
        return 'Success';
      case 214:
        return 'Authentication method not supported. Please try a different authentication type.';
      case 102:
        return 'Authentication failed. Please check your credentials and try again.';
      case 153:
        return 'Operation cancelled by user.';
      default:
        return `Operation failed with error code: ${errorCode}`;
    }
  }
}

// Export singleton instance
export const DataSigningService = new DataSigningServiceClass();

DropdownDataService - Enum Management

Create a service to manage dropdown data and enum conversions:

// src/tutorial/services/DropdownDataService.ts

import { RDNAAuthLevel, RDNAAuthenticatorType } from 'react-native-rdna-client';
import type {
  DropdownOption,
  AuthLevelDropdownData,
  AuthenticatorTypeDropdownData,
} from '../types/DataSigningTypes';

/**
 * Service for managing dropdown data and enum conversions
 */
class DropdownDataServiceClass {

  /**
   * Get authentication level options for dropdown
   * Only includes levels supported for data signing: 0, 1, and 4
   */
  getAuthLevelOptions(): DropdownOption[] {
    return [
      { value: "NONE (0)" },
      { value: "RDNA_AUTH_LEVEL_1 (1)" },
      { value: "RDNA_AUTH_LEVEL_4 (4)" },
      // Note: Levels 2 and 3 are NOT SUPPORTED for data signing
    ];
  }

  /**
   * Get authenticator type options for dropdown
   * Only includes types supported for data signing: 0 and 1
   */
  getAuthenticatorTypeOptions(): DropdownOption[] {
    return [
      { value: "NONE (0)" },
      { value: "RDNA_IDV_SERVER_BIOMETRIC (1)" },
      // Note: RDNA_AUTH_PASS (2) and RDNA_AUTH_LDA (3) are NOT SUPPORTED for data signing
    ];
  }

  /**
   * Convert auth level display value to enum
   * Only handles levels supported for data signing
   */
  convertAuthLevelToEnum(displayValue: string): RDNAAuthLevel {
    switch (displayValue) {
      case "NONE (0)":
        return RDNAAuthLevel.NONE;
      case "RDNA_AUTH_LEVEL_1 (1)":
        return RDNAAuthLevel.RDNA_AUTH_LEVEL_1;
      case "RDNA_AUTH_LEVEL_4 (4)":
        return RDNAAuthLevel.RDNA_AUTH_LEVEL_4;
      default:
        // Default to Level 4 for maximum security
        return RDNAAuthLevel.RDNA_AUTH_LEVEL_4;
    }
  }

  /**
   * Convert authenticator type display value to enum
   * Only handles types supported for data signing
   */
  convertAuthenticatorTypeToEnum(displayValue: string): RDNAAuthenticatorType {
    switch (displayValue) {
      case "NONE (0)":
        return RDNAAuthenticatorType.NONE;
      case "RDNA_IDV_SERVER_BIOMETRIC (1)":
        return RDNAAuthenticatorType.RDNA_IDV_SERVER_BIOMETRIC;
      default:
        // Default to biometric for maximum security
        return RDNAAuthenticatorType.RDNA_IDV_SERVER_BIOMETRIC;
    }
  }

  /**
   * Validate auth level display value
   */
  isValidAuthLevel(displayValue: string): boolean {
    const validValues = this.getAuthLevelOptions().map(option => option.value);
    return validValues.includes(displayValue);
  }

  /**
   * Validate authenticator type display value
   */
  isValidAuthenticatorType(displayValue: string): boolean {
    const validValues = this.getAuthenticatorTypeOptions().map(option => option.value);
    return validValues.includes(displayValue);
  }
}

// Export singleton instance
export const DropdownDataService = new DropdownDataServiceClass();

Error Handling Patterns

Key error handling strategies in the service layer:

  1. Comprehensive Logging: Log all operations with context
  2. Error Transformation: Convert SDK errors to user-friendly messages
  3. State Cleanup: Always attempt to reset state on errors
  4. Non-Blocking Cleanup: Don't let cleanup errors block user flow
  5. Error Categorization: Different handling for different error types

Now let's implement the user interface components for data signing, including form inputs, dropdowns, and result display screens.

DataSigningInputScreen - Main Form Interface

This is the primary screen where users input data to be signed:

The following image showcases the data signing input screen from the sample application:

Data Signing Input Screen

// src/tutorial/screens/dataSigning/DataSigningInputScreen.tsx

import React from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  ScrollView,
  StyleSheet,
  Alert,
  ActivityIndicator,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { useDataSigning } from '../../../uniken/providers/SDKEventProvider';
import { DropdownDataService } from '../../services/DropdownDataService';
import AuthLevelDropdown from './components/AuthLevelDropdown';
import AuthenticatorTypeDropdown from './components/AuthenticatorTypeDropdown';
import PasswordChallengeModal from './components/PasswordChallengeModal';

/**
 * Main screen for data signing input
 */
const DataSigningInputScreen: React.FC = () => {
  const navigation = useNavigation();

  const {
    formState,
    updateFormState,
    passwordModalState,
    submitDataSigning,
    resultDisplay,
  } = useDataSigning();

  /**
   * Handles form submission
   */
  const handleSubmit = async () => {
    console.log('DataSigningInputScreen - Submit button pressed');

    // Validate required fields
    if (!formState.payload.trim()) {
      Alert.alert('Validation Error', 'Please enter a payload to sign');
      return;
    }

    if (!formState.selectedAuthLevel) {
      Alert.alert('Validation Error', 'Please select an authentication level');
      return;
    }

    if (!formState.selectedAuthenticatorType) {
      Alert.alert('Validation Error', 'Please select an authenticator type');
      return;
    }

    if (!formState.reason.trim()) {
      Alert.alert('Validation Error', 'Please enter a reason for signing');
      return;
    }

    try {
      await submitDataSigning();
    } catch (error) {
      console.error('DataSigningInputScreen - Submit error:', error);
      const errorMessage = error instanceof Error ? error.message : 'Failed to submit data signing request. Please try again.';
      Alert.alert('Error', errorMessage);
    }
  };

  /**
   * Navigates to result screen when signing is complete
   */
  React.useEffect(() => {
    if (resultDisplay) {
      console.log('DataSigningInputScreen - Navigating to results screen');
      navigation.navigate('DataSigningResult' as never);
    }
  }, [resultDisplay, navigation]);

  return (
    <View style={styles.container}>
      <ScrollView
        contentContainerStyle={styles.scrollContent}
        keyboardShouldPersistTaps="handled"
        showsVerticalScrollIndicator={false}
      >
        {/* Header */}
        <View style={styles.header}>
          <Text style={styles.title}>Data Signing</Text>
          <Text style={styles.subtitle}>
            Sign your data with cryptographic authentication
          </Text>
        </View>

        {/* Info Section */}
        <View style={styles.infoSection}>
          <Text style={styles.infoTitle}>How it works:</Text>
          <Text style={styles.infoText}>
            1. Enter your data payload and select authentication parameters{'\n'}
            2. Click "Sign Data" to initiate the signing process{'\n'}
            3. Complete authentication when prompted{'\n'}
            4. Receive your cryptographically signed data
          </Text>
        </View>

        {/* Form */}
        <View style={styles.form}>

          {/* Payload Input */}
          <View style={styles.inputGroup}>
            <Text style={styles.label}>Data Payload *</Text>
            <TextInput
              style={[styles.textInput, styles.multilineInput]}
              placeholder="Enter the data you want to sign..."
              value={formState.payload}
              onChangeText={(value) => updateFormState({ payload: value })}
              multiline={true}
              numberOfLines={4}
              maxLength={500}
              textAlignVertical="top"
              editable={!formState.isLoading}
            />
            <Text style={styles.charCount}>
              {formState.payload.length}/500
            </Text>
          </View>

          {/* Auth Level Dropdown */}
          <View style={styles.inputGroup}>
            <Text style={styles.label}>Authentication Level *</Text>
            <AuthLevelDropdown
              selectedValue={formState.selectedAuthLevel}
              onValueChange={(value) => updateFormState({ selectedAuthLevel: value })}
              enabled={!formState.isLoading}
            />
            <Text style={styles.helpText}>
              Level 4 is recommended for maximum security
            </Text>
          </View>

          {/* Authenticator Type Dropdown */}
          <View style={styles.inputGroup}>
            <Text style={styles.label}>Authenticator Type *</Text>
            <AuthenticatorTypeDropdown
              selectedValue={formState.selectedAuthenticatorType}
              onValueChange={(value) => updateFormState({ selectedAuthenticatorType: value })}
              enabled={!formState.isLoading}
            />
            <Text style={styles.helpText}>
              Choose the authentication method for signing
            </Text>
          </View>

          {/* Reason Input */}
          <View style={styles.inputGroup}>
            <Text style={styles.label}>Signing Reason *</Text>
            <TextInput
              style={styles.textInput}
              placeholder="Enter reason for signing"
              value={formState.reason}
              onChangeText={(value) => updateFormState({ reason: value })}
              maxLength={100}
              editable={!formState.isLoading}
            />
            <Text style={styles.charCount}>
              {formState.reason.length}/100
            </Text>
          </View>

        </View>

        {/* Submit Button */}
        <TouchableOpacity
          style={[styles.submitButton, formState.isLoading && styles.submitButtonDisabled]}
          onPress={handleSubmit}
          disabled={formState.isLoading}
        >
          {formState.isLoading ? (
            <View style={styles.loadingContainer}>
              <ActivityIndicator color="#FFFFFF" size="small" />
              <Text style={styles.submitButtonText}>Processing...</Text>
            </View>
          ) : (
            <Text style={styles.submitButtonText}>Sign Data</Text>
          )}
        </TouchableOpacity>

      </ScrollView>

      {/* Password Challenge Modal */}
      <PasswordChallengeModal />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f8f9fa',
  },
  scrollContent: {
    flexGrow: 1,
    padding: 20,
  },
  header: {
    marginBottom: 30,
    alignItems: 'center',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    color: '#1a1a1a',
    marginBottom: 8,
  },
  subtitle: {
    fontSize: 16,
    color: '#666',
    textAlign: 'center',
    lineHeight: 22,
  },
  form: {
    marginBottom: 30,
  },
  inputGroup: {
    marginBottom: 24,
  },
  label: {
    fontSize: 16,
    fontWeight: '600',
    color: '#1a1a1a',
    marginBottom: 8,
  },
  textInput: {
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    padding: 16,
    fontSize: 16,
    backgroundColor: '#fff',
    color: '#1a1a1a',
  },
  multilineInput: {
    height: 100,
    textAlignVertical: 'top',
  },
  charCount: {
    fontSize: 12,
    color: '#888',
    textAlign: 'right',
    marginTop: 4,
  },
  helpText: {
    fontSize: 12,
    color: '#666',
    marginTop: 4,
    fontStyle: 'italic',
  },
  submitButton: {
    backgroundColor: '#007AFF',
    borderRadius: 12,
    paddingVertical: 16,
    paddingHorizontal: 24,
    alignItems: 'center',
    justifyContent: 'center',
    elevation: 3,
    shadowColor: '#007AFF',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.2,
    shadowRadius: 4,
  },
  submitButtonDisabled: {
    backgroundColor: '#ccc',
    shadowOpacity: 0,
    elevation: 0,
  },
  submitButtonText: {
    color: '#FFFFFF',
    fontSize: 18,
    fontWeight: '600',
  },
  loadingContainer: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  infoSection: {
    marginTop: 10,
    marginBottom: 10,
    padding: 16,
    backgroundColor: '#e8f4fd',
    borderRadius: 8,
    borderLeftWidth: 4,
    borderLeftColor: '#007AFF',
  },
  infoTitle: {
    fontSize: 16,
    fontWeight: '600',
    color: '#1a1a1a',
    marginBottom: 8,
  },
  infoText: {
    fontSize: 14,
    color: '#555',
    lineHeight: 20,
  },
});

export default DataSigningInputScreen;

AuthLevelDropdown - Reusable Dropdown Component

// src/tutorial/screens/dataSigning/components/AuthLevelDropdown.tsx

import React, { useState } from 'react';
import {
  View,
  Text,
  TouchableOpacity,
  Modal,
  FlatList,
  StyleSheet,
} from 'react-native';
import { DropdownDataService } from '../../../services/DropdownDataService';

interface AuthLevelDropdownProps {
  selectedValue: string;
  onValueChange: (value: string) => void;
  enabled?: boolean;
}

const AuthLevelDropdown: React.FC<AuthLevelDropdownProps> = ({
  selectedValue,
  onValueChange,
  enabled = true,
}) => {
  const [isVisible, setIsVisible] = useState(false);
  const options = DropdownDataService.getAuthLevelOptions();

  const handleSelect = (value: string) => {
    onValueChange(value);
    setIsVisible(false);
  };

  const displayValue = selectedValue || 'Select Authentication Level';

  return (
    <>
      <TouchableOpacity
        style={[styles.dropdown, !enabled && styles.dropdownDisabled]}
        onPress={() => enabled && setIsVisible(true)}
        disabled={!enabled}
      >
        <Text style={[styles.dropdownText, !selectedValue && styles.placeholderText]}>
          {displayValue}
        </Text>
        <Text style={styles.dropdownArrow}>โ–ผ</Text>
      </TouchableOpacity>

      <Modal
        visible={isVisible}
        transparent={true}
        animationType="fade"
        onRequestClose={() => setIsVisible(false)}
      >
        <View style={styles.modalOverlay}>
          <View style={styles.modalContent}>
            <View style={styles.modalHeader}>
              <Text style={styles.modalTitle}>Select Authentication Level</Text>
              <TouchableOpacity
                style={styles.closeButton}
                onPress={() => setIsVisible(false)}
              >
                <Text style={styles.closeButtonText}>โœ•</Text>
              </TouchableOpacity>
            </View>

            <FlatList
              data={options}
              keyExtractor={(item) => item.value}
              renderItem={({ item }) => (
                <TouchableOpacity
                  style={[
                    styles.option,
                    selectedValue === item.value && styles.selectedOption,
                  ]}
                  onPress={() => handleSelect(item.value)}
                >
                  <Text
                    style={[
                      styles.optionText,
                      selectedValue === item.value && styles.selectedOptionText,
                    ]}
                  >
                    {item.value}
                  </Text>
                </TouchableOpacity>
              )}
              showsVerticalScrollIndicator={false}
            />
          </View>
        </View>
      </Modal>
    </>
  );
};

const styles = StyleSheet.create({
  dropdown: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    padding: 16,
    backgroundColor: '#fff',
    minHeight: 56,
  },
  dropdownDisabled: {
    backgroundColor: '#f5f5f5',
    opacity: 0.6,
  },
  dropdownText: {
    fontSize: 16,
    color: '#1a1a1a',
    flex: 1,
  },
  placeholderText: {
    color: '#999',
  },
  dropdownArrow: {
    fontSize: 12,
    color: '#666',
    marginLeft: 8,
  },
  modalOverlay: {
    flex: 1,
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
    justifyContent: 'center',
    alignItems: 'center',
  },
  modalContent: {
    backgroundColor: '#fff',
    borderRadius: 12,
    width: '90%',
    maxHeight: '70%',
    elevation: 5,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.25,
    shadowRadius: 4,
  },
  modalHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    padding: 20,
    borderBottomWidth: 1,
    borderBottomColor: '#eee',
  },
  modalTitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#1a1a1a',
  },
  closeButton: {
    padding: 4,
  },
  closeButtonText: {
    fontSize: 18,
    color: '#666',
    fontWeight: 'bold',
  },
  option: {
    padding: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#f0f0f0',
  },
  selectedOption: {
    backgroundColor: '#e8f4fd',
  },
  optionText: {
    fontSize: 16,
    color: '#1a1a1a',
  },
  selectedOptionText: {
    color: '#007AFF',
    fontWeight: '600',
  },
});

export default AuthLevelDropdown;

DataSigningResultScreen - Results Display

The following image showcases the successful data signing results screen from the sample application:

Data Signing Success Results Screen

// src/tutorial/screens/dataSigning/DataSigningResultScreen.tsx

import React, { useState } from 'react';
import {
  View,
  Text,
  ScrollView,
  TouchableOpacity,
  StyleSheet,
  Alert,
  Clipboard,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { useDataSigning } from '../../../uniken/providers/SDKEventProvider';
import { DataSigningService } from '../../services/DataSigningService';

const DataSigningResultScreen: React.FC = () => {
  const navigation = useNavigation();
  const { resultDisplay, resetState } = useDataSigning();
  const [copiedField, setCopiedField] = useState<string | null>(null);

  // Convert to result info items for display
  const resultItems = resultDisplay
    ? DataSigningService.convertToResultInfoItems(resultDisplay)
    : [];

  const handleCopyToClipboard = async (value: string, fieldName: string) => {
    try {
      await Clipboard.setString(value);
      setCopiedField(fieldName);

      // Reset the copied state after 2 seconds
      setTimeout(() => {
        setCopiedField(null);
      }, 2000);

      console.log(`DataSigningResultScreen - Copied ${fieldName} to clipboard`);
    } catch (error) {
      console.error('DataSigningResultScreen - Failed to copy to clipboard:', error);
      Alert.alert('Error', 'Failed to copy to clipboard');
    }
  };

  const handleSignAnother = async () => {
    console.log('DataSigningResultScreen - Sign another button pressed');

    try {
      await resetState();
      navigation.goBack();
    } catch (error) {
      console.error('DataSigningResultScreen - Failed to reset state:', error);
      navigation.goBack();
    }
  };

  const renderResultItem = (item: { name: string; value: string }, index: number) => {
    const isSignature = item.name === 'Payload Signature';
    const isLongValue = item.value.length > 50;
    const displayValue = isLongValue && !isSignature
      ? `${item.value.substring(0, 50)}...`
      : item.value;

    return (
      <View key={index} style={styles.resultItem}>
        <View style={styles.resultItemHeader}>
          <Text style={styles.resultLabel}>{item.name}</Text>
          {item.value !== 'N/A' && (
            <TouchableOpacity
              style={styles.copyButton}
              onPress={() => handleCopyToClipboard(item.value, item.name)}
            >
              <Text style={styles.copyButtonText}>
                {copiedField === item.name ? 'โœ“ Copied' : '๐Ÿ“‹ Copy'}
              </Text>
            </TouchableOpacity>
          )}
        </View>

        <View style={[styles.resultValueContainer, isSignature && styles.signatureContainer]}>
          <Text style={[styles.resultValue, isSignature && styles.signatureText]}>
            {displayValue}
          </Text>
        </View>

        {isLongValue && !isSignature && (
          <TouchableOpacity
            style={styles.expandButton}
            onPress={() => Alert.alert(item.name, item.value)}
          >
            <Text style={styles.expandButtonText}>View Full Value</Text>
          </TouchableOpacity>
        )}

        {isSignature && (
          <TouchableOpacity
            style={styles.expandButton}
            onPress={() => Alert.alert('Complete Signature', item.value)}
          >
            <Text style={styles.expandButtonText}>View Complete Signature</Text>
          </TouchableOpacity>
        )}
      </View>
    );
  };

  if (!resultDisplay) {
    return (
      <View style={styles.container}>
        <View style={styles.errorContainer}>
          <Text style={styles.errorText}>No signing results available</Text>
        </View>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <ScrollView
        contentContainerStyle={styles.scrollContent}
        showsVerticalScrollIndicator={false}
      >

        {/* Success Header */}
        <View style={styles.successHeader}>
          <View style={styles.successIcon}>
            <Text style={styles.successIconText}>โœ…</Text>
          </View>
          <Text style={styles.successTitle}>Data Signing Successful!</Text>
          <Text style={styles.successSubtitle}>
            Your data has been cryptographically signed
          </Text>
        </View>

        {/* Results Section */}
        <View style={styles.resultsSection}>
          <Text style={styles.sectionTitle}>Signing Results</Text>
          <Text style={styles.sectionSubtitle}>
            All values below have been cryptographically verified
          </Text>

          <View style={styles.resultsContainer}>
            {resultItems.map(renderResultItem)}
          </View>
        </View>

        {/* Actions Section */}
        <View style={styles.actionsSection}>
          <TouchableOpacity
            style={[styles.button, styles.primaryButton]}
            onPress={handleSignAnother}
          >
            <Text style={styles.buttonText}>๐Ÿ” Sign Another Document</Text>
          </TouchableOpacity>
        </View>

      </ScrollView>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f8f9fa',
  },
  scrollContent: {
    flexGrow: 1,
    padding: 20,
  },
  errorContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  errorText: {
    fontSize: 18,
    color: '#666',
    textAlign: 'center',
    marginBottom: 20,
  },
  successHeader: {
    alignItems: 'center',
    marginBottom: 30,
    paddingVertical: 20,
  },
  successIcon: {
    marginBottom: 16,
  },
  successIconText: {
    fontSize: 48,
  },
  successTitle: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#1a1a1a',
    marginBottom: 8,
    textAlign: 'center',
  },
  successSubtitle: {
    fontSize: 16,
    color: '#666',
    textAlign: 'center',
    lineHeight: 22,
  },
  resultsSection: {
    marginBottom: 30,
  },
  sectionTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#1a1a1a',
    marginBottom: 8,
  },
  sectionSubtitle: {
    fontSize: 14,
    color: '#666',
    marginBottom: 20,
    lineHeight: 20,
  },
  resultsContainer: {
    gap: 16,
  },
  resultItem: {
    backgroundColor: '#fff',
    borderRadius: 12,
    padding: 16,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  resultItemHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 12,
  },
  resultLabel: {
    fontSize: 16,
    fontWeight: '600',
    color: '#1a1a1a',
    flex: 1,
  },
  copyButton: {
    paddingHorizontal: 12,
    paddingVertical: 6,
    backgroundColor: '#f0f0f0',
    borderRadius: 6,
  },
  copyButtonText: {
    fontSize: 12,
    color: '#007AFF',
    fontWeight: '500',
  },
  resultValueContainer: {
    backgroundColor: '#f8f9fa',
    borderRadius: 8,
    padding: 12,
    borderWidth: 1,
    borderColor: '#e9ecef',
  },
  signatureContainer: {
    backgroundColor: '#fff3cd',
    borderColor: '#ffeeba',
  },
  resultValue: {
    fontSize: 14,
    color: '#1a1a1a',
    lineHeight: 20,
  },
  signatureText: {
    fontFamily: 'monospace',
    fontSize: 12,
    color: '#856404',
  },
  expandButton: {
    marginTop: 8,
    alignSelf: 'flex-start',
    paddingHorizontal: 12,
    paddingVertical: 6,
    backgroundColor: '#e8f4fd',
    borderRadius: 6,
  },
  expandButtonText: {
    fontSize: 12,
    color: '#007AFF',
    fontWeight: '500',
  },
  actionsSection: {
    gap: 12,
  },
  button: {
    borderRadius: 12,
    paddingVertical: 16,
    paddingHorizontal: 24,
    alignItems: 'center',
    justifyContent: 'center',
  },
  primaryButton: {
    backgroundColor: '#007AFF',
    elevation: 3,
    shadowColor: '#007AFF',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.2,
    shadowRadius: 4,
  },
  buttonText: {
    color: '#FFFFFF',
    fontSize: 16,
    fontWeight: '600',
  },
});

export default DataSigningResultScreen;

Key UI Features Implemented

  1. ๐Ÿ“ Form Validation: Real-time validation with character counters
  2. ๐ŸŽ›๏ธ Dropdown Components: Reusable dropdown with modal selection
  3. โณ Loading States: Proper loading indicators during operations
  4. ๐Ÿ“‹ Copy Functionality: One-click copying of signature data
  5. ๐Ÿ“ฑ Responsive Design: Clean, modern UI that works on all screen sizes
  6. ๐Ÿ”„ Navigation Flow: Smooth transitions between screens
  7. ๐Ÿ›ก๏ธ Error Handling: User-friendly error messages and recovery

Let's implement the React Context-based state management that coordinates between UI components and the REL-ID SDK event system.

SDKEventProvider - Centralized State Management

The SDKEventProvider manages all data signing state and coordinates between UI and SDK:

// src/uniken/providers/SDKEventProvider.tsx (Data Signing Section)

import React, { createContext, useContext, useEffect, useCallback, ReactNode, useState } from 'react';
import rdnaService from '../services/rdnaService';
import NavigationService from '../../tutorial/navigation/NavigationService';
import type {
  RDNADataSigningResponse,
  RDNADataSigningPasswordChallengeData
} from '../types/rdnaEvents';

// Import data signing services and types
import { DataSigningService } from '../../tutorial/services/DataSigningService';
import type {
  DataSigningFormState,
  PasswordModalState,
  DataSigningResponse,
  DataSigningResultDisplay,
} from '../../tutorial/types/DataSigningTypes';

/**
 * SDK Event Context Interface - Data Signing Section
 */
interface SDKEventContextType {
  // Data Signing State
  formState: DataSigningFormState;
  updateFormState: (updates: Partial<DataSigningFormState>) => void;
  passwordModalState: PasswordModalState;
  updatePasswordModalState: (updates: Partial<PasswordModalState>) => void;

  // Data Signing Actions
  submitDataSigning: () => Promise<void>;
  submitPassword: () => Promise<void>;
  cancelPasswordModal: () => Promise<void>;
  resetDataSigningState: () => Promise<void>;

  // Data Signing Results
  signingResult: DataSigningResponse | null;
  resultDisplay: DataSigningResultDisplay | null;
}

/**
 * SDK Event Provider Component - Data Signing Implementation
 */
export const SDKEventProvider: React.FC<SDKEventProviderProps> = ({ children }) => {
  const [currentScreen, setCurrentScreen] = useState<string | null>(null);

  // =============================================================================
  // DATA SIGNING STATE
  // =============================================================================

  // Form state for data signing input
  const [formState, setFormState] = useState<DataSigningFormState>({
    payload: '',
    selectedAuthLevel: '',
    selectedAuthenticatorType: '',
    reason: '',
    isLoading: false,
  });

  // Password modal state for step-up authentication
  const [passwordModalState, setPasswordModalState] = useState<PasswordModalState>({
    isVisible: false,
    password: '',
    challengeMode: 0,
    attemptsLeft: 3,
    authenticationOptions: [],
    isLargeModal: false,
    keyboardHeight: 0,
    responseData: null,
  });

  // Results from data signing operation
  const [signingResult, setSigningResult] = useState<DataSigningResponse | null>(null);
  const [resultDisplay, setResultDisplay] = useState<DataSigningResultDisplay | null>(null);

  // =============================================================================
  // DATA SIGNING EVENT HANDLERS
  // =============================================================================

  /**
   * Handles the final data signing response event
   */
  const handleDataSigningResponse = useCallback((response: RDNADataSigningResponse) => {
    console.log('SDKEventProvider - Data signing response received:', response);

    // Convert SDK response to our internal format
    const internalResponse: DataSigningResponse = {
      dataPayload: response.dataPayload,
      dataPayloadLength: response.dataPayloadLength,
      reason: response.reason,
      payloadSignature: response.payloadSignature,
      dataSignatureID: response.dataSignatureID,
      authLevel: response.authLevel,
      authenticationType: response.authenticationType,
      status: response.status,
      error: response.error,
    };

    // Update state
    setSigningResult(internalResponse);
    setFormState(prev => ({ ...prev, isLoading: false }));

    // Check if signing was successful
    if (response.error.shortErrorCode === 0 && response.status.statusCode === 100) {
      // Format result for display (excluding status and error)
      const displayData = DataSigningService.formatSigningResultForDisplay(internalResponse);
      setResultDisplay(displayData);

      console.log('SDKEventProvider - Data signing successful');
    } else {
      console.error('SDKEventProvider - Data signing failed:', response.error);

      // Show error dialog
      Alert.alert(
        'Signing Failed',
        DataSigningService.getErrorMessage(response.error.shortErrorCode),
        [
          {
            text: 'Try Again',
            onPress: () => {
              setFormState(prev => ({ ...prev, isLoading: false }));
            },
          },
        ]
      );

      // Reset state on error
      try {
        DataSigningService.resetState();
      } catch (resetError) {
        console.error('SDKEventProvider - Failed to reset state after error:', resetError);
      }
    }
  }, []);

  /**
   * Handles password challenge during data signing
   */
  const handleDataSigningPasswordChallenge = useCallback((challengeData: RDNADataSigningPasswordChallengeData) => {
    console.log('SDKEventProvider - Data signing password challenge received:', challengeData);

    // Extract authentication options from challenge response
    const authOptions = challengeData.challengeResponse?.challengeInfo?.map(
      (challenge) => `${challenge.key}: ${challenge.value}`
    ) || [];

    // Update password modal state
    setPasswordModalState(prev => ({
      ...prev,
      isVisible: true,
      challengeMode: challengeData.challengeMode,
      attemptsLeft: challengeData.attemptsLeft,
      authenticationOptions: authOptions,
      isLargeModal: authOptions.length > 3,
      responseData: challengeData,
    }));

    console.log('SDKEventProvider - Password challenge modal displayed');
  }, []);

  // =============================================================================
  // DATA SIGNING ACTIONS
  // =============================================================================

  /**
   * Updates form state
   */
  const updateFormState = useCallback((updates: Partial<DataSigningFormState>) => {
    setFormState(prev => ({ ...prev, ...updates }));
  }, []);

  /**
   * Updates password modal state
   */
  const updatePasswordModalState = useCallback((updates: Partial<PasswordModalState>) => {
    setPasswordModalState(prev => ({ ...prev, ...updates }));
  }, []);

  /**
   * Submits data signing request
   */
  const submitDataSigning = useCallback(async () => {
    console.log('SDKEventProvider - Submitting data signing request');

    // Validate input
    const validation = DataSigningService.validateSigningInput(
      formState.payload,
      formState.selectedAuthLevel,
      formState.selectedAuthenticatorType,
      formState.reason
    );

    if (!validation.isValid) {
      const errorMessage = validation.errors.join('\n');
      Alert.alert('Validation Error', errorMessage);
      return;
    }

    // Convert dropdown values to enums
    const { authLevel, authenticatorType } = DataSigningService.convertDropdownToEnums(
      formState.selectedAuthLevel,
      formState.selectedAuthenticatorType
    );

    // Create request object
    const request: DataSigningRequest = {
      payload: formState.payload,
      authLevel,
      authenticatorType,
      reason: formState.reason,
    };

    // Set loading state
    setFormState(prev => ({ ...prev, isLoading: true }));

    try {
      await DataSigningService.signData(request);
      console.log('SDKEventProvider - Data signing request submitted successfully');
    } catch (error) {
      console.error('SDKEventProvider - Data signing request failed:', error);
      setFormState(prev => ({ ...prev, isLoading: false }));

      const errorMessage = error instanceof Error ? error.message : 'Failed to initiate data signing';
      Alert.alert('Error', errorMessage);
    }
  }, [formState]);

  /**
   * Submits password for step-up authentication
   */
  const submitPassword = useCallback(async () => {
    console.log('SDKEventProvider - Submitting password for data signing');

    if (!passwordModalState.password.trim()) {
      Alert.alert('Validation Error', 'Please enter your password');
      return;
    }

    try {
      await DataSigningService.submitPassword(
        passwordModalState.password,
        passwordModalState.challengeMode
      );

      console.log('SDKEventProvider - Password submitted successfully');

      // Clear password and keep modal visible for biometric prompt
      setPasswordModalState(prev => ({ ...prev, password: '' }));

    } catch (error) {
      console.error('SDKEventProvider - Password submission failed:', error);

      const errorMessage = error instanceof Error ? error.message : 'Password authentication failed';
      Alert.alert('Authentication Failed', errorMessage);

      // Clear password for retry
      setPasswordModalState(prev => ({ ...prev, password: '' }));
    }
  }, [passwordModalState.password, passwordModalState.challengeMode]);

  /**
   * Cancels password modal and resets data signing state
   */
  const cancelPasswordModal = useCallback(async () => {
    console.log('SDKEventProvider - Cancelling password modal');

    try {
      // Reset data signing state in SDK
      await DataSigningService.resetState();

      // Hide modal and reset states
      setPasswordModalState(prev => ({
        ...prev,
        isVisible: false,
        password: '',
      }));

      setFormState(prev => ({ ...prev, isLoading: false }));

      console.log('SDKEventProvider - Password modal cancelled successfully');
    } catch (error) {
      console.error('SDKEventProvider - Failed to cancel password modal:', error);

      // Hide modal anyway
      setPasswordModalState(prev => ({
        ...prev,
        isVisible: false,
        password: '',
      }));
      setFormState(prev => ({ ...prev, isLoading: false }));
      throw error;
    }
  }, []);

  /**
   * Resets all data signing state
   */
  const resetDataSigningState = useCallback(async () => {
    console.log('SDKEventProvider - Resetting data signing state');

    try {
      // Reset SDK state
      await DataSigningService.resetState();
    } catch (error) {
      console.error('SDKEventProvider - Failed to reset SDK state:', error);
    }

    // Reset all local state
    setFormState({
      payload: '',
      selectedAuthLevel: '',
      selectedAuthenticatorType: '',
      reason: '',
      isLoading: false,
    });

    setPasswordModalState({
      isVisible: false,
      password: '',
      challengeMode: 0,
      attemptsLeft: 3,
      authenticationOptions: [],
      isLargeModal: false,
      keyboardHeight: 0,
      responseData: null,
    });

    setSigningResult(null);
    setResultDisplay(null);

    console.log('SDKEventProvider - Data signing state reset complete');
  }, []);

  // =============================================================================
  // EVENT LISTENER SETUP
  // =============================================================================

  useEffect(() => {
    const eventManager = rdnaService.getEventManager();

    // Register data signing event listeners
    eventManager.on('onAuthenticateUserAndSignData', handleDataSigningResponse);
    eventManager.on('getPasswordStepUpAuthentication', handleDataSigningPasswordChallenge);

    console.log('SDKEventProvider - Data signing event listeners registered');

    return () => {
      eventManager.off('onAuthenticateUserAndSignData', handleDataSigningResponse);
      eventManager.off('getPasswordStepUpAuthentication', handleDataSigningPasswordChallenge);
      console.log('SDKEventProvider - Data signing event listeners cleaned up');
    };
  }, [handleDataSigningResponse, handleDataSigningPasswordChallenge]);

  // =============================================================================
  // CONTEXT VALUE
  // =============================================================================

  const contextValue: SDKEventContextType = {
    // Data Signing State
    formState,
    updateFormState,
    passwordModalState,
    updatePasswordModalState,

    // Data Signing Actions
    submitDataSigning,
    submitPassword,
    cancelPasswordModal,
    resetState: resetDataSigningState,

    // Data Signing Results
    signingResult,
    resultDisplay,
  };

  return (
    <SDKEventContext.Provider value={contextValue}>
      {children}
    </SDKEventContext.Provider>
  );
};

/**
 * Custom hook to use data signing functionality
 */
export const useDataSigning = () => {
  const context = useContext(SDKEventContext);
  if (context === undefined) {
    throw new Error('useDataSigning must be used within an SDKEventProvider');
  }
  return context;
};

Key State Management Features

  1. ๐Ÿ”„ Centralized State: All data signing state managed in one place
  2. ๐ŸŽฏ Event Coordination: Seamless integration between UI and SDK events
  3. ๐Ÿ›ก๏ธ Error Resilience: Comprehensive error handling with automatic recovery
  4. ๐Ÿงน State Cleanup: Proper state reset on cancellation or completion
  5. ๐Ÿ”’ Type Safety: Full TypeScript integration with interface definitions
  6. โšก Performance: Optimized re-renders with useCallback hooks

Password Challenge Modal Integration

The following image showcases the authentication required modal during step-up authentication:

Data Signing Password Challenge Modal

// src/tutorial/screens/dataSigning/components/PasswordChallengeModal.tsx

import React, { useState, useEffect } from 'react';
import {
  Modal,
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StyleSheet,
  Alert,
  ScrollView,
  KeyboardAvoidingView,
  Platform,
} from 'react-native';
import { useDataSigning } from '../../../../uniken/providers/SDKEventProvider';

const PasswordChallengeModal: React.FC = () => {
  const {
    passwordModalState,
    updatePasswordModalState,
    submitPassword,
    cancelPasswordModal,
  } = useDataSigning();

  const [isProcessing, setIsProcessing] = useState(false);

  const handleSubmit = async () => {
    setIsProcessing(true);

    try {
      await submitPassword();
    } catch (error) {
      console.error('PasswordChallengeModal - Submit error:', error);
    } finally {
      setIsProcessing(false);
    }
  };

  const handleCancel = async () => {
    setIsProcessing(true);

    try {
      await cancelPasswordModal();
    } catch (error) {
      console.error('PasswordChallengeModal - Cancel error:', error);
      updatePasswordModalState({ isVisible: false });
    } finally {
      setIsProcessing(false);
    }
  };

  return (
    <Modal
      visible={passwordModalState.isVisible}
      transparent={true}
      animationType="slide"
      onRequestClose={handleCancel}
    >
      <KeyboardAvoidingView
        style={styles.overlay}
        behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      >
        <View style={styles.modalContainer}>
          <ScrollView
            contentContainerStyle={styles.scrollContent}
            keyboardShouldPersistTaps="handled"
          >
            {/* Header */}
            <View style={styles.header}>
              <Text style={styles.title}>Authentication Required</Text>
              <Text style={styles.subtitle}>
                Please enter your password to complete data signing
              </Text>
            </View>

            {/* Authentication Options */}
            {passwordModalState.authenticationOptions.length > 0 && (
              <View style={styles.optionsSection}>
                <Text style={styles.optionsTitle}>Available Authentication Methods:</Text>
                {passwordModalState.authenticationOptions.map((option, index) => (
                  <Text key={index} style={styles.optionItem}>โ€ข {option}</Text>
                ))}
              </View>
            )}

            {/* Password Input */}
            <View style={styles.inputSection}>
              <Text style={styles.label}>Password *</Text>
              <TextInput
                style={styles.textInput}
                placeholder="Enter your password"
                value={passwordModalState.password}
                onChangeText={(value) => updatePasswordModalState({ password: value })}
                secureTextEntry
                autoFocus
                editable={!isProcessing}
              />

              {passwordModalState.attemptsLeft < 3 && (
                <Text style={styles.attemptsText}>
                  {passwordModalState.attemptsLeft} attempts remaining
                </Text>
              )}
            </View>

            {/* Action Buttons */}
            <View style={styles.buttonSection}>
              <TouchableOpacity
                style={[styles.button, styles.submitButton]}
                onPress={handleSubmit}
                disabled={isProcessing || !passwordModalState.password.trim()}
              >
                <Text style={styles.submitButtonText}>
                  {isProcessing ? 'Authenticating...' : 'Authenticate'}
                </Text>
              </TouchableOpacity>

              <TouchableOpacity
                style={[styles.button, styles.cancelButton]}
                onPress={handleCancel}
                disabled={isProcessing}
              >
                <Text style={styles.cancelButtonText}>Cancel</Text>
              </TouchableOpacity>
            </View>
          </ScrollView>
        </View>
      </KeyboardAvoidingView>
    </Modal>
  );
};

// Styles omitted for brevity - include comprehensive styling

export default PasswordChallengeModal;

Integration Benefits

  1. ๐ŸŽ›๏ธ Seamless UI Coordination: UI components automatically sync with SDK events
  2. ๐Ÿ”„ Automatic State Updates: Real-time updates across all components
  3. ๐Ÿ›ก๏ธ Error Boundary Protection: Comprehensive error handling at context level
  4. โšก Performance Optimization: Minimal re-renders with targeted state updates
  5. ๐Ÿงช Testing Friendly: Mockable context for unit testing

Let's implement comprehensive testing scenarios to validate your data signing implementation and ensure robust error handling.

Testing Scenarios Overview

Your data signing implementation should handle these key scenarios:

  1. โœ… Happy Path Flow: Complete successful signing
  2. โš ๏ธ Error Scenarios: Network failures, authentication errors
  3. ๐Ÿ”„ State Cleanup: Proper reset on cancellation
  4. ๐Ÿ”’ Security Validation: Authentication level enforcement
  5. ๐Ÿ“ฑ UI Responsiveness: Loading states and user feedback

Happy Path Testing

Test Case 1: Successful Data Signing Flow

// Manual Testing Steps:

1. **Input Validation**
   - Enter payload: "Test document for signing"
   - Select Auth Level: "RDNA_AUTH_LEVEL_4 (4)"
   - Select Authenticator Type: "RDNA_IDV_SERVER_BIOMETRIC (1)"
   - Enter reason: "Document authorization test"

2. **Form Submission**
   - Click "Sign Data" button
   - Verify loading state appears
   - Check console logs for:
     โœ… "DataSigningService - Starting data signing process"
     โœ… "RdnaService - AuthenticateUserAndSignData sync response success"

3. **Password Challenge** (if triggered)
   - Verify password modal appears
   - Enter correct password
   - Click "Authenticate" button
   - Check console logs for password submission success

4. **Final Result**
   - Verify navigation to results screen
   - Check all result fields are populated:
     โœ… Payload Signature (cryptographic signature)
     โœ… Data Signature ID (unique identifier)
     โœ… Original payload and parameters

5. **State Cleanup**
   - Click "Sign Another Document"
   - Verify return to input screen with clean state
   - Check console logs for successful state reset

Expected Console Output Pattern:

SDKEventProvider - Submitting data signing request
DataSigningService - Starting data signing process
RdnaService - Initiating data signing: {payloadLength: 29, authLevel: 4, ...}
RdnaService - AuthenticateUserAndSignData sync response success
[Password challenge if required]
SDKEventProvider - Data signing response received
SDKEventProvider - Data signing successful

Error Scenario Testing

Test Case 2: Authentication Failure Handling

// Simulate authentication failure:

1. **Trigger Error Scenario**
   - Use invalid credentials during password challenge
   - Or disconnect network during signing process
   - Or use unsupported authentication type

2. **Verify Error Handling**
   โœ… Error dialog displayed with user-friendly message
   โœ… Loading state properly cleared
   โœ… Form remains editable for retry
   โœ… Console logs show comprehensive error details

3. **Expected Error Messages**
   - "Authentication failed. Please check your credentials and try again." (Code 102)
   - "Authentication method not supported. Please try a different type." (Code 214)
   - "Operation cancelled by user." (Code 153)

Test Case 3: Network/Connection Errors

// Simulate network issues:

1. **Preparation**
   - Start data signing process
   - Disable network connection mid-process
   - Or simulate server timeout

2. **Verify Recovery**
   โœ… Appropriate error message shown
   โœ… State automatically reset
   โœ… User can retry operation
   โœ… No stuck loading states

State Cleanup Validation

Test Case 4: Cancel Flow Validation

// Test proper state cleanup:

1. **Password Modal Cancellation**
   - Start data signing process
   - When password modal appears, click "Cancel"
   - Verify:
     โœ… Modal closes immediately
     โœ… Form returns to editable state
     โœ… Console shows: "SDKEventProvider - Password modal cancelled successfully"
     โœ… Console shows: "RdnaService - ResetAuthenticateUserAndSignDataState sync response success"

2. **Navigation Cancellation**
   - Start signing process
   - Use device back button or navigation gesture
   - Verify proper state cleanup occurs

3. **Multiple Reset Calls**
   - Verify reset API can be called multiple times safely
   - No errors or crashes should occur

Security & Validation Testing

Test Case 5: Input Validation

// Test form validation:

1. **Required Field Validation**
   - Submit form with empty fields
   - Verify appropriate error messages

2. **Character Limit Validation**
   - Enter 501 characters in payload field
   - Enter 101 characters in reason field
   - Verify validation prevents submission

3. **Dropdown Validation**
   - Submit without selecting auth level/type
   - Verify validation error displayed

Test Case 6: Authentication Level Enforcement

// Test security levels:

1. **Level 4 Authentication (Recommended)**
   - Select "RDNA_AUTH_LEVEL_4 (4)"
   - Verify biometric challenge is triggered
   - Confirm highest security enforcement

2. **Different Auth Types**
   - Test each authenticator type option
   - Verify appropriate challenge type appears
   - Confirm expected authentication flow

UI Responsiveness Testing

Test Case 7: Loading States & User Feedback

// Test UI behavior:

1. **Loading Indicators**
   โœ… Submit button shows "Processing..." with spinner
   โœ… Form fields become disabled during processing
   โœ… Password modal shows processing state

2. **Copy Functionality** (Results Screen)
   โœ… Copy buttons work for all result fields
   โœ… "Copied" confirmation appears briefly
   โœ… Long signatures can be viewed in full

3. **Responsive Design**
   โœ… UI works on different screen sizes
   โœ… Keyboard handling works properly
   โœ… Modal layouts adapt to content size

Validation Checklist

Use this checklist to ensure comprehensive testing:

Core Functionality

Error Handling

Security Validation

UI/UX Quality

Debugging Console Commands

Use these console checks during testing:

// Check current state (in debugger)
console.log('Form State:', formState);
console.log('Password Modal:', passwordModalState);
console.log('Signing Result:', signingResult);

// Verify service availability
console.log('DataSigningService:', DataSigningService);
console.log('RdnaService methods:', Object.getOwnPropertyNames(rdnaService));

// Check event listener registration
console.log('Event Manager:', rdnaService.getEventManager());

Performance Monitoring

Monitor these performance indicators:

  1. Response Times: API calls should complete within 5-10 seconds
  2. Memory Usage: No memory leaks during repeated operations
  3. State Updates: UI should update within 100ms of state changes
  4. Network Efficiency: Minimal API calls with proper error recovery

Let's explore the essential security practices and production considerations for implementing REL-ID data signing in enterprise applications.

Authentication Level Guidelines

Security Level Selection Matrix (Data Signing Only)

Data Sensitivity

Recommended Level

Use Cases

Security Features

Testing/Development

Level 0

Testing environments only

โš ๏ธ No authentication - NOT for production

Public/Standard

Level 1

Standard documents, general approvals

Device biometric/passcode/password

Confidential/High

Level 4

Financial, legal, medical, high-value transactions

Biometric + Step-up Auth

Implementation Guidelines

// โœ… GOOD: Production-ready authentication level selection for data signing
const getRecommendedAuthLevel = (dataType: string): RDNAAuthLevel => {
  switch (dataType) {
    case 'financial_transaction':
    case 'legal_document':
    case 'medical_record':
    case 'high_value_approval':
      return RDNAAuthLevel.RDNA_AUTH_LEVEL_4; // Maximum security

    case 'general_document':
    case 'standard_approval':
      return RDNAAuthLevel.RDNA_AUTH_LEVEL_1; // Standard authentication

    case 'testing_only':
      return RDNAAuthLevel.NONE; // Testing only - never use in production

    default:
      return RDNAAuthLevel.RDNA_AUTH_LEVEL_4; // Default to maximum security
  }
};

// โœ… GOOD: Correct authenticator type pairing for data signing
const getCorrectAuthenticatorType = (authLevel: RDNAAuthLevel): RDNAAuthenticatorType => {
  switch (authLevel) {
    case RDNAAuthLevel.NONE:
    case RDNAAuthLevel.RDNA_AUTH_LEVEL_1:
      return RDNAAuthenticatorType.NONE; // Let REL-ID choose best available
    case RDNAAuthLevel.RDNA_AUTH_LEVEL_4:
      return RDNAAuthenticatorType.RDNA_IDV_SERVER_BIOMETRIC; // Required for Level 4
    default:
      throw new Error('Unsupported authentication level for data signing');
  }
};

// โŒ BAD: Using unsupported combinations
const badCombinations = [
  { authLevel: RDNAAuthLevel.RDNA_AUTH_LEVEL_2 }, // Will cause SDK error!
  { authLevel: RDNAAuthLevel.RDNA_AUTH_LEVEL_3 }, // Will cause SDK error!
  { authLevel: RDNAAuthLevel.RDNA_AUTH_LEVEL_4, authenticatorType: RDNAAuthenticatorType.RDNA_AUTH_PASS }, // Will cause SDK error!
];

State Management Security Patterns

Proper State Cleanup

// โœ… GOOD: Comprehensive state cleanup pattern
const secureStateCleanup = async () => {
  try {
    // 1. Reset SDK authentication state
    await rdnaService.resetAuthenticateUserAndSignDataState();

    // 2. Clear sensitive form data
    setFormState({
      payload: '', // Clear signed data
      selectedAuthLevel: '',
      selectedAuthenticatorType: '',
      reason: '',
      isLoading: false,
    });

    // 3. Reset authentication modal state
    setPasswordModalState({
      isVisible: false,
      password: '', // Clear password immediately
      challengeMode: 0,
      attemptsLeft: 3,
      authenticationOptions: [],
      isLargeModal: false,
      keyboardHeight: 0,
      responseData: null, // Clear challenge data
    });

    // 4. Clear results (optional - depends on requirements)
    setSigningResult(null);
    setResultDisplay(null);

    console.log('Secure state cleanup completed');
  } catch (error) {
    console.error('State cleanup failed - potential security risk:', error);
    // Force cleanup of local state even if SDK cleanup fails
    setFormState(getInitialFormState());
    setPasswordModalState(getInitialPasswordState());
  }
};

// โŒ BAD: Incomplete cleanup leaving sensitive data
const badCleanup = () => {
  setFormState({ ...formState, isLoading: false }); // Password still in memory!
};

Memory Management for Sensitive Data

// โœ… GOOD: Secure password handling
const handlePasswordSubmission = async (password: string) => {
  try {
    // Use password immediately
    await DataSigningService.submitPassword(password, challengeMode);

    // Clear password from all variables immediately
    password = ''; // Clear parameter
    setPasswordModalState(prev => ({ ...prev, password: '' })); // Clear state

  } catch (error) {
    // Clear password even on error
    password = '';
    setPasswordModalState(prev => ({ ...prev, password: '' }));
    throw error;
  }
};

// โŒ BAD: Password persists in memory
const badPasswordHandling = async (password: string) => {
  await submitPassword(password);
  // Password remains in memory and state!
};

Error Handling Security Patterns

Secure Error Message Display

// โœ… GOOD: Security-aware error handling
const getSecureErrorMessage = (error: any): string => {
  const errorCode = error?.error?.shortErrorCode || error?.code;

  switch (errorCode) {
    case 0:
      return 'Operation completed successfully';
    case 102:
      return 'Authentication failed. Please verify your credentials.';
    case 153:
      return 'Operation was cancelled by user.';
    case 214:
      return 'Authentication method not supported. Please try another method.';
    default:
      // โœ… GOOD: Don't expose internal error details
      console.error('Internal error details (not shown to user):', error);
      return 'Operation failed. Please try again or contact support.';
  }
};

// โŒ BAD: Exposing sensitive error information
const badErrorHandling = (error: any) => {
  Alert.alert('Error', JSON.stringify(error)); // Exposes internal details!
};

Comprehensive Error Recovery

// โœ… GOOD: Robust error recovery with security cleanup
const handleSigningError = async (error: any) => {
  console.error('Data signing error:', error);

  try {
    // 1. Attempt SDK state cleanup
    await DataSigningService.resetState();

    // 2. Clear sensitive local state
    await secureStateCleanup();

    // 3. Show user-friendly error
    const userMessage = getSecureErrorMessage(error);
    Alert.alert('Signing Error', userMessage, [
      { text: 'Try Again', onPress: () => resetToInitialState() },
      { text: 'Cancel', style: 'cancel' }
    ]);

  } catch (cleanupError) {
    console.error('Error cleanup failed:', cleanupError);
    // Force application to safe state
    await forceSecurityReset();
  }
};

Production Security Considerations

Audit Logging Requirements

// โœ… GOOD: Comprehensive audit logging
const auditSigningOperation = {
  logSigningAttempt: (payload: string, authLevel: number, userId: string) => {
    console.log('AUDIT: Data signing initiated', {
      timestamp: new Date().toISOString(),
      userId,
      payloadLength: payload.length,
      payloadHash: generateHash(payload), // Hash, not actual payload
      authLevel,
      sessionId: getCurrentSessionId(),
    });
  },

  logSigningSuccess: (signatureId: string, userId: string) => {
    console.log('AUDIT: Data signing successful', {
      timestamp: new Date().toISOString(),
      userId,
      signatureId,
      sessionId: getCurrentSessionId(),
    });
  },

  logSigningFailure: (error: any, userId: string) => {
    console.log('AUDIT: Data signing failed', {
      timestamp: new Date().toISOString(),
      userId,
      errorCode: error?.error?.shortErrorCode,
      sessionId: getCurrentSessionId(),
    });
  }
};

// โŒ BAD: No audit logging or logging sensitive data
console.log('Signing payload:', payload); // Exposes sensitive data!

Network Security Patterns

// โœ… GOOD: Network security considerations
const networkSecurityConfig = {
  // Use HTTPS only
  enforceHTTPS: true,

  // Implement request timeout
  requestTimeout: 30000, // 30 seconds

  // Retry with exponential backoff
  maxRetries: 3,
  baseDelay: 1000,

  // Certificate pinning (production)
  certificatePinning: true,
};

// Network request with security
const secureNetworkCall = async (requestData: any) => {
  try {
    // Add request timeout
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), networkSecurityConfig.requestTimeout);

    const response = await fetch(endpoint, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        // Add security headers
        'X-Requested-With': 'XMLHttpRequest',
      },
      body: JSON.stringify(requestData),
      signal: controller.signal,
    });

    clearTimeout(timeoutId);
    return response;

  } catch (error) {
    if (error.name === 'AbortError') {
      throw new Error('Request timeout - please check your connection');
    }
    throw error;
  }
};

Data Protection Guidelines

Sensitive Data Handling

// โœ… GOOD: Secure data handling patterns
const secureDataHandling = {
  // Don't store sensitive data unnecessarily
  minimizeDataStorage: (payload: string) => {
    // Store hash instead of original payload for verification
    return {
      payloadHash: generateHash(payload),
      payloadLength: payload.length,
      timestamp: Date.now(),
    };
  },

  // Clear data from memory after use
  secureClearData: (dataObject: any) => {
    Object.keys(dataObject).forEach(key => {
      if (typeof dataObject[key] === 'string') {
        dataObject[key] = ''; // Clear string data
      } else {
        dataObject[key] = null; // Clear object references
      }
    });
  },

  // Validate data before processing
  validateDataIntegrity: (data: any) => {
    // Implement checksum validation
    // Verify data hasn't been tampered with
    return isValidDataStructure(data) && hasValidChecksum(data);
  }
};

// โŒ BAD: Storing sensitive data unnecessarily
const badDataStorage = {
  lastPayload: 'sensitive document content', // Don't do this!
  userPasswords: ['password1', 'password2'], // Never do this!
};

Compliance & Regulatory Considerations

Industry-Specific Requirements

Industry

Key Requirements

Implementation Notes

Financial Services

SOX, PCI DSS, Basel III

Audit logs, data encryption, multi-factor auth

Healthcare

HIPAA, HITECH

Patient data protection, access controls

Government

FISMA, FedRAMP

High-security authentication, audit trails

Legal

eIDAS, ESIGN Act

Legal validity, non-repudiation

Regulatory Compliance Checklist

Performance & Scalability Patterns

// โœ… GOOD: Performance optimization patterns
const performanceOptimizations = {
  // Batch signing operations when possible
  batchSigning: async (payloads: string[]) => {
    // Process multiple documents in single authentication session
    return Promise.all(payloads.map(payload => signSinglePayload(payload)));
  },

  // Implement request deduplication
  deduplicateRequests: (requestId: string) => {
    // Prevent duplicate signing requests
    if (pendingRequests.has(requestId)) {
      return pendingRequests.get(requestId);
    }
    const promise = performSigning(requestId);
    pendingRequests.set(requestId, promise);
    return promise;
  },

  // Cache authentication state appropriately
  cacheAuthState: (duration: number = 300000) => { // 5 minutes
    // Cache authentication for short periods within same session
    // Balance security with user experience
  }
};

Congratulations! You've successfully implemented a complete, production-ready data signing solution using REL-ID SDK with React Native.

What You've Accomplished

Throughout this codelab, you've built:

๐Ÿ” Secure Cryptographic Signing System

๐Ÿ—๏ธ Production-Grade Architecture

๐Ÿ“ฑ Modern User Experience

๐Ÿ›ก๏ธ Enterprise Security Practices

Key Learning Outcomes

You've mastered these essential concepts:

  1. REL-ID Data Signing APIs: Deep understanding of authenticateUserAndSignData(), resetAuthenticateUserAndSignDataState(), and onAuthenticateUserAndSignData callback patterns
  2. Authentication Architecture: Implementation of authentication levels, authenticator types, and step-up authentication flows
  3. Event-Driven Integration: Seamless coordination between React UI components and REL-ID SDK event system
  4. Security Best Practices: Production-ready patterns for handling sensitive data, error recovery, and state cleanup
  5. REL-ID Integration Patterns: Proper SDK integration with comprehensive error handling and state management

Production Deployment Checklist

Before deploying to production, ensure:

Security Validation

Performance & Scalability

Next Steps & Advanced Features

Immediate Enhancements

  1. Batch Signing: Implement multiple document signing in single authentication session
  2. Signature Verification: Add signature validation and verification screens
  3. Document Templates: Pre-configured signing templates for common document types
  4. Offline Support: Cache signed documents for offline viewing and verification

Integration Opportunities

  1. Document Management: Integration with document storage systems
  2. Workflow Systems: Connect to approval and business process workflows
  3. Analytics Dashboard: User signing activity and security analytics
  4. Mobile Device Management: Enterprise MDM integration for additional security

Advanced Security Features

  1. Multi-Signature Support: Require multiple users to sign single document
  2. Time-Stamping: RFC 3161 timestamp authority integration
  3. Certificate Chaining: PKI certificate validation and trust chains
  4. Hardware Security Modules: HSM integration for enterprise deployments

Resources & Further Learning

REL-ID Documentation

Thank you for completing the REL-ID Data Signing Flow codelab! ๐ŸŽ‰

You're now equipped to build secure, production-grade data signing features in your Cordova applications using REL-ID SDK's powerful cryptographic capabilities.