2.9 KiB
2.9 KiB
Attestation format registry
Purpose
Defines the pluggable contract every per-format attestation verifier implements (AttestationFormatVerifier), the shared input bundle (AttestationVerifierInput), and the canonical result shape (AttestationVerifyResult). Consumers can registerAttestationFormat(registry, name, verifier) to add a new IANA-registered format (e.g. apple-appattest) or override a built-in without forking the package.
Features
- Three-type public contract:
AttestationType(basic | self | attCA | anonCA | none),AttestationVerifyResult({ trusted, attestationType, trustPath }),AttestationFormatVerifier((input) => Result | Promise<Result>). AttestationFormatRegistry = Map<string, AttestationFormatVerifier>— opaque mutable handle the dispatcher consumes.- Last-write-wins registration so
registerAttestationFormat(registry, "packed", custom)cleanly replaces the built-in. - Allows synchronous or async verifiers —
noneis synchronous,tpmis async, callers do not need to know the difference.
Usage: Basic Example
import {
defaultAttestationRegistry,
registerAttestationFormat,
} from "@suaveplan/webauthn/verifier";
const registry = defaultAttestationRegistry();
registerAttestationFormat(registry, "apple-appattest", customVerifier);
Usage: Advanced Example
import {
type AttestationFormatVerifier,
registerAttestationFormat,
} from "@suaveplan/webauthn/verifier";
const counted: AttestationFormatVerifier = async (input) => {
metrics.count(input.attestationObject.fmt);
return builtin(input);
};
registerAttestationFormat(registry, "packed", counted);
API Reference
AttestationType— literal union.AttestationVerifyResult—{ trusted: boolean, attestationType: AttestationType, trustPath: readonly Uint8Array[] }.AttestationVerifierInput—{ attestationObject, clientDataHash, expectedRpIdHash }.AttestationFormatVerifier—(input) => Result | Promise<Result>.AttestationFormatRegistry—Map<string, AttestationFormatVerifier>.registerAttestationFormat(registry, name, verifier)— register or override.
Implementation Notes
trustPathis the leaf-first X.509 chain as raw DER byte strings. Empty when the format does not provide a chain (self,none). Consumers feed the chain into their own metadata-trust source (FIDO MDS3, attestation-CA allow-list) — the verifier is intentionally agnostic about which roots to trust.- The registry is a plain
Maprather than a custom class so consumers can iterate / inspect / serialise it freely. The contract is the value types, not the container. - Returning a discriminated
attestationTyperather than a boolean lets the caller adjust policy:basicandattCAare stronger thanself, andnoneis the weakest (but still acceptable when the RP's policy permits anonymous registration).