import type { JWK } from 'jose'; import { exportJWK } from 'jose'; import { createPublicKey } from 'node:crypto'; import type { JwksConfig } from '../config/check-jwks-config'; /** * Resolved BFF signing identity — the parsed private key, the * derived public JWK (ready to ship in `/.well-known/jwks.json`), * the `kid`, and the JOSE algorithm. Computed once at boot from * `assertJwksConfig()` so neither the strategy nor the JWKS * controller has to re-derive on the hot path. * * The DI token {@link BFF_SIGNING_KEY} wires this into Nest's * provider graph. Both the `SignedAssertionStrategy` (signing) and * the JWKS controller (publishing) read from this same instance — * one source of truth for the BFF's signing material. */ export interface BffSigningKey { readonly config: JwksConfig; /** Public JWK shape, derived from the private key. Includes `kid`, `alg`, `use=sig`. */ readonly publicJwk: JWK; } export const BFF_SIGNING_KEY = 'BFF_SIGNING_KEY'; /** * Builds the {@link BffSigningKey} from the validated env config. * Async because `jose.exportJWK` is async on Node — it operates on * Web Crypto's KeyObject under the hood. */ export async function buildBffSigningKey(config: JwksConfig): Promise { // `exportJWK` accepts either a public KeyObject or a CryptoKey. // We have the private one — derive the public first so the // exported JWK has no private material. const publicKey = createPublicKey(config.privateKey); const baseJwk = await exportJWK(publicKey); const publicJwk: JWK = { ...baseJwk, kid: config.kid, alg: config.alg, use: 'sig', }; return { config, publicJwk }; }