Bind MCP auth grants to the server URL to require re-consent on URL change#320165
Merged
Conversation
…hange An MCP auth grant was previously keyed only by server id, so re-pointing a server at a new URL (while keeping the same id) would silently reuse a token the user consented to for the original endpoint. Store the URL the grant was made for and only release the token when the server's current URL still matches, otherwise re-prompt. Cosmetic origin differences (host case, default port, root trailing slash) are normalized via WHATWG URL so they don't force spurious re-consent, while a path trailing slash is preserved as a meaningful difference. Stdio servers (no URL) and product.json trusted servers are unaffected.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR strengthens MCP authentication by binding stored auth grants to the MCP server URL (for HTTP transports), ensuring users are re-prompted if a server keeps the same id but is repointed to a different endpoint.
Changes:
- Extend
AllowedMcpServerwith an optionalurlbinding and updateisAccessAllowed(...)to optionally enforce URL matching via canonical WHATWGURLcomparison. - Update
MainThreadMcpto pass the current HTTP server URL into access checks and to persist the URL when granting access. - Add a dedicated test suite covering URL binding behaviors, including cosmetic equivalence and legacy grants.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/services/authentication/test/browser/authenticationQueryServiceMocks.ts | Updates test mock signature to accept an optional MCP server URL. |
| src/vs/workbench/services/authentication/test/browser/authenticationMcpAccessService.test.ts | Adds a URL-binding-focused test suite for isAccessAllowed. |
| src/vs/workbench/services/authentication/browser/authenticationMcpAccessService.ts | Implements URL canonical comparison, stores URL binding on grants, and enforces URL match when provided. |
| src/vs/workbench/api/browser/mainThreadMcp.ts | Threads the MCP server’s current HTTP URL through access checks and persists it when granting access. |
Copilot's findings
- Files reviewed: 4/4 changed files
- Comments generated: 4
Address PR review: the optional mcpServerUrl conflated id-only inspection with the HTTP token-release gate. Split into isAccessAllowed (id only, used by the management/query API) and isAccessAllowedForUrl (URL required, used by the HTTP gate in mainThreadMcp). Auth is HTTP-only, so the gate now narrows the launch to HTTP and always passes the URL. Align the test mock with production semantics (canonical URL equality + legacy allowed===undefined => true) by reusing the exported urlsEqual helper.
dmitrivMS
approved these changes
Jun 5, 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.
Problem
An MCP auth grant was previously keyed only by the server id. Because a server's
idcan stay the same while itsurlchanges, re-pointing an HTTP MCP server at a new endpoint (or editingmcp.jsonto swap the URL under an existing id) would silently reuse a token the user had consented to release for the original endpoint. That means a token scoped/intended forhttps://trusted.example.com/mcpcould be sent tohttps://evil.example.com/mcpwithout any re-prompt.Fix
Bind each grant to the URL it was made for:
AllowedMcpServernow stores an optionalurl(the URL the grant was made for;undefinedfor stdio servers, which have no URL).isAccessAllowed(...)takes an optionalmcpServerUrl. When provided, the token is only released if the server's current URL still matches the stored one; otherwise it returnsundefined(re-prompt). Omitting the argument preserves the old id-only behavior for inspection (e.g. the management UI).MainThreadMcppasses the HTTP server's current URL through on the access checks and persists it when access is granted.URL comparison semantics
URLs are compared by their canonical WHATWG
URLform so that:SERVER.EXAMPLE.COM), default port, encoding, and a root trailing slash (foo.comvsfoo.com/).foo.com/avsfoo.com/a/) is treated as a different endpoint and re-prompts.Behavior notes
url) re-prompt once when a URL is supplied — intentional, since we can't prove they were consented for the current endpoint.urlis only overwritten when one is provided).product.jsontrusted servers are unaffected.Tests
Adds a
isAccessAllowed URL binding (security)suite covering match, cosmetic-equivalence, path-slash difference, URL change, legacy grants, inspection-without-URL, stdio, trusted-server bypass, and the management-toggle-preserves-binding case. FullAuthenticationMcpAccessServicesuite passes (42/42).Opening as a draft for review.