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-storageFor iOS, you may need:
bash
cd ios && pod installBasic 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);
}