MCP: Cannot authenticate to multiple Atlassian sites simultaneously - OAuth credentials shared across MCP server configurations · Issue #293533 · microsoft/vscode · GitHub
Skip to content

MCP: Cannot authenticate to multiple Atlassian sites simultaneously - OAuth credentials shared across MCP server configurations #293533

Closed

Description

Summary:
When configuring multiple Atlassian MCP servers pointing to different Atlassian Cloud sites (e.g., site-a.atlassian.net and site-b.atlassian.net), VS Code only retains authentication for one site. The OAuth credential appears to be keyed by the OAuth client ID and/or MCP auth provider identity derived only from the server URL origin, causing all Atlassian MCP connections to share a single token.

Steps to Reproduce:

  1. Configure two Atlassian MCP servers in mcp.json with different names (and different query params to differentiate URLs).
    • Example: https://mcp.atlassian.com/?site=a and https://mcp.atlassian.com/?site=b
  2. Authenticate the first connection — prompted with fresh OAuth flow, select site-a.atlassian.net.
  3. Authenticate the second connection — prompted to reuse "MCP" credential or "Sign in to another account".
  4. Select "Sign in to another account" and authenticate to site-b.atlassian.net.
  5. Call getAccessibleAtlassianResources on both MCP servers.

Expected Result:

  • Site A connection returns site-a.atlassian.net.
  • Site B connection returns site-b.atlassian.net.

Actual Result:

  • Both connections return the same site (whichever was authenticated last).
  • The second authentication overwrites the first.

Investigation Notes:

  • Credentials are stored in ~/Library/Application Support/Code/User/globalStorage/state.vscdb.
  • The dynamicAuthProviders table entry shows e.g.:
    {
      "providerId": "https://mcp.atlassian.com/",
      ...
    }
  • The providerId appears to be derived from the URL origin only, stripping query parameters.
    This means multiple MCP servers with the same origin but different query params (e.g. ?site=a vs ?site=b) resolve to the same providerId and therefore share credentials.
  • The key uses https://mcp.atlassian.com/ (without query params) and a shared clientId, so all Atlassian MCP servers resolve to the same credential regardless of the configured URL or server name.

Suggested Fix:
Include the full configured URL (including query params) or at minimum the MCP server name from mcp.json when generating the providerId / credential key, allowing per-server credential isolation (multiple OAuth tokens per MCP server configuration).

Workaround:
Use separate VS Code profiles for each Atlassian site.

Environment:

  • VS Code: 1.108.2
  • GitHub Copilot extension: 1.388.0
  • GitHub Copilot Chat extension: 0.36.2
  • OS: macOS

Activity

  1. added
    chat-ext-issueTriaging assistance tag. Automatically applied if an issue comes from the chat oss repo.
    on Feb 6, 2026
  2. removed
    chat-ext-issueTriaging assistance tag. Automatically applied if an issue comes from the chat oss repo.
    on Feb 6, 2026
  3. clgtm commented on Feb 10, 2026

    @clgtm

    In case this helps -

    Command:   authprobe scan --trace-failure --llm-max-tokens=1080 https://mcp.atlassian.com/v1/mcp                                                                                                                                                                                                   [4950/8569]
    Scanning:  https://mcp.atlassian.com/v1/mcp
    Scan time: Feb 10, 2026 15:38:14 UTC
    Github:    https://github.com/authprobe/authprobe
    
    Funnel
      [1] MCP probe (401 + WWW-Authenticate)      [+] PASS
            missing WWW-Authenticate/resource_metadata
    
      [2] MCP initialize + tools/list             [-] SKIP
            initialize -> 401 (non-JSON response) (auth required)
    
      [3] PRM fetch matrix                        [X] FAIL
            https://mcp.atlassian.com/.well-known/oauth-protected-resource -> 404
            https://mcp.atlassian.com/.well-known/oauth-protected-resource/v1/mcp ->
            404
            PRM unreachable or unusable; OAuth discovery unavailable
    
      [4] Auth server metadata                    [-] SKIP
            no authorization_servers in PRM
    
      [5] Token endpoint readiness (heuristics)   [-] SKIP
            no token_endpoint in metadata
    
      [6] Dynamic client registration (RFC 7591)  [-] SKIP
            no registration_endpoint in metadata
    
    Primary Finding (HIGH): AUTH_REQUIRED_BUT_NOT_ADVERTISED (confidence 1.00)
      Evidence:
          401/403 without WWW-Authenticate/resource_metadata
          no PRM endpoints returned valid metadata
          Auth appears required but OAuth discovery was not advertised. Next steps: add
          WWW-Authenticate + PRM for OAuth/MCP discovery, or document the required non-OAuth auth
          (e.g., SigV4).
    
    ┌───────────────────────┤ CALL TRACE ├───────────────────────┐
    Call Trace Using: https://github.com/authprobe/authprobe
    
      ┌────────────┐                                                    ┌────────────┐
      │ authprobe  │                                                    │ MCP Server │
      └─────┬──────┘                                                    └─────┬──────┘
            │                                                                 │
            │ ╔═══ Step 1: MCP probe                    ═══════╪═══════════════════╗
            │  GET https://mcp.atlassian.com/v1/mcp
            │  Reason: 401 + WWW-Authenticate discovery
            │    Accept:  text/event-stream
            │    Host:    mcp.atlassian.com
            ├─────────────────────────────────────────────────────────────────►│
            │  401 Unauthorized                                                                                                                                                                                                                                                                                           │    Atl-Request-Id:                    90f18011-287c-4b68-823e-70de1643691a
            │    Atl-Traceid:                       90f18011287c4b68823e70de1643691a
            │    Cf-Ray:                            9cbcb13cafe1f820-PDX
            │    Content-Length:                    79
            │    Content-Type:                      application/json
            │    Date:                              Tue, 10 Feb 2026 15:38:13 GMT
            │    Ge-Edge-Trusted-Cloudflare-Proxy:  bWNwLWNsb3VkZmxhcmUK
            │    Nel:                               {"failure_fraction": 0.01, "include_subdomains": true, "max_age": 600, "report_to": "endpoint-1"}
            │    Report-To:                         {"endpoints": [{"url": "https://dz8aopenkvv6s.cloudfront.net"}], "group": "endpoint-1", "include_subdomains": true, "max_age": 600}
            │    Server:                            AtlassianEdge
            │    Server-Timing:                     atl-edge;dur=13,atl-edge-internal;dur=3,atl-edge-upstream;dur=11,atl-edge-pop;desc="aws-us-west-2"
            │    Strict-Transport-Security:         max-age=63072000; preload
            │    Vary:                              Accept-Encoding
            │    Www-Authenticate:                  Bearer realm="OAuth", error="invalid_token", error_description="Missing or invalid access token"
            │    X-Content-Type-Options:            nosniff
            │    X-Xss-Protection:                  1; mode=block
            │◄─────────────────────────────────────────────────────────────────┤
            │                                                                  │
            │ ╔═══ Step 2: MCP initialize               ═══════╪═══════════════════╗                                                                                                                                                                                                                   [4883/8569]
            │  POST https://mcp.atlassian.com/v1/mcp
            │  Reason: Step 2: MCP initialize + tools/list (initialize)
            │    Accept:                application/json, text/event-stream
            │    Content-Type:          application/json
            │    Host:                  mcp.atlassian.com
            │    Mcp-Protocol-Version:  2025-11-25
            ├─────────────────────────────────────────────────────────────────►│
            │  401 Unauthorized
            │    Atl-Request-Id:                    6260d19c-d19e-43b3-ad5d-5243f55c67c6
            │    Atl-Traceid:                       6260d19cd19e43b3ad5d5243f55c67c6
            │    Cf-Ray:                            9cbcb13cd82af820-PDX
            │    Content-Length:                    79
            │    Content-Type:                      application/json
            │    Date:                              Tue, 10 Feb 2026 15:38:13 GMT
            │    Ge-Edge-Trusted-Cloudflare-Proxy:  bWNwLWNsb3VkZmxhcmUK
            │    Nel:                               {"failure_fraction": 0.01, "include_subdomains": true, "max_age": 600, "report_to": "endpoint-1"}
            │    Report-To:                         {"endpoints": [{"url": "https://dz8aopenkvv6s.cloudfront.net"}], "group": "endpoint-1", "include_subdomains": true, "max_age": 600}
            │    Server:                            AtlassianEdge
            │    Server-Timing:                     atl-edge;dur=13,atl-edge-internal;dur=3,atl-edge-upstream;dur=11,atl-edge-pop;desc="aws-us-west-2"
            │    Strict-Transport-Security:         max-age=63072000; preload
            │    Vary:                              Accept-Encoding
            │    Www-Authenticate:                  Bearer realm="OAuth", error="invalid_token", error_description="Missing or invalid access token"
            │    X-Content-Type-Options:            nosniff
            │    X-Xss-Protection:                  1; mode=block
            │◄─────────────────────────────────────────────────────────────────┤
            │                                                                  │
            │ ╔═══ Step 3: PRM Discovery                ═══════╪═══════════════════╗
            │  GET https://mcp.atlassian.com/.well-known/oauth-protected-resource
            │  Reason: Step 3: PRM fetch matrix
            │    Accept:  application/json
            │    Host:    mcp.atlassian.com
            ├─────────────────────────────────────────────────────────────────►│
            │  404 Not Found
            │    Atl-Request-Id:                    79e502a3-e1a2-40fd-a88e-dc043dc74f3b
            │    Atl-Traceid:                       79e502a3e1a240fda88edc043dc74f3b
            │    Cf-Ray:                            9cbcb13d1bb89873-PDX
            │    Content-Length:                    13
            │    Content-Type:                      text/plain; charset=UTF-8
            │    Date:                              Tue, 10 Feb 2026 15:38:13 GMT
            │    Ge-Edge-Trusted-Cloudflare-Proxy:  bWNwLWNsb3VkZmxhcmUK
            │    Nel:                               {"failure_fraction": 0.01, "include_subdomains": true, "max_age": 600, "report_to": "endpoint-1"}
            │    Report-To:                         {"endpoints": [{"url": "https://dz8aopenkvv6s.cloudfront.net"}], "group": "endpoint-1", "include_subdomains": true, "max_age": 600}
            │    Server:                            AtlassianEdge
            │    Server-Timing:                     atl-edge;dur=13,atl-edge-internal;dur=3,atl-edge-upstream;dur=11,atl-edge-pop;desc="aws-us-west-2"
            │    Strict-Transport-Security:         max-age=63072000; preload
            │    Vary:                              Accept-Encoding
            │    X-Content-Type-Options:            nosniff
            │    X-Xss-Protection:                  1; mode=block
            │◄─────────────────────────────────────────────────────────────────┤
            │                                                                  │
            │  GET https://mcp.atlassian.com/.well-known/oauth-protected-resource/v1/mcp
            │  Reason: Step 3: PRM fetch matrix
            │    Accept:  application/json
            │    Host:    mcp.atlassian.com
            ├─────────────────────────────────────────────────────────────────►│
            │  404 Not Found
            │    Atl-Request-Id:                    89d3baf3-55f7-4010-bc49-8cf9085a7acb
            │    Atl-Traceid:                       89d3baf355f74010bc498cf9085a7acb
            │    Cf-Ray:                            9cbcb13d48edf820-PDX
            │    Content-Length:                    13
            │    Content-Type:                      text/plain; charset=UTF-8
            │    Date:                              Tue, 10 Feb 2026 15:38:13 GMT
            │    Ge-Edge-Trusted-Cloudflare-Proxy:  bWNwLWNsb3VkZmxhcmUK
            │    Nel:                               {"failure_fraction": 0.01, "include_subdomains": true, "max_age": 600, "report_to": "endpoint-1"}
            │    Report-To:                         {"endpoints": [{"url": "https://dz8aopenkvv6s.cloudfront.net"}], "group": "endpoint-1", "include_subdomains": true, "max_age": 600}
            │    Server:                            AtlassianEdge
            │    Server-Timing:                     atl-edge;dur=13,atl-edge-internal;dur=3,atl-edge-upstream;dur=11,atl-edge-pop;desc="aws-us-west-2"
            │    Strict-Transport-Security:         max-age=63072000; preload
            │    Vary:                              Accept-Encoding
            │    X-Content-Type-Options:            nosniff
            │    X-Xss-Protection:                  1; mode=block
            │◄─────────────────────────────────────────────────────────────────┤
            ▼                                                                  ▼
    
    ┌───────────────┤ FAILED TEST VERBOSE OUTPUT ├───────────────┐
    == Step 3: PRM fetch matrix ==
    > GET /.well-known/oauth-protected-resource HTTP/1.1
    > Host: mcp.atlassian.com
    > Accept: application/json
    >
    > (empty body)
    < HTTP/2.0 404 Not Found
    < Atl-Request-Id: 061d2fe2-4e01-4073-8434-a72d68434adb
    < Atl-Traceid: 061d2fe24e0140738434a72d68434adb
    < Cf-Ray: 9cbcb13d8c539873-PDX
    < Content-Length: 13
    < Content-Type: text/plain; charset=UTF-8
    < Date: Tue, 10 Feb 2026 15:38:14 GMT
    < Ge-Edge-Trusted-Cloudflare-Proxy: bWNwLWNsb3VkZmxhcmUK
    < Nel: {"failure_fraction": 0.01, "include_subdomains": true, "max_age": 600, "report_to": "endpoint-1"}
    < Report-To: {"endpoints": [{"url": "https://dz8aopenkvv6s.cloudfront.net"}], "group": "endpoint-1", "include_subdomains": true, "max_age": 600}
    < Server: AtlassianEdge
    < Server-Timing: atl-edge;dur=13,atl-edge-internal;dur=3,atl-edge-upstream;dur=11,atl-edge-pop;desc="aws-us-west-2"
    < Strict-Transport-Security: max-age=63072000; preload
    < Vary: Accept-Encoding
    < X-Content-Type-Options: nosniff
    < X-Xss-Protection: 1; mode=block
    <
    < 404 Not Found
    
    == Step 3: PRM fetch matrix ==
    > GET /.well-known/oauth-protected-resource/v1/mcp HTTP/1.1
    > Host: mcp.atlassian.com
    > Accept: application/json
    >
    > (empty body)
    < HTTP/2.0 404 Not Found
    < Atl-Request-Id: 28e8d58a-30c3-4119-a4fb-9e8faf8b0616
    < Atl-Traceid: 28e8d58a30c34119a4fb9e8faf8b0616
    < Cf-Ray: 9cbcb13db9c7f820-PDX
    < Content-Length: 13
    < Content-Type: text/plain; charset=UTF-8
    < Date: Tue, 10 Feb 2026 15:38:14 GMT
    < Ge-Edge-Trusted-Cloudflare-Proxy: bWNwLWNsb3VkZmxhcmUK
    < Nel: {"failure_fraction": 0.01, "include_subdomains": true, "max_age": 600, "report_to": "endpoint-1"}
    < Report-To: {"endpoints": [{"url": "https://dz8aopenkvv6s.cloudfront.net"}], "group": "endpoint-1", "include_subdomains": true, "max_age": 600}
    < Server: AtlassianEdge
    < Server-Timing: atl-edge;dur=12,atl-edge-internal;dur=3,atl-edge-upstream;dur=11,atl-edge-pop;desc="aws-us-west-2"
    < Strict-Transport-Security: max-age=63072000; preload
    < Vary: Accept-Encoding
    < X-Content-Type-Options: nosniff
    < X-Xss-Protection: 1; mode=block
    <
    < 404 Not Found
    
    

    ┌────────────────────┤ LLM EXPLANATION ├─────────────────────┐

    Primary Finding Explanation: AUTH_SERVER_ISSUER_MISMATCH (high confidence, 1.00)

    Summary:
    The MCP OAuth server metadata returned from the discovery endpoint at https://mcp.slack.com/.well-known/oauth-authorization-server reports an issuer "https://slack.com" which mismatches the expected issuer "https://mcp.slack.com" used to perform the discovery.


    Spec-grounded Analysis

    Relevant Specifications:

    • RFC 8414 (OAuth 2.0 Authorization Server Metadata)
    • MCP 2025-11-25 (MCP OAuth specification)
    • RFC 9728 (Protected Resource Metadata)
    • JSON-RPC 2.0 (general interaction protocol)

    Why this mismatch is a valid and justified failure:

    1. Issuer Identifier MUST EXACTLY MATCH in Metadata (RFC 8414, Section 3):

      "authorization server metadata MUST include an issuer property that is the issuer identifier of the authorization server as defined in [OpenID.issuer]. The issuer value in the metadata document MUST match the issuer value used for discovery."

      Here, the discovery URI was anchored in the issuer https://mcp.slack.com namespace, but the JSON metadata claims "issuer": "https://slack.com". According to RFC 8414, this lack of exact string match constitutes a discovery failure.

    2. Issuer Consistency Required for Trust (RFC 9728, Section 3):
      MCP leverages issuer consistency to bind metadata, token issuance, and protected resource validation. A mismatch risks security and operational failure.

    3. MCP 2025-11-25 Requirements for OAuth Provider Metadata:
      MCP explicitly mandates strict matching between authorization server metadata issuer and discovery URLs to avoid ambiguity or cross-issuer issues. The MCP tooling depends on this for critical flows such as token issuance and client registration.

    4. Security and Interoperability Implications:

      • The mismatched issuer may result in clients accepting tokens or metadata from a different domain, breaking trust boundaries and facilitating attacks (token substitution, replay, or phishing).
      • OAuth clients and MCP tooling will reject authorization servers whose issuer does not match the discovery URI; this is by design and necessary for secure interoperability.

    Correct Server Behavior

    • The OAuth authorization server metadata at https://mcp.slack.com/.well-known/oauth-authorization-server must contain an issuer field exactly equal to the discovery issuer URI:
      {
        ...
        "issuer": "https://mcp.slack.com",
        ...
      }
      
    • The metadata should conform to RFC 8414 with all required fields present and consistent.
    • The server must not redirect or provide metadata pointing to a different issuer domain (e.g., "https://slack.com").
    • Compliance tools will validate the issuer field against the initial discovery URL and fail if there is any discrepancy.

    This binding guarantees that the authorization server, protected resource, and client share a consistent trust domain as mandated by MCP and OAuth 2.0 standards.


    Secondary Notes

    • Other findings such as MCP initialization failure (non-JSON 404 on initialize) and PRM resource mismatch compound interoperability issues but are not as impactful as the fundamental issuer identity failure.
    • Per JSON-RPC 2.0, MCP devices must respond to initialize requests with valid JSON-RPC responses. Returning a 404 non-JSON constitutes a protocol compliance failure.
    • RFC 9728 requires exact resource URL matching for protected resource metadata, relevant to secured resource identification but secondary to the issuer mismatch.
    • RFC 7591 (dynamic client registration) and token endpoint readiness were skipped due to absent metadata fields, which may be a result of this issuer metadata inconsistency.

    Conclusion

    The AUTH_SERVER_ISSUER_MISMATCH failure is valid, justified, and critical under RFC 8414 and MCP 2025-11-25 requirements. The metadata issuer must be corrected to exactly match the MCP OAuth discovery URI https://mcp.slack.com. Without this fix, compliance, interoperability, and security guarantees are compromised.


    If you need further assistance on resolving remaining findings or guidance on MCP tooling corrections, please ask.
    exit status 2

  4. TylerLeonhardt commented on Feb 16, 2026

    @TylerLeonhardt
    Member

    Thanks for the detailed report! VS Code uses a standard OAuth flow here — the authorization server is discovered via the .well-known/oauth-authorization-server endpoint on the MCP server origin or via the WWW-Authenticate header.

    Can you clarify what the different authorization servers are for each Atlassian site and how they are exposed via a typical OAuth flow?

  5. vs-code-engineering commented on Feb 24, 2026

    @vs-code-engineering
    Contributor

    This issue has been closed as it has been labeled with info-needed and has not received the additional information that was requested from the issue author. It has been labeled as info-needed for 7 days.

  6. juteasl commented on Mar 18, 2026

    @juteasl

    Confirming this issue is still reproducible and not limited to VS Code. The same root cause affects Claude Code's MCP OAuth implementation — when two Atlassian MCP server entries are configured pointing to the same MCP endpoint, authenticating the second entry overwrites the credential for the first. Both entries end up using whichever site was authenticated last.

    The providerId being derived from the URL origin alone (ignoring server name and query params) is definitively the issue. Users who believe they have two sites configured simultaneously are mistaken — only the last-authenticated site is actually active.

    Would be great to see this reopened and addressed. Multi-tenant Atlassian setups are common in enterprise environments where users need access to both a personal/dev site and a work organization site simultaneously.

  7. TylerLeonhardt commented on Mar 19, 2026

    @TylerLeonhardt
    Member

    I'll reopen but my question hasn't been answered yet.

  8. juteasl commented on Mar 19, 2026

    @juteasl

    To answer TylerLeonhardt's question directly:

    Atlassian uses a single centralized OAuth authorization server — https://auth.atlassian.com — for all Atlassian Cloud sites. There are no per-site authorization servers. The .well-known/oauth-authorization-server endpoint on mcp.atlassian.com returns the same AS regardless of query parameters.

    The per-site differentiation works as follows:

    1. OAuth completes against the shared auth.atlassian.com
    2. The resulting token is multi-site — it covers all cloud resources the user has access to
    3. Callers hit https://api.atlassian.com/oauth/token/accessible-resources to enumerate which Atlassian Cloud sites the token grants access to
    4. API calls then include a cloudId path segment to target a specific site
    5. The mcp.atlassian.com query params (e.g. ?site=a) tell the MCP gateway which site to proxy to — but the underlying token is the same either way

    This means VS Code's auth server discovery is working correctly — it finds one auth server because there is only one. The problem is that VS Code doesn't currently support multiple independent credential sessions for the same auth server origin across different MCP server configs. When a second Atlassian MCP entry authenticates, it overwrites the first because both resolve to providerId: "https://mcp.atlassian.com/".

    The fix needs to be in how the providerId / credential key is derived — it should incorporate the MCP server name from mcp.json (or the full configured URL) in addition to the origin, so that two MCP server entries pointing to the same endpoint can hold independent tokens. This would allow a user to be authenticated to site-a.atlassian.net via one server config and site-b.atlassian.net via another simultaneously.

    Happy to provide additional detail or test a fix if that's helpful.

  9. TylerLeonhardt commented on Mar 19, 2026

    @TylerLeonhardt
    Member

    This doesn't seem aligned to OAuth 2.0 spec.

    Authorization Server Metadata

    issuer
    REQUIRED. The authorization server's issuer identifier, which is a URL that
    uses the "https" scheme and has no query or fragment components.

    https://datatracker.ietf.org/doc/html/rfc8414#section-2

    I'm trying to understand how to map Atlassian's setup to what we follow which is standard OAuth 2.0. I can't be in the business of special casing Atlassian.

  10. 7 remaining items

  11. added this to the Backlog milestone on Mar 27, 2026
  12. celidrissi commented on Jul 7, 2026

    @celidrissi

    +1 for this. Very anoying issue 🥲

  13. rpelevin commented on Jul 7, 2026

    @rpelevin

    Small regression shape I would want here: keep OAuth issuer discovery separate from the local credential slot identity.

    I would test two MCP server entries that share the same authorization server and provider origin, but represent different configured resources or site bindings:

    1. authenticate entry A and bind it to site/resource A;
    2. authenticate entry B and bind it to site/resource B;
    3. call the same MCP tool through each entry and verify the selected token/resource context does not collapse to whichever entry authenticated last;
    4. change the configured server name or resource binding and require re-auth or an explicit migration, rather than silently reusing the old credential slot.

    That keeps the RFC 8414 issuer semantics intact while making the client-side storage invariant explicit: the credential lookup key should include the configured MCP server identity or resource binding, not only the OAuth provider origin. A useful negative test is token A being valid cryptographically but rejected for entry B because the local credential slot, resource binding, or site context no longer matches the proposed MCP call.

  14. added
    insiders-releasedPatch has been released in VS Code Insiders
    and removed
    unreleasedPatch has not yet been released in VS Code Insiders
    on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

authenticationIssues with the Authentication platformchat-mcpfeature-requestRequest for new features or functionalityinsiders-releasedPatch has been released in VS Code Insidersverification-neededVerification of issue is requested

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions