# fetch-message-signatures

`fetch-message-signatures` is a JavaScript module for HTTP Message Signatures ([RFC 9421][]) built
on the Fetch API.

The module provides sender, recipient, and `Accept-Signature` operations on top of `Request`,
`Response`, `Headers`, and `fetch`, together with Web Cryptography implementations for ECDSA P-256,
ECDSA P-384, Ed25519, RSA-PSS with SHA-512, and RSASSA-PKCS1-v1_5 with SHA-256. HMAC and other
cryptography can be supplied through custom providers. Trusted-key selection and authorization
remain application responsibilities.

RFC 9421 is built on Structured Fields ([RFC 9651][]), and the parser and serializer behind it are
exported for HTTP fields this module does not define.

## [💗 Help the project](https://github.com/sponsors/panva)

Support from the community to continue maintaining and improving this module is welcome. If you find
this module useful, please consider supporting this project by
[becoming a sponsor](https://github.com/sponsors/panva).

## Dependencies: 0

`fetch-message-signatures` has no dependencies and it exports tree-shakeable ESM from a single
module.

## [API Reference](docs/README.md)

`fetch-message-signatures` is distributed via
[npmjs.com](https://www.npmjs.com/package/fetch-message-signatures),
[jsdelivr.com](https://www.jsdelivr.com/package/npm/fetch-message-signatures), and
[github.com](https://github.com/panva/fetch-message-signatures).

## Quick Start

Sign every outgoing request and send it. `createSigningFetch()` returns a drop-in `fetch`, so the
signing happens around the call you already make.

```ts
import * as FetchSig from 'fetch-message-signatures'

// 1. Your signing key
const { privateKey } = await FetchSig.generateEd25519KeyPair()

// 2. Wrap fetch
const signingFetch = FetchSig.createSigningFetch({
  sign: {
    signer: FetchSig.ed25519Signer(privateKey),
    components: ['@method', '@authority', '@path'],
    parameters: { alg: 'ed25519', keyid: 'client-key' },
  },
})

// 3. Send it, exactly like fetch. The request goes out signed.
const response = await signingFetch('https://api.example/orders/123')
const order = await response.json()
```

The request that went out carries the two fields RFC 9421 defines:

```text
signature-input: sig1=("@method" "@authority" "@path");created=1735689600;alg="ed25519";keyid="client-key"
signature:       sig1=:<base64 of the 64 Ed25519 signature bytes>:
```

The recipient checks that signature against explicit policy. A valid signature on its own is not
enough: the components it covers and the parameters it carries have to be the ones the application
requires, which is why the policy is spelled out rather than defaulted.

```ts
declare const incoming: Request
declare const clientPublicKey: CryptoKey

const verified = await FetchSig.verify(incoming, {
  verifier: FetchSig.ed25519Verifier(clientPublicKey),
  policy: {
    requiredComponents: ['@method', '@authority', '@path'],
    requiredParameters: ['created', 'keyid'],
    algorithms: ['ed25519'],
    maxAge: 60,
  },
})

// sig1 ed25519
console.log(verified.label, verified.algorithm)
```

A recipient that accepts more than one client selects the trusted key itself, from the signature's
`keyid`. That is what [`VerifierFactory`](docs/type-aliases/VerifierFactory.md) is for, and the
[recipient guide](guides/recipient.md) covers it.

Signing requests is the common direction, because it only asks the recipient to verify. When the
server signs its responses too, [`createSignedFetch()`](docs/functions/createSignedFetch.md) adds
verification on the way back in, and
[`createVerifyingFetch()`](docs/functions/createVerifyingFetch.md) does that alone so a bundler can
omit the signing side. To sign a message you already hold, rather than wrapping `fetch`, use
[`sign()`](docs/functions/sign.md).

The bytes that were signed are one canonicalized line per covered component plus the
`@signature-params` line, which repeats the `Signature-Input` member value. `createSignatureBase()`
returns exactly that string, which is the first thing to compare when two implementations disagree:

```text
"@method": GET
"@authority": api.example
"@path": /orders/123
"@signature-params": ("@method" "@authority" "@path");created=1735689600;alg="ed25519";keyid="client-key"
```

## [Guides](guides/README.md)

For sender and recipient integration, cryptographic providers, component selection, Structured
Fields, `Accept-Signature`, Fetch behavior, and security guidance, see the
[guides directory](guides/README.md).

## Runtime Requirements

The Fetch wrappers, `sign()`, `appendSignature()`, `signRequested()`, and `appendAcceptSignature()`
require standards-compatible `Request`, `Response`, and `Headers` implementations, and the wrappers
also require `fetch` itself. Every reading operation, including `createSignature()` and `verify()`,
additionally accepts a plain object carrying `method`, `url`, and `headers` for a request, or
`status` and `headers` for a response, so a server that never constructs a `Request` can sign and
verify by attaching the returned field values itself. A descriptor field value can be an array of
occurrences in wire order, and an optional `trailers` record supplies trailer occurrences. The
built-in cryptographic providers require the Web Cryptography API and runtime support for the
selected algorithm. The package does not provide polyfills.

Signing and verification capture the target message and related request into package-owned,
immutable snapshots. A verifier factory and `policy.validate` receive the same `MessageSnapshot`,
whose lowercase header and trailer names map to frozen occurrence arrays, rather than the caller's
mutable message object. Keep the source message stable until the operation settles: it is compared
with the snapshot after application callbacks run, and a change rejects the operation.

Package configuration records must be object literals or null-prototype objects whose own members
are enumerable data properties. Frozen records are supported; class instances, inherited or
non-enumerable configuration, accessors, and Proxies are not. Fetch-wrapper `RequestInit` values
follow the same rule. This restriction does not apply to message and header inputs, cryptographic
provider implementations, or host objects such as `CryptoKey`, `Request`, `Response`, and `Headers`.

Structured Field Byte Sequences use `Uint8Array.prototype.toBase64()` and `Uint8Array.fromBase64()`
where they are available, and fall back to `btoa()` and `atob()` where they are not. Both paths
produce the same results and are covered by the test suite.

Runtime-specific behavior that affects signatures, such as manual redirect handling in browsers,
repeated field lines, trailers, and response reconstruction, is documented in
[Fetch behavior and limitations](guides/fetch.md).

## Supported Operations

- Sign requests and responses.
- Verify one or more message signatures against explicit recipient policy.
- Generate key pairs containing Web Cryptography's `CryptoKey` objects and providers for ECDSA
  P-256, ECDSA P-384, Ed25519, RSA-PSS with SHA-512, and RSASSA-PKCS1-v1_5 with SHA-256.
- Bind a response signature to components of its related request.
- Create, parse, append, and fulfill `Accept-Signature` requests.
- Derive RFC 9421 request and response components from Fetch messages or plain descriptors.
- Process HTTP fields as Structured Fields, dictionary members, raw byte sequences, or trailers.
- Parse and serialize Structured Field Dictionaries, Lists, and Items.
- Wrap `fetch` with request signing, response verification, or both through independent
  tree-shakeable exports.

## Cryptographic Algorithms

`fetch-message-signatures` includes tree-shakeable key-pair generators, signer factories, and
verifier factories backed by Web Cryptography for `ecdsa-p256-sha256`, `ecdsa-p384-sha384`,
`ed25519`, `rsa-pss-sha512`, and `rsa-v1_5-sha256`. Other algorithm identifiers and key systems,
including `hmac-sha256`, can be supplied through the [`Signer`](docs/interfaces/Signer.md) and
[`Verifier`](docs/interfaces/Verifier.md) interfaces. Applications choose trusted keys, algorithms,
and authorization policy.

The [cryptographic providers guide](guides/cryptography.md) documents the exact algorithm mappings,
key extractability, signature parameter handling, and custom provider contract. Exported functions
are listed under [Cryptographic Algorithms](docs/README.md#cryptographic-algorithms) in the API
reference.

## Security Considerations

A valid signature authenticates only its covered components. It does not by itself establish
authorization, freshness, replay protection, or body integrity. Applications must define trusted
keys, required component coverage, timestamp and nonce policy, and independently validate
`Content-Digest` when body integrity matters.

Fetch also hides or normalizes some protocol-layer HTTP details. Read the
[security guidance](guides/security.md) and [Fetch behavior](guides/fetch.md) before deploying
signatures across a network boundary. Security vulnerabilities should be reported according to the
[Security Policy].

## Specifications

- [HTTP Message Signatures (RFC 9421)][RFC 9421]
- [Structured Field Values for HTTP (RFC 9651)][RFC 9651]

[RFC 9421]: https://www.rfc-editor.org/info/rfc9421/
[RFC 9651]: https://www.rfc-editor.org/info/rfc9651/
[Security Policy]: https://github.com/panva/fetch-message-signatures/security/policy
