fix(auth): accept RFC 8252 §7.1 private-use redirect URI schemes - #12
Open
DUC750 wants to merge 1 commit into
Open
fix(auth): accept RFC 8252 §7.1 private-use redirect URI schemes#12DUC750 wants to merge 1 commit into
DUC750 wants to merge 1 commit into
Conversation
parseClientRedirectUri (and, by delegation, the auto-registration isValidRedirectUri) accepted only https: and http-loopback redirect URIs. Native OAuth clients that use a private-use URI scheme (RFC 8252 §7.1) — e.g. Claude's `claude://claude.ai/mcp-auth-callback/sdk`, VS Code's `vscode://…`, Cursor's `cursor://…` — were rejected with HTTP 400 "Invalid redirect URI" before credential entry, making the OAuth login unusable for them. Accept private-use URI schemes in addition to https/http-loopback, while keeping a denylist for schemes that can execute script or read local resources (javascript:, data:, vbscript:, file:, blob:). The authoritative open-redirect guard remains the SDK authorize handler's exact-match against the client's registered redirect_uris, so widening the scheme allowlist to the schemes RFC 8252 sanctions does not weaken that guarantee. Consolidate the duplicated validation: isValidRedirectUri now delegates to parseClientRedirectUri so both paths share one definition. Add the repository's first vitest suite covering the accept/reject matrix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Problem
parseClientRedirectUri(src/server/auth/oauth/http.ts) — and, by delegation, the auto-registrationisValidRedirectUri(src/server/http/express-app.ts) — accept onlyhttps:andhttp:-loopback redirect URIs.Native OAuth clients that use a private-use URI scheme as sanctioned by RFC 8252 §7.1 are therefore rejected. Concretely, the current Claude client registers and authorizes with:
The client completes Dynamic Client Registration fine (
POST /register→ 201), the sign-in page renders, the user enters valid credentials — andPOST /authorize/localthen returns400 Invalid redirect URIbecauseparseClientRedirectUrireturnsnullfor theclaude:scheme. The login is unusable for such clients. The same applies tovscode://…,cursor://…, and reverse-domain schemes likecom.example.app:/cb.Reproduction (holding everything but
redirect_uriconstant)claude://claude.ai/mcp-auth-callback/sdk400 Invalid redirect URIhttps://app.example.com/cbhttp://127.0.0.1:1234/cbhttp://example.com/cb400 Invalid redirect URIFix
Accept private-use URI schemes in addition to
https:/http:-loopback, while keeping a denylist for schemes that can execute script or read local resources (javascript:,data:,vbscript:,file:,blob:).The
https:(any host) andhttp:-loopback (RFC 8252 §8.3) behaviour is unchanged.Why this is safe
The scheme allowlist is defense-in-depth, not the authoritative open-redirect guard. The
@modelcontextprotocol/sdkauthorize handler only ever redirects to aredirect_urithe client previously registered — exact match, orInvalidRequestError('Unregistered redirect_uri'):An attacker cannot register a victim client's redirect URIs, and PKCE (S256) protects the code regardless. Widening the accepted scheme set to the private-use schemes RFC 8252 explicitly defines does not weaken that guarantee; it only stops over-blocking legitimate native clients. Dangerous script-/local-resource schemes stay rejected.
Changes
parseClientRedirectUri: reject a denylist of dangerous schemes, keephttps/http-loopback, accept other (private-use) schemes.isValidRedirectUri(auto-registration): now delegates toparseClientRedirectUri, removing the duplicated scheme logic so both the callback path and auto-registration share one definition (they had drifted into two copies of the same rule).vitestsuite (src/server/auth/oauth/http.test.ts) covering the accept/reject matrix, includingclaude://…accepted andjavascript:/data:/file:rejected.Verification
Locally against this branch:
The unit suite proves the scheme matrix. It does not spin up a full browser OAuth round-trip; the motivating end-to-end case is the real
claude://…value shown above.