⚛️ React Native SDK

React Native SDK ⚛️

Build secure cross-platform mobile apps with hardware-backed device attestation and 20+ AI providers. One codebase - iOS AND Android protected!

🔒

How it works: Every API request is cryptographically verified using Apple’s App Attest (iOS) or Google’s Play Integrity (Android). This proves the request came from YOUR legitimate app on a real device - not a hacker, bot, or compromised device. Your API keys never touch the device.

Requirements

RequirementDetails
React Native 0.70+Modern React Native version
iOS 14.0+App Attest support
Android API 24+Play Integrity support
Physical DeviceSimulators/emulators have limited attestation
ProtectMyAPI AccountSign up free
⚠️

Testing: Test on real devices! iOS Simulator doesn’t support App Attest, and Android Emulator has limited Play Integrity support.


Installation

Add the Package

# Using npm
npm install @protectmyapi/react-native-sdk
 
# Using yarn
yarn add @protectmyapi/react-native-sdk
 
# Using pnpm
pnpm add @protectmyapi/react-native-sdk

iOS Setup

  1. Install pods:
cd ios && pod install
  1. Open your project in Xcode
  2. Select your app target → Signing & Capabilities
  3. Click + Capability → Add App Attest
📱

Ensure your Bundle ID matches what’s configured in the ProtectMyAPI Dashboard.

Android Setup

  1. Go to Google Cloud Console
  2. Enable Play Integrity API
  3. In ProtectMyAPI Dashboard, add:
    • Your Package Name (e.g., com.yourcompany.yourapp)
    • Your SHA-256 fingerprint
# Find your SHA-256
cd android && ./gradlew signingReport

Update your android/app/build.gradle:

android {
    defaultConfig {
        minSdkVersion 24  // Required for Play Integrity
    }
}

Initialize the SDK

import { ProtectMyAPI } from '@protectmyapi/react-native-sdk';
 
// In your app entry point (App.tsx or index.js)
async function initializeApp() {
  await ProtectMyAPI.initialize({
    appToken: 'app_your_token_here',
    environment: 'production',
    enableAttestation: true,
    enableSecurityChecks: true,
  });
}
 
// Call before your app renders
initializeApp().then(() => {
  // App is ready
});

Configuration Options

The SDK offers extensive configuration:

import { ProtectMyAPI } from '@protectmyapi/react-native-sdk';
 
await ProtectMyAPI.initialize({
  // Required
  appToken: 'app_your_token_here',
  
  // Environment
  environment: 'production',           // 'development' | 'staging' | 'production'
  
  // Security Options
  enableAttestation: true,             // Enable App Attest / Play Integrity
  enableSecurityChecks: true,          // Run security checks
  allowEmulator: false,                // Block emulators/simulators
  allowRootedDevices: false,           // Block rooted/jailbroken devices
  enableCertificatePinning: true,      // Pin TLS certificates
  certificatePins: [                   // Custom certificate hashes
    'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
  ],
  
  // Network Options
  timeout: 30000,                      // Request timeout in ms
  debugMode: false,                    // Enable debug logging
  
  // Retry Configuration
  retryConfig: {
    maxRetryAttempts: 3,               // Auto-retry failed requests
    initialRetryDelayMs: 1000,         // Initial retry delay
    maxRetryDelayMs: 30000,            // Max retry delay
    retryBackoffMultiplier: 2.0,       // Exponential backoff
  },
});

Configuration Parameters

ParameterTypeDefaultDescription
appTokenstringRequiredYour app token from the dashboard
environmentstring'production''development', 'staging', or 'production'
enableAttestationbooleantrueEnable device attestation
enableSecurityChecksbooleantrueRun jailbreak/root detection
allowEmulatorbooleanfalseAllow emulators/simulators
allowRootedDevicesbooleanfalseAllow rooted/jailbroken devices
enableCertificatePinningbooleanfalsePin TLS certificates
certificatePinsstring[][]Custom certificate pin hashes
timeoutnumber30000Network timeout in milliseconds
debugModebooleanfalseEnable debug logging

Retry Configuration

ParameterTypeDefaultDescription
maxRetryAttemptsnumber3Maximum retry attempts
initialRetryDelayMsnumber1000Initial delay in milliseconds
maxRetryDelayMsnumber30000Maximum delay in milliseconds
retryBackoffMultipliernumber2.0Backoff multiplier

Making Secure Requests

Basic Requests

import { ProtectMyAPI } from '@protectmyapi/react-native-sdk';
 
// GET request
const users = await ProtectMyAPI.get('/api/users');
 
// POST request
const newUser = await ProtectMyAPI.post('/api/users', {
  name: 'John Doe',
  email: '[email protected]',
});
 
// PUT request
const updated = await ProtectMyAPI.put('/api/users/123', {
  name: 'John Updated',
});
 
// PATCH request
const patched = await ProtectMyAPI.patch('/api/users/123', {
  email: '[email protected]',
});
 
// DELETE request
await ProtectMyAPI.delete('/api/users/123');

Full Request Options

const response = await ProtectMyAPI.secureRequest({
  endpoint: '/api/data',
  method: 'POST',
  body: {
    key: 'value',
  },
  headers: {
    'X-Custom-Header': 'custom-value',
  },
});

TypeScript Support

interface User {
  id: string;
  name: string;
  email: string;
}
 
// Typed responses
const user = await ProtectMyAPI.get<User>('/api/users/123');
console.log(user.name); // TypeScript knows this is a string
 
const users = await ProtectMyAPI.get<User[]>('/api/users');
users.forEach(u => console.log(u.email));

React Hooks

The SDK provides React hooks for seamless integration:

useProtectMyAPI

Manage SDK lifecycle and initialization:

import { useProtectMyAPI } from '@protectmyapi/react-native-sdk';
 
function App() {
  const { 
    isInitialized, 
    isInitializing, 
    error, 
    initialize,
    securityReport,
    refreshSecurityReport,
  } = useProtectMyAPI();
 
  useEffect(() => {
    if (!isInitialized && !isInitializing) {
      initialize({
        appToken: 'app_your_token_here',
        environment: __DEV__ ? 'development' : 'production',
        allowEmulator: __DEV__,
      });
    }
  }, [isInitialized, isInitializing]);
 
  if (isInitializing) {
    return <LoadingScreen />;
  }
 
  if (error) {
    return <ErrorScreen error={error} />;
  }
 
  return <MainApp />;
}

useSecureRequest

Make API requests with loading and error states:

import { useSecureRequest } from '@protectmyapi/react-native-sdk';
 
interface UserProfile {
  id: string;
  name: string;
  avatar: string;
}
 
function ProfileScreen() {
  const { 
    data, 
    loading, 
    error, 
    execute,
    reset,
  } = useSecureRequest<UserProfile>('/api/user/profile');
 
  useEffect(() => {
    execute();
  }, []);
 
  if (loading) {
    return <ActivityIndicator />;
  }
 
  if (error) {
    return (
      <View>
        <Text>Error: {error.message}</Text>
        <Button title="Retry" onPress={() => execute()} />
      </View>
    );
  }
 
  return (
    <View>
      <Image source={{ uri: data?.avatar }} />
      <Text>{data?.name}</Text>
    </View>
  );
}

useSecurityReport

Monitor device security status:

import { useSecurityReport } from '@protectmyapi/react-native-sdk';
 
function SecurityStatusBadge() {
  const { 
    report, 
    isSecure, 
    loading, 
    refresh,
  } = useSecurityReport({
    refreshOnForeground: true,
    refreshInterval: 60000, // Check every minute
    allowEmulator: __DEV__,
    allowRootedDevices: false,
  });
 
  if (!isSecure) {
    return (
      <View style={styles.warning}>
        <Text>⚠️ Security Issue Detected</Text>
        {report?.isRooted && <Text>Device is rooted/jailbroken</Text>}
        {report?.isEmulator && <Text>Running on emulator</Text>}
        {report?.isHooked && <Text>Hooking framework detected</Text>}
      </View>
    );
  }
 
  return (
    <View style={styles.secure}>
      <Text>✅ Device Secure</Text>
    </View>
  );
}

Security Features

Security Report

import { ProtectMyAPI } from '@protectmyapi/react-native-sdk';
 
const report = await ProtectMyAPI.getSecurityReport();
 
console.log('Rooted/Jailbroken:', report.isRooted);
console.log('Emulator:', report.isEmulator);
console.log('Hooks detected:', report.isHooked);
console.log('Debugger attached:', report.isDebuggerAttached);
console.log('App tampered:', report.isAppTampered);
console.log('Reverse engineered:', report.isReverseEngineered);
console.log('Failed checks:', report.failedChecks);

Manual Security Checks

import { SecurityChecker } from '@protectmyapi/react-native-sdk';
 
// Individual checks
const isRooted = await SecurityChecker.isDeviceRooted();
const isEmulator = await SecurityChecker.isEmulator();
const isHooked = await SecurityChecker.isHookingDetected();
const isDebugged = await SecurityChecker.isDebuggerAttached();
const isTampered = await SecurityChecker.isAppTampered();
 
// Block if compromised
if (isRooted || isHooked) {
  Alert.alert(
    'Security Alert',
    'This app cannot run on compromised devices.',
    [{ text: 'OK', onPress: () => BackHandler.exitApp() }]
  );
}

Enforce Security Checks

import { SecurityChecker } from '@protectmyapi/react-native-sdk';
 
try {
  await SecurityChecker.enforceSecurityChecks({
    allowEmulator: __DEV__,
    allowRootedDevices: false,
  });
  // All checks passed
} catch (error) {
  // Security violation detected
  console.error('Security violation:', error.message);
}

What’s Detected

CheckDescription
JailbreakCydia, Sileo, checkra1n, unc0ver, Substitute
HookingFrida, Cycript, Substrate, LibHook
Debuggerlldb, gdb attached debuggers
SimulatoriOS Simulator detection
TamperingCode signature validation, binary modifications
Reverse EngineeringDisassembly tools, runtime introspection

Error Handling

Error Types

import { 
  ProtectMyAPI, 
  ProtectMyAPIError, 
  ErrorCode 
} from '@protectmyapi/react-native-sdk';
 
try {
  const data = await ProtectMyAPI.get('/api/data');
} catch (error) {
  if (error instanceof ProtectMyAPIError) {
    switch (error.code) {
      case ErrorCode.NOT_INITIALIZED:
        console.error('SDK not initialized');
        break;
        
      case ErrorCode.ATTESTATION_FAILED:
        console.error('Device attestation failed');
        console.error('Reason:', error.integrityReason);
        break;
        
      case ErrorCode.SECURITY_VIOLATION:
        console.error('Security violation:', error.violationType);
        // e.g., 'ROOTED_DEVICE', 'HOOK_DETECTED', 'EMULATOR_DETECTED'
        break;
        
      case ErrorCode.NETWORK_ERROR:
        console.error('Network error:', error.message);
        break;
        
      case ErrorCode.SERVER_ERROR:
        console.error(`Server error (${error.statusCode}):`, error.message);
        break;
        
      case ErrorCode.RATE_LIMITED:
        console.error('Rate limit exceeded');
        break;
        
      case ErrorCode.UNAUTHORIZED:
        console.error('Invalid app token');
        break;
        
      default:
        console.error('Unknown error:', error.message);
    }
    
    // Check if error is retryable
    if (error.isRetryable()) {
      // Implement retry logic
    }
  }
}

Error Codes Reference

CodeDescription
NOT_INITIALIZEDSDK not initialized
INVALID_CONFIGURATIONInvalid configuration
ATTESTATION_FAILEDDevice attestation failed
ATTESTATION_NOT_SUPPORTEDAttestation not supported on device
SECURITY_VIOLATIONSecurity check failed
NETWORK_ERRORNetwork connectivity issue
SERVER_ERRORServer returned an error
RATE_LIMITEDRate limit exceeded
UNAUTHORIZEDInvalid or expired app token
INVALID_RESPONSEInvalid response from server
CERTIFICATE_PINNING_FAILEDCertificate pinning validation failed

Complete Example

Here’s a full example of a React Native app using the SDK:

import React, { useEffect, useState } from 'react';
import {
  SafeAreaView,
  View,
  Text,
  FlatList,
  TouchableOpacity,
  ActivityIndicator,
  StyleSheet,
  Alert,
} from 'react-native';
import {
  ProtectMyAPI,
  useProtectMyAPI,
  useSecureRequest,
  useSecurityReport,
  ProtectMyAPIError,
  ErrorCode,
} from '@protectmyapi/react-native-sdk';
 
// Types
interface Post {
  id: string;
  title: string;
  body: string;
}
 
// App Component
export default function App() {
  const { isInitialized, isInitializing, error, initialize } = useProtectMyAPI();
  const { isSecure, report } = useSecurityReport({ allowEmulator: __DEV__ });
 
  useEffect(() => {
    initialize({
      appToken: 'app_your_token_here',
      environment: __DEV__ ? 'development' : 'production',
      enableAttestation: true,
      enableSecurityChecks: true,
      allowEmulator: __DEV__,
      debugMode: __DEV__,
    });
  }, []);
 
  if (isInitializing) {
    return (
      <SafeAreaView style={styles.container}>
        <ActivityIndicator size="large" />
        <Text>Initializing...</Text>
      </SafeAreaView>
    );
  }
 
  if (error) {
    return (
      <SafeAreaView style={styles.container}>
        <Text style={styles.error}>Error: {error.message}</Text>
      </SafeAreaView>
    );
  }
 
  if (!isSecure) {
    return (
      <SafeAreaView style={styles.container}>
        <Text style={styles.error}>⚠️ Security Issue Detected</Text>
        <Text>{report?.failedChecks.join(', ')}</Text>
      </SafeAreaView>
    );
  }
 
  return (
    <SafeAreaView style={styles.container}>
      <PostsList />
    </SafeAreaView>
  );
}
 
// Posts List Component
function PostsList() {
  const { data: posts, loading, error, execute } = useSecureRequest<Post[]>('/api/posts');
  const [creating, setCreating] = useState(false);
 
  useEffect(() => {
    execute();
  }, []);
 
  const createPost = async () => {
    setCreating(true);
    try {
      await ProtectMyAPI.post('/api/posts', {
        title: 'New Post',
        body: 'This is a secure post!',
      });
      execute(); // Refresh list
      Alert.alert('Success', 'Post created!');
    } catch (err) {
      if (err instanceof ProtectMyAPIError) {
        Alert.alert('Error', err.message);
      }
    } finally {
      setCreating(false);
    }
  };
 
  if (loading) {
    return <ActivityIndicator size="large" />;
  }
 
  if (error) {
    return (
      <View>
        <Text style={styles.error}>{error.message}</Text>
        <TouchableOpacity onPress={() => execute()}>
          <Text>Retry</Text>
        </TouchableOpacity>
      </View>
    );
  }
 
  return (
    <View style={styles.flex}>
      <TouchableOpacity 
        style={styles.button} 
        onPress={createPost}
        disabled={creating}
      >
        <Text style={styles.buttonText}>
          {creating ? 'Creating...' : 'Create Post'}
        </Text>
      </TouchableOpacity>
      
      <FlatList
        data={posts}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View style={styles.post}>
            <Text style={styles.title}>{item.title}</Text>
            <Text>{item.body}</Text>
          </View>
        )}
      />
    </View>
  );
}
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#fff',
  },
  flex: {
    flex: 1,
  },
  error: {
    color: 'red',
    fontSize: 16,
    textAlign: 'center',
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 12,
    borderRadius: 8,
    marginBottom: 16,
  },
  buttonText: {
    color: '#fff',
    textAlign: 'center',
    fontWeight: '600',
  },
  post: {
    padding: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#eee',
  },
  title: {
    fontSize: 18,
    fontWeight: '600',
    marginBottom: 8,
  },
});

AI Integration

The SDK works seamlessly with all ProtectMyAPI AI providers:

// OpenAI Chat
const response = await ProtectMyAPI.post('/openai/chat', {
  message: 'Hello, how are you?',
  model: 'gpt-4o-mini',
});
 
// Anthropic Claude
const response = await ProtectMyAPI.post('/anthropic/chat', {
  message: 'Explain quantum computing',
  model: 'claude-3-5-sonnet-latest',
});
 
// Generate Images with DALL-E
const image = await ProtectMyAPI.post('/openai/images', {
  prompt: 'A sunset over mountains',
  size: '1024x1024',
});
 
// Transcribe Audio
const formData = new FormData();
formData.append('file', audioFile);
const transcription = await ProtectMyAPI.post('/openai/transcribe', formData);
🤖

See the AI Providers section for complete documentation on all 20+ supported AI providers.


Troubleshooting

iOS: “App Attest not available”

  • Ensure you’re running on a physical device (not simulator)
  • Verify the App Attest capability is enabled in Xcode
  • Check that your Bundle ID matches the ProtectMyAPI Dashboard

Android: “Play Integrity unavailable”

  • Ensure you have Google Play Services installed
  • The device must pass basic integrity checks
  • For testing, use a signed release build

”Security violation detected”

This occurs when security checks detect a compromised environment:

// For development only:
await ProtectMyAPI.initialize({
  appToken: 'your-token',
  allowEmulator: __DEV__,      // Allow in development
  allowRootedDevices: false,   // Never allow in production
  debugMode: __DEV__,
});
🚫

Never disable security checks in production builds!

Network Errors

The SDK includes automatic retry with exponential backoff:

await ProtectMyAPI.initialize({
  appToken: 'your-token',
  retryConfig: {
    maxRetryAttempts: 5,        // Increase retries
    initialRetryDelayMs: 2000,  // Start with 2s delay
    maxRetryDelayMs: 60000,     // Max 60s delay
    retryBackoffMultiplier: 2,  // Double each time
  },
});

Best Practices


Migration from Other SDKs

From iOS SDK

The React Native SDK follows similar patterns:

// iOS Swift
ProtectMyAPI.shared.configure(appId: "token")
let data = try await ProtectMyAPI.shared.get(endpoint: "/api/data")
// React Native
await ProtectMyAPI.initialize({ appToken: 'token' });
const data = await ProtectMyAPI.get('/api/data');

From Android SDK

// Android Kotlin
ProtectMyAPI.initialize(context, config)
val data = ProtectMyAPI.get("/api/data")
// React Native
await ProtectMyAPI.initialize({ appToken: 'token' });
const data = await ProtectMyAPI.get('/api/data');

From Flutter SDK

// Flutter Dart
await ProtectMyAPI.initialize(appId: 'token');
final data = await ProtectMyAPI.instance.get('/api/data');
// React Native
await ProtectMyAPI.initialize({ appToken: 'token' });
const data = await ProtectMyAPI.get('/api/data');

Support