TypeScript

Installation

npm install @zourzouvillys/httpsig

Requires Node.js 20 or later. The package is ESM-only.

Quick Example: Sign a Request

import { signMessage, signatureInputHeader, signatureHeader } from '@zourzouvillys/httpsig';
import { newKeyPair } from '@zourzouvillys/httpsig';
import { component } from '@zourzouvillys/httpsig';
import * as crypto from 'node:crypto';

// Create a key pair (auto-detects algorithm from key type)
const cryptoKp = crypto.generateKeyPairSync('ed25519');
const kp = newKeyPair('my-key-id', cryptoKp.privateKey);
const key = kp.signingKey;

// Build the message representation
const url = new URL('https://example.com/api/resource');
const msg = {
  isRequest: true,
  method: 'POST',
  url,
  headerValues(name: string): string[] {
    if (name === 'content-type') return ['application/json'];
    return [];
  },
};

// Sign
const result = await signMessage(msg, 'sig1', {
  components: [
    component('@method'),
    component('@path'),
    component('@authority'),
    component('content-type'),
  ],
  keyId: 'my-key-id',
  created: Math.floor(Date.now() / 1000),
}, key);

console.log('Signature-Input:', signatureInputHeader(result));
console.log('Signature:', signatureHeader(result));

Note that all sign and verify operations are async to support Web Crypto API backends.

Quick Example: Verify a Signature

import { verifyMessage } from '@zourzouvillys/httpsig';
import { newVerifyingKey } from '@zourzouvillys/httpsig';
import { component } from '@zourzouvillys/httpsig';
import type { KeyProvider } from '@zourzouvillys/httpsig';

// Set up a KeyProvider (auto-detects algorithm from key type)
const provider: KeyProvider = async (keyId, algorithm) => {
  const pub = await loadPublicKey(keyId);
  if (!pub) throw new Error(`unknown key: ${keyId}`);
  return newVerifyingKey(keyId, pub);
};

// Verify (msg has Signature and Signature-Input headers)
const result = await verifyMessage(msg, provider, {
  requiredComponents: [
    component('@method'),
    component('@authority'),
  ],
  maxAgeMs: 5 * 60 * 1000, // 5 minutes
});

console.log(`Verified: label=${result.label}, keyId=${result.keyId}`);

HTTP Client Integrations

fetch

import { createSigningFetch } from '@zourzouvillys/httpsig/fetch';

const signedFetch = createSigningFetch({ key: myKey });
const response = await signedFetch('https://example.com/api', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ data: 'value' }),
});

axios

import axios from 'axios';
import { addSigningInterceptor } from '@zourzouvillys/httpsig/axios';

const client = axios.create({ baseURL: 'https://api.example.com' });
addSigningInterceptor(client, { key: myKey });

const response = await client.post('/resource', { data: 'value' });

undici

import { request } from 'undici';
import { createSigningRequest } from '@zourzouvillys/httpsig/undici';

const signedRequest = createSigningRequest(request, { key: myKey });
const { statusCode, body } = await signedRequest('https://example.com/api');

See the Integrations Guide for more details.

Web Crypto API

For browser or edge runtimes, use the Web Crypto adapters:

import { newWebCryptoSigningKey, newWebCryptoVerifyingKey } from '@zourzouvillys/httpsig';

const cryptoKey = await crypto.subtle.generateKey('Ed25519', true, ['sign', 'verify']);

const signingKey = newWebCryptoSigningKey('my-key', cryptoKey.privateKey, 'ed25519');
const verifyingKey = newWebCryptoVerifyingKey('my-key', cryptoKey.publicKey, 'ed25519');