fix: canonical resource URI + log rejected DCR redirect_uris for OAuth#43
Merged
Conversation
When a Dynamic Client Registration request is rejected because the client's redirect_uri isn't in OAUTH_ALLOWED_REDIRECT_URIS, the server returned a bare 400 with no record of what was actually requested. This is the common cause of Claude.ai web / Cowork connections failing right after discovery succeeds. Log a dcr_redirect_uri_rejected warning with the requested URIs and the active allowlist so the exact value can be added to the env var. Document the symptom and remedy in the user guide. https://claude.ai/code/session_01U3EtN3puoZRq2t7nedcnHY
There was a problem hiding this comment.
Pull request overview
Adds diagnostic visibility for OAuth Dynamic Client Registration failures when clients submit redirect URIs outside the configured allowlist, and documents the troubleshooting path for operators.
Changes:
- Adds structured warning logging for rejected
/oauth/registerredirect URIs. - Includes requested, rejected, and allowed redirect URI lists in the diagnostic log.
- Updates the User Guide troubleshooting table with the new log event and remediation.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
app/oauth.py |
Logs rejected DCR redirect URI attempts before returning 400. |
docs/USER_GUIDE.md |
Documents the new DCR rejection symptom and fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The protected-resource metadata advertised the resource as
"<base>/mcp/" (trailing slash), but MCP clients (Claude.ai web / Cowork)
canonicalize the resource to the no-slash form "<base>/mcp" per the MCP
authorization spec ("implementations should consistently omit [trailing
slashes]") and send that as the RFC 8707 resource indicator. The
mismatch caused Claude to reject authorization after a token was already
issued, even though authenticated /mcp/ calls succeeded.
Advertise "<base>/mcp" so the advertised resource matches the client's
canonical form.
https://claude.ai/code/session_01U3EtN3puoZRq2t7nedcnHY
This was referenced May 23, 2026
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.
Two fixes for OAuth MCP connections (Claude.ai web / Cowork), found while debugging a live deployment with container logs.
1. Advertise the canonical resource URI without a trailing slash (the connection blocker)
After the redirect-URI allowlist was corrected, the full OAuth flow succeeded in the logs —
register → 201,authorize → 302,token → 200, and authenticatedPOST /mcp/ → 200calls processingListToolsRequest— yet Claude still reported "Authorization with the MCP server failed."Root cause: our protected-resource metadata advertised
resource: https://<host>/mcp/(trailing slash), but Claude canonicalizes the resource to the no-slash form and sendsresource=https://<host>/mcp(visible in the/oauth/authorizequery in the logs). The MCP authorization spec is explicit:So Claude's post-token resource-identity check failed on the mismatch. Fix: advertise
https://<host>/mcp(no slash), matching the client's canonical form. The token already works against/mcp/(proven by the 200s), so this is purely the advertised identifier.2. Log rejected DCR redirect_uris (diagnostic)
POST /oauth/registerreturned a bare400with no record of the rejectedredirect_uri, which made the earlier "callback not allowlisted" failure hard to diagnose. Now logs adcr_redirect_uri_rejectedwarning with therequestedURIs and the activeallowedlist.Verification
ruffclean. Metadata tests updated to assert the no-slash canonical resource.https://claude.ai/code/session_01U3EtN3puoZRq2t7nedcnHY