npm package: @flarcos/http-message-signatures
Description: RFC 9421 HTTP Message Signatures and RFC 9530 Content-Digest for Node.js — sign, verify, and digest HTTP requests with Ed25519
Author: flarcos
Latest version: 0.1.0

# @flarcos/http-message-signatures

RFC 9421 HTTP Message Signatures and RFC 9530 Content-Digest for Node.js.

Sign and verify HTTP requests with Ed25519 keys. Built for [GNAP (RFC 9635)](https://www.rfc-editor.org/rfc/rfc9635.html) and [Open Payments](https://openpayments.dev), but works with any HTTP API that requires message signatures.

## Install

```bash
npm install @flarcos/http-message-signatures
```

## Usage

### Sign a request

```typescript
import { signRequest, computeContentDigest, loadKey } from '@flarcos/http-message-signatures';

const key = loadKey(`-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEI...
-----END PRIVATE KEY-----`);

// For requests with a body, compute Content-Digest first
const body = JSON.stringify({ amount: '100', currency: 'EUR' });
const digest = computeContentDigest(body);

const result = await signRequest({
  key,
  keyId: 'my-key-id',
  method: 'POST',
  url: 'https://api.example.com/payments',
  headers: {
    'Content-Type': 'application/json',
    'Content-Digest': digest,
    'Content-Length': String(Buffer.byteLength(body)),
    'Authorization': 'GNAP my-access-token',
  },
});

// result.headers contains all headers including Signature and Signature-Input
const response = await fetch('https://api.example.com/payments', {
  method: 'POST',
  headers: result.headers,
  body,
});
```

### Verify Content-Digest

```typescript
import { verifyContentDigest } from '@flarcos/http-message-signatures';

const isValid = verifyContentDigest(responseBody, response.headers['content-digest']);
```

### Key utilities

```typescript
import { loadKey, exportPublicJwk, jwkThumbprint } from '@flarcos/http-message-signatures';

const key = loadKey(pemString);
const jwk = exportPublicJwk(key);
const thumbprint = jwkThumbprint(jwk); // Use as keyId
```

## API

### `signRequest(params)`

Signs an HTTP request per RFC 9421. Automatically determines covered components based on headers present:

| Header present | Components covered |
|---|---|
| *(always)* | `@method`, `@target-uri` |
| `Authorization` | `authorization` |
| `Content-Digest` | `content-digest` |
| `Content-Length` | `content-length` |
| `Content-Type` | `content-type` |

### `computeContentDigest(body, algorithm?)`

Computes RFC 9530 Content-Digest. Default algorithm: `sha-256`.

### `verifyContentDigest(body, headerValue)`

Verifies a Content-Digest header against a body. Returns `boolean`.

### `loadKey(pem)`

Loads an Ed25519 private key from PEM string.

### `exportPublicJwk(privateKey)`

Exports the public key as JWK.

### `jwkThumbprint(jwk)`

Computes the JWK Thumbprint (RFC 7638) — useful as a `keyId`.

## Standards

- [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421.html) — HTTP Message Signatures
- [RFC 9530](https://www.rfc-editor.org/rfc/rfc9530.html) — Digest Fields (Content-Digest)
- [RFC 7638](https://www.rfc-editor.org/rfc/rfc7638.html) — JWK Thumbprint
- [RFC 9635](https://www.rfc-editor.org/rfc/rfc9635.html) — GNAP (uses HTTP Message Signatures)

## Related

- [@flarcos/kiota-authentication-gnap](https://github.com/flarcos/kiota-authentication-gnap) — GNAP auth provider (uses this package)
- [@flarcos/kiota-arazzo](https://github.com/flarcos/arazzo-sdk) — Arazzo workflow SDK

## License

Apache-2.0