React.js SDK Guide
Installation
bash
npm install axios
# or
yarn add axiosBasic Usage
jsx
import React, { useState } from 'react';
import axios from 'axios';
const API_BASE_URL = 'https://keyclaim.org/api';
const API_KEY = 'kc_your_api_key_here';
function ChallengeValidation() {
const [challenge, setChallenge] = useState('');
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(false);
// Step 1: Create challenge
const createChallenge = async () => {
setLoading(true);
try {
const response = await axios.post(`${API_BASE_URL}/challenge/create`, {
key: API_KEY,
ttl: 30
});
setChallenge(response.data.challenge);
} catch (error) {
console.error('Failed to create challenge:', error);
} finally {
setLoading(false);
}
};
// Step 2: Generate HMAC response
const generateResponse = async (challenge) => {
// Use Web Crypto API for HMAC
const encoder = new TextEncoder();
const keyData = encoder.encode(API_KEY);
const messageData = encoder.encode(challenge);
const cryptoKey = await crypto.subtle.importKey(
'raw',
keyData,
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);
const hashArray = Array.from(new Uint8Array(signature));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
};
// Step 3: Validate
const validateChallenge = async () => {
if (!challenge) return;
setLoading(true);
try {
const response = await generateResponse(challenge);
const result = await axios.post(`${API_BASE_URL}/challenge/validate`, {
key: API_KEY,
challenge: challenge,
response: response
});
setResult(result.data);
} catch (error) {
console.error('Validation failed:', error);
} finally {
setLoading(false);
}
};
return (
<div>
<button onClick={createChallenge} disabled={loading}>
Create Challenge
</button>
{challenge && (
<>
<p>Challenge: {challenge}</p>
<button onClick={validateChallenge} disabled={loading}>
Validate
</button>
</>
)}
{result && (
<div>
<p>{result.valid ? '✓ Valid' : '✗ Invalid'}</p>
{result.signature && <p>Signature: {result.signature}</p>}
</div>
)}
</div>
);
}
export default ChallengeValidation;Using Frontend Purpose Keys
jsx
import React, { useState } from 'react';
const FRONTEND_TOKEN = process.env.REACT_APP_KEYCLAIM_TOKEN;
function FrontendValidation() {
const [challenge, setChallenge] = useState('');
const [result, setResult] = useState(null);
const createChallenge = async () => {
const res = await fetch('https://keyclaim.org/api/challenge/frontend-create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: FRONTEND_TOKEN, ttl: 30 })
});
const data = await res.json();
setChallenge(data.challenge);
};
const validateChallenge = async () => {
const res = await fetch('https://keyclaim.org/api/challenge/frontend-validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: FRONTEND_TOKEN, challenge })
});
const data = await res.json();
setResult(data);
};
return (
<div>
<button onClick={createChallenge}>Create Challenge</button>
{challenge && (
<>
<p>Challenge: {challenge}</p>
<button onClick={validateChallenge}>Validate</button>
</>
)}
{result && <p>{result.valid ? '✓ Valid' : '✗ Invalid'}</p>}
</div>
);
}RSA Decryption (Client-Side)
jsx
async function decryptChallenge(encryptedChallenge, privateKeyPEM) {
// Convert PEM to ArrayBuffer
const pemHeader = '-----BEGIN PRIVATE KEY-----';
const pemFooter = '-----END PRIVATE KEY-----';
const pemContents = privateKeyPEM
.replace(pemHeader, '')
.replace(pemFooter, '')
.replace(/\s/g, '');
const binary = atob(pemContents);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
// Import key
const key = await crypto.subtle.importKey(
'pkcs8',
bytes.buffer,
{
name: 'RSA-OAEP',
hash: 'SHA-256'
},
false,
['decrypt']
);
// Decrypt
const encryptedBytes = Uint8Array.from(atob(encryptedChallenge), c => c.charCodeAt(0));
const decrypted = await crypto.subtle.decrypt(
{ name: 'RSA-OAEP' },
key,
encryptedBytes
);
return new TextDecoder().decode(decrypted);
}