Wire RFC 9728 resource_metadata discovery into Bearer 401 + observability#107
Merged
Conversation
…lity Bearer middleware 401s on /mcp now advertise the protected-resource metadata document on WWW-Authenticate via `resource_metadata=`, wiring the previously-dead `unauthorizedWithDiscovery` helper at src/oauth/handlers.ts:626 into all three failure sites: empty token, the four typed JWT errors (invalid signature, expired, aud mismatch, malformed), and the unknown-error fallthrough. The discovery endpoint already exists on this server, so no new endpoint work — this is pure substitution at the call sites. Adds three `console.warn` lines (`[oauth] bearer_failure reason=…`) matching the existing log shape (`[oauth] <event> ip=<ip>`), with the catch-block reason discriminated by `instanceof` so operators can tell signature drift from clock skew from aud-mismatch (RFC 8707) from a malformed token. The `unauthorized()` helper is preserved — `error="invalid_token"` moves from WWW-Authenticate to the JSON body, mirroring what `unauthorizedWithDiscovery` was always emitting. Updates the two pre-existing tests that asserted `error="invalid_token"` on the header. Spec: https://datatracker.ietf.org/doc/html/rfc9728
jpr5
added a commit
that referenced
this pull request
Jun 15, 2026
jpr5
added a commit
that referenced
this pull request
Jun 15, 2026
…8/6750 (#110) Updates three doc pages to reflect what shipped in v1.15.0 (PR #106) and v1.15.1 (PR #107): - `docs/deploy/index.html`: add `PATHFINDER_CONSENT_HMAC_KEY` to env-vars table, docker-compose, and systemd examples (Required in production, same shape as `MCP_JWT_SECRET`). - `docs/config/index.html`: document consent screen, redirect_uri policy, client cap (10k/100-per-IP), and security headers. - `docs/clients/index.html`: note the consent screen in the claude.ai flow + the new `WWW-Authenticate` `resource_metadata=` discovery hint. No behavioral change. Pages auto-deploy via `.github/workflows/deploy-pages.yml` on merge.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
resource_metadataonWWW-Authenticate(wires the deadunauthorizedWithDiscoveryhelper atsrc/oauth/handlers.ts:626, which had zero callers since landing).[oauth] bearer_failure reason=…log lines for observability —empty_token, the four typed JWT errors discriminated byinstanceof(invalid_signature/expired/aud_mismatch/malformed_token), and theunknownfallthrough..well-known/oauth-protected-resourcealready lives on this server, so no new endpoint work.Spec: https://datatracker.ietf.org/doc/html/rfc9728
Red test output
Pre-substitution, the new RFC 9728 cases fail because the current
unauthorized()emits noresource_metadata=:Post-substitution: 4 passed, 0 failed, full repo suite green (
Test Files 334 passed (334) | Tests 6248 passed (6248)).Test plan
RFC 9728 resource_metadata discovery on 401describe block inoauth-handlers.test.tsassertsWWW-Authenticate: Bearer resource_metadata="…/.well-known/oauth-protected-resource"on each Bearer failure mode (empty / signature / expired / aud).bearerMiddleware > expired tokenandoauth-e2e > garbage tokenupdated to the new contract (error="invalid_token"moved from header to JSON body, mirroring whatunauthorizedWithDiscoverywas always emitting).npm testgreen locally + in CI.Risk
None — discovery endpoint already live; substitution preserves 401 status code;
unauthorized()helper retained (other callers may exist); no new infra. The header contract narrows fromerror="invalid_token"(RFC 6750 optional) toresource_metadata=…(RFC 9728 required for discovery) — bodies are unchanged.