import { Controller, Get, Inject } from '@nestjs/common'; import type { JWK } from 'jose'; import { BFF_SIGNING_KEY, type BffSigningKey } from './bff-signing-key'; /** * `GET /.well-known/jwks.json` โ€” publishes the BFF's public key * material so downstream services can verify `X-User-Assertion` * JWTs minted by `SignedAssertionStrategy` per * [ADR-0014](../../../../docs/decisions/0014-downstream-api-access-obo-pattern.md) * ยง"Service strategy". * * v1 publishes a single key. When the rotation chantier ships, * `keys` will hold both the current and the previous public JWKs * so a downstream that cached the previous one keeps verifying * during the cut-over window. The shape is JWKS-canonical so * existing JOSE clients on the downstream side just point at the * URL and work. * * **Routing** โ€” the controller's `@Controller('.well-known/jwks.json')` * combined with `main.ts`'s `setGlobalPrefix('api', { exclude: * [/^\.well-known/] })` lands the route at the bare-root path * (`/.well-known/jwks.json`), which is where the well-known URI * convention places it (RFC 8615). * * **No auth / no CSRF.** Public by design โ€” the JWKS is the * downstream's verification anchor; gating it would defeat the * purpose. The double-submit CSRF middleware already exempts GET * methods so the route comes out clean. */ @Controller('.well-known/jwks.json') export class JwksController { constructor(@Inject(BFF_SIGNING_KEY) private readonly key: BffSigningKey) {} @Get() jwks(): { keys: readonly JWK[] } { return { keys: [this.key.publicJwk] }; } }