Back to Documentation
React Native Integration

React Native Integration

Cross-platform mobile integration guide for React Native

React Native SDK Guide

Installation

bash
npm install react-native-crypto-js
npm install @react-native-async-storage/async-storage
# or
yarn add react-native-crypto-js @react-native-async-storage/async-storage

For iOS, you may need:

bash
cd ios && pod install

Basic Usage

javascript
import CryptoJS from 'react-native-crypto-js';
import AsyncStorage from '@react-native-async-storage/async-storage';

const API_BASE_URL = 'https://keyclaim.org/api';
const API_KEY = 'kc_your_api_key_here';

// Step 1: Create a challenge
async function createChallenge() {
  try {
    const response = await fetch(`${API_BASE_URL}/challenge/create`, {
      method: 'POST',
      headers: { 
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${API_KEY}`
      },
      body: JSON.stringify({
        ttl: 30
      })
    });
    
    const data = await response.json();
    return data.challenge;
  } catch (error) {
    console.error('Failed to create challenge:', error);
    throw error;
  }
}

// Step 2: Generate HMAC response
function generateResponseHMAC(challenge) {
  return CryptoJS.HmacSHA256(challenge, API_KEY).toString();
}

// Step 3: Decrypt challenge with private key (using react-native-rsa-native)
// Note: You'll need to install: npm install react-native-rsa-native
import { RSA } from 'react-native-rsa-native';

async function decryptChallenge(encryptedChallenge, privateKeyPEM) {
  try {
    const decrypted = await RSA.decrypt(encryptedChallenge, privateKeyPEM);
    return decrypted;
  } catch (error) {
    console.error('Failed to decrypt:', error);
    throw error;
  }
}

// Step 4: Validate response
async function validateChallenge(challenge, response, decryptedChallenge = null) {
  try {
    const payload = {
      key: API_KEY,
      challenge: challenge,
      response: response
    };
    
    if (decryptedChallenge) {
      payload.decryptedChallenge = decryptedChallenge;
    }
    
    const result = await fetch(`${API_BASE_URL}/challenge/validate`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    
    return await result.json();
  } catch (error) {
    console.error('Validation failed:', error);
    throw error;
  }
}

// Complete example (HMAC)
async function mainHMAC() {
  try {
    const challenge = await createChallenge();
    console.log('Challenge:', challenge);
    
    const response = generateResponseHMAC(challenge);
    console.log('Response:', response);
    
    const result = await validateChallenge(challenge, response);
    console.log('Validation result:', result);
    
    if (result.valid) {
      console.log('✓ Challenge validated successfully!');
    }
  } catch (error) {
    console.error('Error:', error);
  }
}

// Complete example (RSA)
async function mainRSA(privateKeyPEM) {
  try {
    // If API key has a key pair assigned, challenge will be automatically encrypted
    const encryptedChallenge = await createChallenge();
    console.log('Challenge (may be encrypted if key pair is assigned):', encryptedChallenge);
    
    // Decrypt if encrypted (check if it's base64 and longer than normal challenge)
    const decrypted = await decryptChallenge(encryptedChallenge, privateKeyPEM);
    console.log('Decrypted Challenge:', decrypted);
    
    const result = await validateChallenge(
      encryptedChallenge,
      decrypted,
      decrypted
    );
    
    console.log('Validation result:', result);
    
    if (result.valid) {
      console.log('✓ Challenge validated successfully!');
    }
  } catch (error) {
    console.error('Error:', error);
  }
}

React Component Example

jsx
import React, { useState } from 'react';
import { View, Text, Button, TextInput, Alert } from 'react-native';
import CryptoJS from 'react-native-crypto-js';

export default function ChallengeValidation() {
  const [challenge, setChallenge] = useState('');
  const [response, setResponse] = useState('');
  const [result, setResult] = useState(null);
  const [loading, setLoading] = useState(false);

  const API_KEY = 'kc_your_api_key_here';

  const createChallenge = async () => {
    setLoading(true);
    try {
      const res = await fetch('https://keyclaim.org/api/challenge/create', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ key: API_KEY, ttl: 30 })
      });
      const data = await res.json();
      setChallenge(data.challenge);
    } catch (error) {
      Alert.alert('Error', 'Failed to create challenge');
    } finally {
      setLoading(false);
    }
  };

  const validateChallenge = async () => {
    if (!challenge) return;
    
    setLoading(true);
    try {
      const hmacResponse = CryptoJS.HmacSHA256(challenge, API_KEY).toString();
      
      const res = await fetch('https://keyclaim.org/api/challenge/validate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          key: API_KEY,
          challenge: challenge,
          response: hmacResponse
        })
      });
      
      const data = await res.json();
      setResult(data);
    } catch (error) {
      Alert.alert('Error', 'Validation failed');
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <Button title="Create Challenge" onPress={createChallenge} disabled={loading} />
      
      {challenge && (
        <>
          <TextInput
            value={challenge}
            editable={false}
            style={{ borderWidth: 1, padding: 10, marginTop: 10 }}
          />
          <Button title="Validate" onPress={validateChallenge} disabled={loading} />
        </>
      )}
      
      {result && (
        <Text style={{ marginTop: 20 }}>
          {result.valid ? '✓ Valid' : '✗ Invalid'}
        </Text>
      )}
    </View>
  );
}

Storing Private Keys Securely

javascript
import * as Keychain from 'react-native-keychain';
// npm install react-native-keychain

// Save private key securely
async function savePrivateKey(privateKey) {
  await Keychain.setGenericPassword('keyclaim_private_key', privateKey);
}

// Retrieve private key
async function getPrivateKey() {
  const credentials = await Keychain.getGenericPassword();
  return credentials ? credentials.password : null;
}

// Usage
const privateKey = await getPrivateKey();
if (privateKey) {
  const decrypted = await decryptChallenge(encryptedChallenge, privateKey);
}