Summary
src/oauth-endpoints.js:24 advertises the wrong JWKS endpoint in the OAuth Authorization Server Metadata:
jwks_uri: `${ISSUER_BASE_URL}/.well-known/jwks`,
The issuer's own openid-configuration advertises /.well-known/jwks.json (note the .json), and the two paths serve different key sets:
| Endpoint |
Keys |
Algs |
/.well-known/jwks (what we advertise) |
3 |
RS256 only — stale |
/.well-known/jwks.json (issuer's real jwks_uri) |
9 |
RS256 + ES256 + EdDSA, rotated monthly — current |
Evidence
A freshly minted wallet access token (scope mcp) is signed with:
kid = 2026-06-06T22:58:52.309Z_ce9-47d-RS256
That kid is present only in /.well-known/jwks.json and is absent from /.well-known/jwks. So any consumer that follows our advertised jwks_uri to verify a current token's signature will fail with "no matching key".
Why this hasn't broken admin-mcp (yet)
src/jwt-validation.js decodes tokens without verifying the signature (jwt.decode, not jwt.verify) — it only checks iss/aud/exp/scope/sub/jti. So admin-mcp never actually fetches jwks_uri and never noticed the stale path. The bug is latent:
- Any RFC 8414 client / resource server that reads our
/.well-known/oauth-authorization-server metadata and verifies signatures will fail for all current tokens.
- admin-mcp itself cannot safely add signature verification until this is corrected.
Fix
- Point
jwks_uri at ${ISSUER_BASE_URL}/.well-known/jwks.json (minimum), or better, discover it dynamically from ${ISSUER_BASE_URL}/.well-known/openid-configuration.
- Whoever verifies should be alg-aware (issuer publishes RS256 + ES256 + EdDSA), not RS256-only.
Context
Found during the praca-cloud Phase 0 spike, which (unlike admin-mcp) must verify the wallet token signature because it mints real AAuth agent capability off it. Verifying against /.well-known/jwks.json succeeds (valid: true); against /.well-known/jwks it fails with "no JWKS key for kid …".
Summary
src/oauth-endpoints.js:24advertises the wrong JWKS endpoint in the OAuth Authorization Server Metadata:The issuer's own
openid-configurationadvertises/.well-known/jwks.json(note the.json), and the two paths serve different key sets:/.well-known/jwks(what we advertise)/.well-known/jwks.json(issuer's realjwks_uri)Evidence
A freshly minted wallet access token (scope
mcp) is signed with:That
kidis present only in/.well-known/jwks.jsonand is absent from/.well-known/jwks. So any consumer that follows our advertisedjwks_urito verify a current token's signature will fail with "no matching key".Why this hasn't broken admin-mcp (yet)
src/jwt-validation.jsdecodes tokens without verifying the signature (jwt.decode, notjwt.verify) — it only checks iss/aud/exp/scope/sub/jti. So admin-mcp never actually fetchesjwks_uriand never noticed the stale path. The bug is latent:/.well-known/oauth-authorization-servermetadata and verifies signatures will fail for all current tokens.Fix
jwks_uriat${ISSUER_BASE_URL}/.well-known/jwks.json(minimum), or better, discover it dynamically from${ISSUER_BASE_URL}/.well-known/openid-configuration.Context
Found during the praca-cloud Phase 0 spike, which (unlike admin-mcp) must verify the wallet token signature because it mints real AAuth agent capability off it. Verifying against
/.well-known/jwks.jsonsucceeds (valid: true); against/.well-known/jwksit fails with "no JWKS key for kid …".