Repository navigation
Add RFC 6750 error to the /mcp 401 WWW-Authenticate header - #30
Merged
Merged
Conversation
When a bearer token is presented but invalid/expired, include error="invalid_token" (+ error_description) in the WWW-Authenticate challenge, in addition to the RFC 9728 resource_metadata pointer, so MCP clients can distinguish "expired -> re-authorize" from "no token" and prompt an inline reconnect rather than sending the user to settings. A request with no token still gets a bare challenge (RFC 6750: omit the error code when no credentials were sent). The header is already exposed cross-origin via the /mcp CORS layer, so browser-based clients can read it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014T9N8USDfNK5yzznPGg7Ym
There was a problem hiding this comment.
Pull request overview
This PR updates the /mcp bearer-token gate to include an RFC 6750 error="invalid_token" (plus error_description) in the WWW-Authenticate challenge only when a bearer token was presented but is invalid/expired, while keeping the bare RFC 9728 resource_metadata challenge for requests with no token. This helps MCP clients distinguish “token expired → re-authorize” from “no credentials provided”.
Changes:
- Track whether a token was presented and branch the
WWW-Authenticateheader accordingly. - Add
error="invalid_token"and anerror_descriptionto theWWW-Authenticateheader for invalid/expired tokens. - Preserve the existing JSON body (
{"error":"invalid_token"}) and discovery pointer (resource_metadata=...).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address PR #30 review: - Parse the Authorization scheme case-insensitively (RFC 7235 §2.1), so `Authorization: bearer <token>` is recognized as a token (previously a case-sensitive strip_prefix("Bearer ") treated it as no token, which also mis-set had_token and dropped the error="invalid_token" challenge). - Extract the WWW-Authenticate challenge into a pure `bearer_challenge` helper and unit-test both branches (bare vs error="invalid_token"), to lock in the RFC 6750/9728 header formatting. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014T9N8USDfNK5yzznPGg7Ym
Comment on lines
+890
to
+893
| .and_then(|h| { | ||
| let (scheme, rest) = h.split_once(' ')?; | ||
| scheme.eq_ignore_ascii_case("Bearer").then(|| rest.trim().to_owned()) | ||
| }); |
|
|
||
| let no_token = super::bearer_challenge(false); | ||
| assert!(no_token.starts_with("Bearer ")); | ||
| assert!(!no_token.contains("error="), "a bare challenge must omit the error code: {no_token}"); |
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.
What & why
Helps MCP clients trigger re-authorization inline (a reconnect prompt in the chat) instead of making the user dig into connector settings when their token expires.
The
/mcp401 already carried the RFC 9728 discovery pointer (WWW-Authenticate: Bearer resource_metadata="…"), and #29 made it readable cross-origin. The one missing piece was the RFC 6750 §3errorfield in the header — clients that key their auto-reprompt off the challenge string couldn't tell "token expired → re-authorize" from "no token / other."Change
In
require_token, when a bearer token was presented but is invalid/expired, the challenge now includes the error code:A request with no token still gets a bare challenge (RFC 6750: omit the error code when no credentials were sent):
The
{"error":"invalid_token"}JSON body is unchanged. No new dependencies; ~a few lines inrequire_token.Testing
cargo buildclean (one pre-existing macro warning).POST /mcpwith noAuthorization→WWW-Authenticate: Bearer resource_metadata="…"(no error field).POST /mcpwith an invalid bearer token →WWW-Authenticate: Bearer error="invalid_token", error_description="The access token is invalid or expired", resource_metadata="…"./mcpCORS headers, incl.access-control-expose-headers: www-authenticate,mcp-session-id, so browser clients can read the challenge.Notes
mainafter Add CORS to /mcp so browser-based MCP connectors can probe it #29's squash-merge; fresh PR.🤖 Generated with Claude Code
https://claude.ai/code/session_014T9N8USDfNK5yzznPGg7Ym
Generated by Claude Code