first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
# `KeyResolver`
## Purpose
Caller-supplied key-resolution function consulted when `header.kid` is present and no key has been supplied via `opts.key`, `header.jwk`, or `header.x5c`. The resolver receives the parsed header so dispatch can branch on additional parameters (`iss`, custom tenant headers, environment-specific kid namespaces). Returning `undefined` surfaces as `JwsError(JWS.KEY_NOT_FOUND)`; throwing propagates verbatim so callers can map their own error taxonomy onto JWS verification.
## Features
- Async or sync — both signatures are accepted.
- Receives `(kid, header)`; the header is the parsed `JoseHeader`, including unknown parameters.
- `undefined` / `null` return → typed `JWS.KEY_NOT_FOUND` thrown by the pipeline.
## Usage: Basic Example
```ts
import { compactVerify, type KeyResolver } from "@suaveplan/jws";
const keyResolver: KeyResolver = async (kid) => keyStore.get(kid);
await compactVerify(token, undefined, {
algorithms: ["RS256"],
keyResolver,
});
```
## Usage: Advanced Example
```ts
import { compactVerify, JwsError, type KeyResolver } from "@suaveplan/jws";
// Dispatch on a custom tenant header:
const keyResolver: KeyResolver = async (kid, header) => {
const tenant = header["tenant"] as string | undefined;
if (tenant === "acme") return acmeKeyStore.get(kid);
if (tenant === "globex") return globexKeyStore.get(kid);
return undefined; // → KEY_NOT_FOUND
};
try {
await compactVerify(token, undefined, {
algorithms: ["RS256"],
keyResolver,
});
} catch (err) {
if (err instanceof JwsError && err.code === "JWS.KEY_NOT_FOUND") {
// Inspect err.context.kid for the unresolved id.
}
}
```
## API Reference
- `type KeyResolver = (kid: string, header: JoseHeader) => Promise<unknown> | unknown`
Returning anything other than `undefined` / `null` is treated as the verification key and passed to the algorithm impl unchanged. The pipeline performs no type-check on the returned value — it relies on the algorithm impl to throw `JWS.KEY_ALG_MISMATCH` if the resolved key doesn't match the declared `alg`.
## Implementation Notes
Resolution priority in the verify pipeline is `opts.key → header.jwk → header.x5c → opts.keyResolver(header.kid)`. The resolver only runs when none of the earlier sources resolved a key; this means a token carrying both `kid` AND `jwk` will route through `jwk` first (assuming `allowEmbeddedJwk: true`). That ordering is deliberate: when a producer self-describes a key in the token, the consumer's stored key for the same `kid` should be re-verified against the embedded one — but doing that match is the consumer's responsibility, not this library's. The pipeline simply uses whichever source the caller has opted in to first.