3.2 KiB
jwks-endpoint
(no submodules)
Purpose
Provides a framework-agnostic JWKS endpoint backed by a TenantKeyStore. The endpoint serialises active public keys from the store into a { keys: JWK[] } JSON Web Key Set and exposes an HTTP response builder that any framework can consume. An internal cache reduces key-store reads on high-traffic endpoints, with the TTL controlled by cacheMaxAge.
Features
JWKSEndpointclass with configurable cache TTL and injectable clockgetJWKS(config?)— fetches active public keys and returns aJWKSobjecthandleRequest(request)— returns a framework-agnostic{ status, body, headers }responseclearCache()— invalidates the internal JWKS cache on demandformatJWKSResponse(keys)— standalone pure function to wrap JWK arrayscreateJWKSEndpoint(options)— factory functionincludeExpiredoption to bypass cache and include all keys
Usage: Basic Example
import { InMemoryKeyStore } from "@suaveplan/jwt-tenant/jwks/in-memory-key-store";
import { JWKSEndpoint } from "@suaveplan/jwt-tenant/jwks/jwks-endpoint";
import { generateKeyPair } from "@suaveplan/jwt-tenant/keys/key-generator";
const store = new InMemoryKeyStore();
const kp = await generateKeyPair("EdDSA");
await store.addKey(kp);
const endpoint = new JWKSEndpoint({ keyStore: store });
const response = await endpoint.handleRequest(null);
// response.status === 200
// JSON.parse(response.body).keys.length === 1
Usage: Advanced Example
import { JWKSEndpoint } from "@suaveplan/jwt-tenant/jwks/jwks-endpoint";
// Use in a Hono or Express route
app.get("/.well-known/jwks.json", async (req, res) => {
const response = await endpoint.handleRequest(req);
res.status(response.status)
.set(response.headers)
.send(response.body);
});
// Force cache invalidation after key rotation
await store.addKey(newKeyPair);
endpoint.clearCache();
API Reference
JWKSEndpoint
Constructor: new JWKSEndpoint(options: JWKSEndpointOptions).
options.keyStore—TenantKeyStoreimplementationoptions.cacheMaxAge— cache TTL in milliseconds (default 5 min)options.clock— injectable clock for cache expiry
Methods
getJWKS(config?): Promise<JWKS>— Returns active public keys as a JWKS. Caches result unlessconfig.includeExpiredistrue.handleRequest(request): Promise<JWKSResponse>— Returns{ status: 200, body: string, headers }. Therequestparameter is accepted for interface compatibility.clearCache(): void— Resets the internal cache immediately.
formatJWKSResponse(keys: JWK[]): JWKS
Pure function that wraps a JWK[] in a { keys } envelope.
createJWKSEndpoint(options): JWKSEndpoint
Factory function; equivalent to new JWKSEndpoint(options).
Implementation Notes
The cache uses a simple timestamp check against clock(). Cache invalidation on key rotation must be explicit (clearCache()). The handleRequest _request parameter is intentionally ignored — the endpoint serves the same JWKS for all requests. In production, mount the endpoint behind a CDN with short TTLs for automatic key rotation propagation.