Back to Documentation
Node.js Documentation

Node.js Documentation

JavaScript/TypeScript integration guide

Official GuideProduction Ready

Node.js SDK Guide

Official SDK Installation

We provide official SDK packages for Node.js and TypeScript that you can install directly from npm:

Node.js SDK

bash
npm install @keyclaim/sdk
# or
yarn add @keyclaim/sdk

TypeScript SDK

bash
npm install @keyclaim/sdk-typescript
# or
yarn add @keyclaim/sdk-typescript

Quick Start with Official SDK

Node.js

javascript
const { KeyClaimClient } = require('@keyclaim/sdk');

// Initialize client (base URL is automatically set to https://keyclaim.org)
const client = new KeyClaimClient({
  apiKey: 'kc_your_api_key_here',
  secret: 'your-secret-key' // Optional, defaults to API key
});

// Complete validation flow
async function main() {
  try {
    // Create challenge, generate response, and validate in one call
    const result = await client.validate('hmac', 30);
    
    if (result.valid) {
      console.log('✓ Validation successful!');
      console.log('Signature:', result.signature);
      console.log('Quota:', result.quota);
    } else {
      console.log('✗ Validation failed:', result.error);
    }
  } catch (error) {
    console.error('Error:', error.message);
  }
}

main();

TypeScript

typescript
import { KeyClaimClient } from '@keyclaim/sdk-typescript';

// Initialize client (base URL is automatically set to https://keyclaim.org)
const client = new KeyClaimClient({
  apiKey: 'kc_your_api_key_here',
  secret: 'your-secret-key' // Optional, defaults to API key
});

// Complete validation flow
async function main() {
  try {
    // Create challenge, generate response, and validate in one call
    const result = await client.validate('hmac', 30);
    
    if (result.valid) {
      console.log('✓ Validation successful!');
      console.log('Signature:', result.signature);
      console.log('Quota:', result.quota);
    } else {
      console.log('✗ Validation failed:', result.error);
    }
  } catch (error) {
    console.error('Error:', error.message);
  }
}

main();

Step-by-Step Usage with Official SDK

javascript
const { KeyClaimClient } = require('@keyclaim/sdk');

const client = new KeyClaimClient({
  apiKey: 'kc_your_api_key_here',
  secret: 'your-secret-key'
});

// Step 1: Create a challenge
const { challenge, expires_in } = await client.createChallenge({ ttl: 30 });
console.log('Challenge:', challenge);

// Step 2: Generate response (using HMAC)
const response = client.generateResponse(challenge, 'hmac');
console.log('Response:', response);

// Step 3: Validate the challenge-response pair
const result = await client.validateChallenge({
  challenge: challenge,
  response: response
});

if (result.valid) {
  console.log('✓ Challenge validated successfully!');
  if (result.signature) {
    console.log('Signature:', result.signature);
  }
  if (result.quota) {
    console.log('Quota:', result.quota.used, '/', result.quota.quota);
  }
} else {
  console.log('✗ Validation failed:', result.error);
}

Response Methods

The official SDK supports multiple response generation methods:

javascript
// Echo (testing only)
const echoResponse = client.generateResponse(challenge, 'echo');

// HMAC-SHA256 (recommended for production)
const hmacResponse = client.generateResponse(challenge, 'hmac');

// SHA256 Hash
const hashResponse = client.generateResponse(challenge, 'hash');

// Custom (with custom data)
const customResponse = client.generateResponse(challenge, 'custom', {
  userId: '123',
  timestamp: Date.now()
});

Error Handling

javascript
const { KeyClaimClient, KeyClaimError } = require('@keyclaim/sdk');

try {
  const result = await client.validate();
} catch (error) {
  if (error instanceof KeyClaimError) {
    console.error('KeyClaim Error:', error.message);
    console.error('Code:', error.code);
    console.error('Status:', error.statusCode);
  } else {
    console.error('Unknown error:', error);
  }
}

Package Links

  • Node.js SDK: [@keyclaim/sdk on npm](https://www.npmjs.com/package/@keyclaim/sdk)
  • TypeScript SDK: [@keyclaim/sdk-typescript on npm](https://www.npmjs.com/package/@keyclaim/sdk-typescript)

---

Manual Implementation (Alternative)

If you prefer to implement the API calls manually without the official SDK, you can use the following examples:

Installation

bash
npm install axios
# or
yarn add axios

Basic Usage

javascript
const axios = require('axios');

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 axios.post(`${API_BASE_URL}/challenge/create`, {
      key: API_KEY,
      ttl: 30  // Challenge expires in 30 seconds
    });
    
    return response.data.challenge;
  } catch (error) {
    console.error('Failed to create challenge:', error.response?.data || error.message);
    throw error;
  }
}

// Step 2: Generate response from challenge
// Option A: Simple echo (for testing)
function generateResponseSimple(challenge) {
  return challenge; // Echo the challenge back
}

// Option B: HMAC-based response (recommended for production)
const crypto = require('crypto');

function generateResponseHMAC(challenge, secret) {
  // Generate HMAC-SHA256 hash of the challenge
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(challenge);
  return hmac.digest('hex');
}

// Option C: Custom response generation
function generateResponseCustom(challenge, userData) {
  // Your custom logic here
  // Example: Combine challenge with user data
  const data = `${challenge}:${userData.userId}:${userData.timestamp}`;
  return crypto.createHash('sha256').update(data).digest('hex');
}

// Step 3: Validate the response
async function validateChallenge(challenge, response) {
  try {
    const result = await axios.post(`${API_BASE_URL}/challenge/validate`, {
      key: API_KEY,
      challenge: challenge,
      response: response
    });
    
    return result.data;
  } catch (error) {
    console.error('Validation failed:', error.response?.data || error.message);
    return { valid: false, error: error.response?.data?.error };
  }
}

// Complete example
async function main() {
  try {
    // Create challenge
    const challenge = await createChallenge();
    console.log('Challenge:', challenge);
    
    // Generate response (choose one method)
    const secret = 'your-secret-key'; // Use your own secret
    const response = generateResponseHMAC(challenge, secret);
    // Or use simple echo for testing:
    // const response = generateResponseSimple(challenge);
    
    console.log('Response:', response);
    
    // Validate
    const result = await validateChallenge(challenge, response);
    console.log('Validation result:', result);
    
    if (result.valid) {
      console.log('✓ Challenge validated successfully!');
      if (result.signature) {
        console.log('Signature:', result.signature);
      }
    } else {
      console.log('✗ Validation failed:', result.error);
    }
  } catch (error) {
    console.error('Error:', error);
  }
}

main();

Error Handling

javascript
async function createChallengeWithRetry(maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await createChallenge();
    } catch (error) {
      if (error.response?.status === 401) {
        throw new Error('Invalid API key');
      }
      if (error.response?.status === 403) {
        throw new Error('Rate limited or security check failed');
      }
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

Response Generation Methods

Method 1: Echo (Testing Only)

javascript
function generateResponseEcho(challenge) {
  return challenge;
}

Method 2: HMAC-SHA256 (Recommended)

javascript
const crypto = require('crypto');

function generateResponseHMAC(challenge, secret) {
  return crypto.createHmac('sha256', secret)
    .update(challenge)
    .digest('hex');
}

Method 3: SHA256 Hash

javascript
const crypto = require('crypto');

function generateResponseHash(challenge, salt) {
  return crypto.createHash('sha256')
    .update(challenge + salt)
    .digest('hex');
}

Method 4: Custom Algorithm

javascript
function generateResponseCustom(challenge, userId, timestamp) {
  const data = `${challenge}:${userId}:${timestamp}`;
  return crypto.createHash('sha256').update(data).digest('hex');
}

Complete SDK Class

javascript
const axios = require('axios');
const crypto = require('crypto');

class KeyClaimClient {
  constructor(apiKey, baseUrl = 'https://keyclaim.org/api', secret = null) {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
    this.secret = secret || apiKey; // Use API key as default secret
  }

  async createChallenge(ttl = 30) {
    const response = await axios.post(`${this.baseUrl}/challenge/create`, {
      key: this.apiKey,
      ttl: ttl
    });
    return response.data;
  }

  generateResponse(challenge, method = 'hmac') {
    switch (method) {
      case 'echo':
        return challenge;
      case 'hmac':
        return crypto.createHmac('sha256', this.secret)
          .update(challenge)
          .digest('hex');
      case 'hash':
        return crypto.createHash('sha256')
          .update(challenge + this.secret)
          .digest('hex');
      default:
        throw new Error(`Unknown method: ${method}`);
    }
  }

  async validate(challenge, response) {
    try {
      const result = await axios.post(`${this.baseUrl}/challenge/validate`, {
        key: this.apiKey,
        challenge: challenge,
        response: response
      });
      return result.data;
    } catch (error) {
      return {
        valid: false,
        error: error.response?.data?.error || error.message
      };
    }
  }

  async validateChallenge(challenge, method = 'hmac') {
    const response = this.generateResponse(challenge, method);
    return await this.validate(challenge, response);
  }
}

// Usage
const client = new KeyClaimClient('kc_your_api_key', 'https://keyclaim.org/api', 'your-secret');

async function example() {
  const { challenge } = await client.createChallenge();
  const result = await client.validateChallenge(challenge, 'hmac');
  console.log(result);
}