Back to Documentation
React.js Integration

React.js Integration

Client-side integration guide for React applications

React.js SDK Guide

Installation

bash
npm install axios
# or
yarn add axios

Basic 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);
}