Files
skills/docs/api-stack/api/api-auth-bearer-static/src/mtls/mtls-gate.md
2026-05-29 08:20:56 +01:00

2.8 KiB

mTLS gate — @suaveplan/api-auth-bearer-static

(no submodules)

Purpose

The mTLS gate enforces an optional second factor on individual token table entries. When operators mark an entry with mtlsRequired: true, the resolver MUST refuse to surface the entry's Principal unless the host listener attached a verified peer certificate to the inbound request via request.tls.peerCertificate. This guards the highest-privilege service tokens (e.g. internal admin API) against credential leakage — the bearer token alone is necessary but no longer sufficient.

The adapter does NOT establish TLS itself. The host listener (Bun's TLS-enabled server, Node's https, a Cloudflare-Worker TLS terminator ahead of the worker, etc.) is responsible for completing the TLS handshake and surfacing the verified subject as request.tls.peerCertificate. This module just inspects what was attached.

Features

  • Pure function — no I/O, no state.
  • Defensive — accepts any Request-shaped input even when the tls field was not attached (returns undefined).
  • Treats peerCertificate: null identically to absent.
  • mtlsRequired === false short-circuits without inspecting the request.

Usage: Basic

import { checkMtlsGate } from "@suaveplan/api-auth-bearer-static/mtls";

const request = new Request("https://example/test");
const allowed = checkMtlsGate(request, true);
// allowed === false — no peerCertificate attached

Usage: Advanced

import {
    checkMtlsGate,
    extractPeerCertificate,
} from "@suaveplan/api-auth-bearer-static/mtls";

// Host listener pattern — wrap the inbound request before handing off to
// the auth chain.
const withTls = Object.assign(request, {
    tls: { peerCertificate: parsedX509Subject },
});

const cert = extractPeerCertificate(withTls);
// cert === parsedX509Subject

const allowed = checkMtlsGate(withTls, true);
// allowed === true

API Reference

  • interface RequestTlsInfo { peerCertificate?: unknown }
  • extractPeerCertificate(request: Request): unknown | undefined
  • checkMtlsGate(request: Request, mtlsRequired: boolean): boolean

Implementation Notes

The Request Web type has no standardised tls field. Both Node's undici-based Request and Bun's polyfill ignore unknown properties on construction, so the host pattern is to attach tls as a plain property after the Request is built (or to wrap it in a Proxy / subclass). The extractor reads through that property with a single defensive type cast and returns undefined for any shape that does not satisfy the { peerCertificate: <truthy> } invariant. The opaque certificate itself is not parsed here — that is the host's job, and a future api-auth-mtls adapter (out of scope for Stage 4) will own subject extraction.