This codelab demonstrates how to implement Mobile Threat Detection (MTD) flow using the react-native-rdna-client npm plugin. MTD now performs a synchronous check during the RELID SDK initialization to ensure critical threats are detected early. Once the SDK is successfully initialized, MTD continues monitoring asynchronously in the background to detect and respond to any emerging threats during runtime.

What You'll Learn

Get the Code from GitHub

The code to get started is stored 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-MTD folder in the repository you cloned earlier

What You'll Need

The sample app provides a complete MTD implementation. Let's examine the key components:

Component

Purpose

Sample App Reference

MTD Context

Global threat state management

src/uniken/MTDContext/MTDThreatContext.tsx

Threat Modal

UI for displaying threats

src/uniken/components/modals/ThreatDetectionModal.tsx

Event Handling

Extended event manager

src/uniken/services/rdnaEventManager.ts

Threat Types

TypeScript interfaces

src/uniken/types/rdnaEvents.ts

Permissions

Platform-Specific Permissions

The plugin requires specific permissions for optimal MTD functionality:

iOS Configuration: Refer to the iOS Permissions Documentation for complete Info.plist configuration including:

Android Configuration: Refer to the Android Permissions Documentation for runtime and normal permissions required for MTD features.

MTD Event Types

The RELID SDK triggers two main MTD events during initialization:

Event Type

Description

User Action Required

onUserConsentThreats

Non-terminating threats

User can choose to proceed or exit using takeActionOnThreats API

onTerminateWithThreats

Critical threats

Application must exit immediately

Define TypeScript interfaces for MTD threat handling:

// src/uniken/types/rdnaEvents.ts
export interface RDNAThreatInfo {
  threatName: string;
  threatMsg: string;
  threatId: number;
  threatCategory: string;        // SYSTEM, APP, NETWORK
  threatSeverity: string;        // LOW, MEDIUM, HIGH
  threatReason: string[] | string;
  networkInfo: {
    bssid: string;
    maliciousAddress: string;
    maliciousMacAddress: string;
    ssid: string;
  };
  appInfo: {
    appName: string;
    appSha256: string;
    packageName: string;
  };
  rememberActionForSession: number;  // 0/1 for API, handled as boolean in implementation
  configuredAction: string;
  shouldProceedWithThreats: number;  // 0/1 for API, handled as boolean in implementation
}

export interface RDNAUserConsentThreatsData {
  threats: RDNAThreatInfo[];
}

export interface RDNATerminateWithThreatsData {
  threats: RDNAThreatInfo[];
}

Threat Categories and Severities

Understanding threat classification helps in implementing:

Category

Examples

Platform

SYSTEM

Usb Debugging, Rooted Device

Android and iOS

NETWORK

Network MITM, Unsecured Access Point

Android and iOS

APP

Malware App, Repacked App

Only Android

Extend your existing event manager to handle MTD events:

// src/uniken/services/rdnaEventManager.ts (additions)
class RdnaEventManager {
  // Add MTD callback properties  
  private userConsentThreatsHandler?: RDNAUserConsentThreatsCallback;
  private terminateWithThreatsHandler?: RDNATerminateWithThreatsCallback;

  private registerEventListeners() {
    // ... existing listeners ...

    // Add MTD event listeners
    this.listeners.push(
      this.rdnaEmitter.addListener('onUserConsentThreats', this.onUserConsentThreats.bind(this)),
      this.rdnaEmitter.addListener('onTerminateWithThreats', this.onTerminateWithThreats.bind(this)),
    );
  }

  /**
   * Handles security threat events requiring user consent
   */
  private onUserConsentThreats(response: RDNAJsonResponse) {
    try {
      const threatArray = JSON.parse(response.response);
      const userConsentData: RDNAUserConsentThreatsData = { threats: threatArray };
      
      if (this.userConsentThreatsHandler) {
        this.userConsentThreatsHandler(userConsentData);
      }
    } catch (error) {
      console.error("Failed to parse user consent threats:", error);
    }
  }

  /**
   * Handles critical security threat events requiring app termination
   */
  private onTerminateWithThreats(response: RDNAJsonResponse) {
    try {
      const threatArray = JSON.parse(response.response);
      const terminateData: RDNATerminateWithThreatsData = { threats: threatArray };
      
      if (this.terminateWithThreatsHandler) {
        this.terminateWithThreatsHandler(terminateData);
      }
    } catch (error) {
      console.error("Failed to parse terminate threats:", error);
    }
  }

  public setUserConsentThreatsHandler(callback?: RDNAUserConsentThreatsCallback): void {
    this.userConsentThreatsHandler = callback;
  }

  public setTerminateWithThreatsHandler(callback?: RDNATerminateWithThreatsCallback): void {
    this.terminateWithThreatsHandler = callback;
  }
}

Key features of MTD event handling:

The takeActionOnThreats API is only required for handling threats received through the onUserConsentThreats event. This allows the application to take appropriate action based on user consent.

The onTerminateWithThreats event is triggered only when critical threats are detected. In such cases, the SDK automatically terminates internally, and no further actions can be performed through the plugin until the SDK is reinitialized.

Add threat response capability to your RELID service:

// src/uniken/services/rdnaService.ts (addition)
export class RdnaService {
  /**
   * Takes action on detected security threats
   * @param modifiedThreatsJson JSON string containing threat action decisions
   * @returns Promise<RDNASyncResponse> that resolves with sync response structure
   */
  async takeActionOnThreats(modifiedThreatsJson: string): Promise<RDNASyncResponse> {
    return new Promise((resolve, reject) => {
      RdnaClient.takeActionOnThreats(modifiedThreatsJson, response => {
        console.log('RdnaService - Take action on threats response received');

        const result: RDNASyncResponse = response;

        if (result.error && result.error.longErrorCode === 0) {
          console.log('RdnaService - Successfully took action on threats');
          resolve(result);
        } else {
          const errorMessage =
            result.error?.errorString ||
            'Unknown error from takeActionOnThreats';
          console.error(
            'RdnaService - Take action on threats failed:',
            errorMessage,
          );
          reject(result);
        }
      });
    });
  }
}

Important Response Parameters

When responding to threats, two key parameters control the behavior:

Parameter

Purpose

Implementation Values

API Values

shouldProceedWithThreats

Whether to continue despite threats

true = proceed, false = terminate

Converted to boolean in JSON

rememberActionForSession

Cache decision for session

true = remember, false = ask again

Converted to boolean in JSON

Note: While the API documentation may show these as number types (0/1), the actual implementation uses boolean values (true/false) for better code readability.

Create a React Context to manage MTD state across your application:

// src/uniken/MTDContext/MTDThreatContext.tsx
interface MTDThreatState {
  isModalVisible: boolean;
  threats: RDNAThreatInfo[];
  isConsentMode: boolean;
  isProcessing: boolean;
  showThreatModal: (threats: RDNAThreatInfo[], isConsent: boolean) => void;
  hideThreatModal: () => void;
  handleProceed: () => void;
  handleExit: () => void;
}

export const MTDThreatProvider: React.FC<MTDThreatProviderProps> = ({ children }) => {
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [threats, setThreats] = useState<RDNAThreatInfo[]>([]);
  const [isConsentMode, setIsConsentMode] = useState(false);
  const [isProcessing, setIsProcessing] = useState(false);

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

    eventManager.setUserConsentThreatsHandler((data: RDNAUserConsentThreatsData) => {
      showThreatModal(data.threats, true);
    });

    eventManager.setTerminateWithThreatsHandler((data: RDNATerminateWithThreatsData) => {
      showThreatModal(data.threats, false);
    });

    return () => {
      console.log('MTDThreatProvider cleanup');
    };
  }, []);
};

Threat Response Logic

The context handles user decisions by modifying threat objects:

const handleProceed = () => {
  setIsProcessing(true);

  // Modify all threats to proceed with action
  const modifiedThreats = threats.map(threat => ({
    ...threat,
    shouldProceedWithThreats: true,
    rememberActionForSession: true,
    threatReason: Array.isArray(threat.threatReason)
      ? threat.threatReason.join(',')
      : threat.threatReason
  }));

  const threatsJsonString = JSON.stringify(modifiedThreats);

  rdnaService.takeActionOnThreats(threatsJsonString)
    .then(() => {
      hideThreatModal();
    })
    .catch((error) => {
      setIsProcessing(false);
      Alert.alert('Failed to proceed with threats', error.errorString);
    });
};

const handleExit = () => {
  if (isConsentMode) {
    setIsProcessing(true);

    const modifiedThreats = threats.map(threat => ({
      ...threat,
      shouldProceedWithThreats: false,
      rememberActionForSession: true,
      threatReason: Array.isArray(threat.threatReason)
        ? threat.threatReason.join(',')
        : threat.threatReason
    }));

    const threatsJsonString = JSON.stringify(modifiedThreats);

    rdnaService.takeActionOnThreats(threatsJsonString)
      .then(() => {
        // Application will exit via terminateWithThreats callback
      })
      .catch((error) => {
        setIsProcessing(false);
        Alert.alert('Failed to process threat action', error.errorString);
      });
  } else {
    // Direct exit for terminate threats
    BackHandler.exitApp();
  }
};

Key features of the MTD context:

Create a modal component to display threat information to users:

// src/uniken/components/modals/ThreatDetectionModal.tsx
interface ThreatDetectionModalProps {
  visible: boolean;
  threats: RDNAThreatInfo[];
  isConsentMode: boolean;
  isProcessing?: boolean;
  onProceed?: () => void;
  onExit: () => void;
}

const ThreatDetectionModal: React.FC<ThreatDetectionModalProps> = ({
  visible,
  threats,
  isConsentMode,
  isProcessing = false,
  onProceed,
  onExit,
}) => {
  const getThreatSeverityColor = (severity: string): string => {
    switch (severity.toUpperCase()) {
      case 'HIGH': return '#dc2626';
      case 'MEDIUM': return '#f59e0b';  
      case 'LOW': return '#10b981';
      default: return '#6b7280';
    }
  };

  const renderThreatItem = (threat: RDNAThreatInfo, index: number) => (
    <View key={threat.threatId} style={styles.threatItem}>
      <Text style={styles.threatName}>{threat.threatName}</Text>
      <Text style={styles.threatMessage}>{threat.threatMsg}</Text>
      <View style={[styles.severityBadge,
        { backgroundColor: getThreatSeverityColor(threat.threatSeverity) }]}>
        <Text style={styles.severityText}>{threat.threatSeverity}</Text>
      </View>
    </View>
  );

  return (
    <Modal visible={visible} transparent={true} animationType="fade">
      <View style={styles.modalOverlay}>
        <View style={styles.modalContainer}>
          <Text style={styles.modalTitle}>
            {isConsentMode ? 'Security Threats Detected' : 'Critical Security Threat'}
          </Text>
          
          <ScrollView style={styles.threatsContainer}>
            {threats.map((threat, index) => renderThreatItem(threat, index))}
          </ScrollView>

          <View style={styles.buttonContainer}>
            {isConsentMode && onProceed && (
              <TouchableOpacity 
                style={styles.proceedButton} 
                onPress={onProceed}
                disabled={isProcessing}
              >
                <Text style={styles.buttonText}>
                  {isProcessing ? 'Processing...' : 'Proceed Anyway'}
                </Text>
              </TouchableOpacity>
            )}
            
            <TouchableOpacity 
              style={styles.exitButton} 
              onPress={onExit}
              disabled={isProcessing}
            >
              <Text style={styles.buttonText}>Exit Application</Text>
            </TouchableOpacity>
          </View>
        </View>
      </View>
    </Modal>
  );
};

Key features of the threat detection modal:

The following image showcases screen from the sample application:

Mobile Threat Detection Screen

Wrap your application with the MTD context provider:

// App.tsx
import {MTDThreatProvider} from './src/uniken/MTDContext';
import { NavigationContainer } from '@react-navigation/native';
import { navigationRef } from './src/tutorial/navigation/NavigationService';

const AppContent = () => {
  const {
    isModalVisible,
    threats,
    isConsentMode,
    isProcessing,
    handleProceed,
    handleExit,
  } = useMTDThreat();

  return (
    <>
      <NavigationContainer ref={navigationRef}>
        {/* Your existing navigation including SecurityExitScreen */}
      </NavigationContainer>
    </>
  );
};

function App() {
  return (
    <MTDThreatProvider>
      <AppContent />
    </MTDThreatProvider>
  );
}

The context provider approach offers several advantages:

Test Scenarios

  1. User Consent Threats: Test with low-medium severity threats
  2. Terminate Threats: Test with high severity threats requiring app exit
  3. Mixed Threat Types: Test scenarios with multiple threat categories
  4. User Interactions: Test both "Proceed" and "Exit" flows

Common MTD Test Scenarios

Threat Category

Examples

Typical Severity

Expected Response

SYSTEM

Usb Debugging, Rooted Device

LOW-HIGH

User consent or termination

NETWORK

Network MITM, Unsecured Access Point

LOW-MEDIUM

User consent or termination

APP

Malware App, Repacked App

MEDIUM-HIGH

User consent or termination

Debugging MTD Events

Use these debugging techniques to verify MTD functionality:

// Verify callback registration
console.log('MTD callbacks:', {
  userConsent: !!eventManager.onUserConsentThreatsCallback,
  terminate: !!eventManager.onTerminateWithThreatsCallback
});

// Log threat data
console.log('Received threats:', threats.map(t => ({
  name: t.threatName,
  severity: t.threatSeverity,
  category: t.threatCategory
})));

Modal Not Appearing

Cause: MTD callbacks not properly registered Solution: Verify MTDThreatProvider wraps your app and callbacks are set

Cause: Event listeners not attached Solution: Check that rdnaEmitter.addListener calls are successful

Threats Not Processed

Cause: Incorrect JSON format in takeActionOnThreats call Solution: Ensure threat objects are properly serialized

// Correct format
const threatsJsonString = JSON.stringify(modifiedThreats);
console.log('Sending to API:', threatsJsonString);

Cause: Missing required threat properties Solution: Verify all required properties are present in threat objects

App Not Exiting

Cause: BackHandler.exitApp() may not work as expected on all platforms

Solution: Use platform-appropriate exit handling

const handleExit = () => {
  if (isConsentMode) {
    // For consent threats, call takeActionOnThreats first
    // This will trigger terminateWithThreats callback which handles exit
  } else {
    // For terminate threats, exit directly
    BackHandler.exitApp();
  }
};

Best Practice: Test exit behavior on both iOS and Android devices

Important Security Guidelines

  1. Never ignore terminate threats - Always exit the app when required
  2. Log threat details securely - For analysis without exposing sensitive data
  3. Educate users appropriately - Provide clear but not alarming explanations
  4. Keep SDK updated - Regular updates include latest threat detection
  5. Test thoroughly - Verify MTD behavior across different device states

Threat Response Guidelines

Threat Severity

Recommended Action

User Choice

LOW

Usually proceed with warning

User decides

MEDIUM

Proceed with caution

User decides with strong warning

HIGH

Consider termination

Limited or no user choice

Memory and Performance

Congratulations! You've successfully learned how to implement comprehensive MTD functionality with:

Key Security Benefits

Your MTD implementation now provides:

Next Steps