Build the JWKS publishing Router. The router registers path as an
absolute path internally, so the effective endpoint is the router's
mount point + path. Mount at the application root (app.use(createRouter( express, keyStore))) for the common case. Prefix-mounting (e.g.
app.use("/auth", createRouter(...))) is valid only when the advertised
jwks_uri carries the same base path — typically because the issuer
identifier itself has that prefix (jwks_uri = ${issuer}${path}). If the
mount prefix and the issuer prefix disagree, discovery advertises a
jwks_uri that does not resolve. (The core jwksModule mounts at "/" and
relies on the issuer prefix to carry any base path.)
A successful response carries Cache-Control: public, max-age=N, where N is
cacheMaxAgeSeconds (JWKS is public data and the most-polled verifier
endpoint).
The route never publishes an empty key set (#282). A symmetric (HS256)
keystore answers 404 jwks_not_published; an asymmetric keystore that
yields no exportable public key answers 503 jwks_unavailable. Both carry
Cache-Control: no-store so the condition is not pinned in a shared cache
after the operator fixes it.
Direct callers bypass the config schema, so path and cacheMaxAgeSeconds
are validated here and the factory throws on misconfiguration (a non-
absolute path or a negative / non-integer cache age) — failing fast at
boot rather than registering an unexpected route or emitting an invalid
Cache-Control header.
Build the JWKS publishing Router. The router registers
pathas an absolute path internally, so the effective endpoint is the router's mount point +path. Mount at the application root (app.use(createRouter( express, keyStore))) for the common case. Prefix-mounting (e.g.app.use("/auth", createRouter(...))) is valid only when the advertisedjwks_uricarries the same base path — typically because the issuer identifier itself has that prefix (jwks_uri = ${issuer}${path}). If the mount prefix and the issuer prefix disagree, discovery advertises ajwks_urithat does not resolve. (The corejwksModulemounts at "/" and relies on the issuer prefix to carry any base path.)A successful response carries
Cache-Control: public, max-age=N, where N iscacheMaxAgeSeconds(JWKS is public data and the most-polled verifier endpoint).The route never publishes an empty key set (#282). A symmetric (HS256) keystore answers
404 jwks_not_published; an asymmetric keystore that yields no exportable public key answers503 jwks_unavailable. Both carryCache-Control: no-storeso the condition is not pinned in a shared cache after the operator fixes it.Direct callers bypass the config schema, so
pathandcacheMaxAgeSecondsare validated here and the factory throws on misconfiguration (a non- absolute path or a negative / non-integer cache age) — failing fast at boot rather than registering an unexpected route or emitting an invalidCache-Controlheader.