Skip to content

OAuth DCR client information is missing after Atlassian authorization, preventing refresh #244

Description

@emmanuelchucks

Summary

With Atlassian Rovo MCP's OAuth 2.1 endpoint, authorization succeeds and MCPorter stores both an eight-hour access token and a refresh token, but the resulting vault entry has no clientInfo. Once the access token expires, MCPorter cannot redeem the refresh token and falls back to sending the expired access token, producing 401 invalid_token and requiring another browser authorization.

I reproduced this independently on macOS and Linux with separate OAuth registrations.

Environment

  • MCPorter: 0.12.3
  • Node.js: 24.x
  • Platforms: macOS and Linux
  • Server: https://mcp.atlassian.com/v1/mcp/authv2
  • Server config: HTTP transport with auth: "oauth"

Reproduction

  1. Configure the Atlassian server:

    {
      "mcpServers": {
        "atlassian": {
          "baseUrl": "https://mcp.atlassian.com/v1/mcp/authv2",
          "auth": "oauth"
        }
      }
    }
  2. Run mcporter auth atlassian --reset and complete the browser consent flow.

  3. Confirm that the credential entry contains tokens.refresh_token and tokens.expires_at, but does not contain clientInfo. This can be checked without printing credentials:

    import fs from "node:fs";
    import os from "node:os";
    import path from "node:path";
    
    const vault = JSON.parse(
      fs.readFileSync(path.join(os.homedir(), ".mcporter", "credentials.json"), "utf8"),
    );
    const entry = Object.values(vault.entries).find(
      (candidate) => candidate.serverName === "atlassian",
    );
    console.log({
      hasRefreshToken: Boolean(entry?.tokens?.refresh_token),
      hasClientInfo: Boolean(entry?.clientInfo?.client_id),
    });

    Observed immediately after successful authorization:

    { hasRefreshToken: true, hasClientInfo: false }
    
  4. After the access token's eight-hour lifetime, run:

    MCPORTER_LOG_LEVEL=debug \
      mcporter list atlassian --status --json --no-oauth

    The relevant output is:

    Cached OAuth token for 'atlassian' is expired, but no client information is available.
    Using cached OAuth access token for 'atlassian' (non-interactive).
    

    The request then fails with HTTP 401 and {"error":"invalid_token"}.

Expected behavior

The dynamic client registration used during authorization should remain available with the token set so MCPorter can silently redeem the refresh token after access-token expiry.

Additional evidence

  • Atlassian advertises offline_access and refresh_token; the grant includes offline_access, and a refresh token is present.
  • The authorization request contains a dynamically registered client_id.
  • The issued access-token JWT contains the same client_id, confirming the grant is bound to that registration.
  • The two reproductions used different client IDs, so this was not one host invalidating another host's refresh chain.

As a local diagnostic, I restored minimal client information from each host's own JWT claim:

{
  "client_id": "<the token's client_id claim>",
  "token_endpoint_auth_method": "none"
}

Without reauthorizing, the already-expired Linux credential then refreshed successfully:

Refreshed cached OAuth access token for 'atlassian' (non-interactive).

A subsequent server status check and authenticated read both passed. This suggests the refresh-token and silent-refresh paths are healthy once the registration information is available.

Related issues checked

This appears distinct: the refresh token exists, but the dynamic client information needed to use it is absent after successful authorization.

No access tokens, refresh tokens, client IDs, authorization codes, states, or PKCE values are included here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Urgent regression or broken agent/channel workflow affecting real users now.clawsweeper:current-main-reproClawSweeper found a high-confidence current-main issue reproduction.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:auth-providerThis issue is about auth, provider routing, model choice, or SecretRef resolution.impact:session-stateThis issue is about session, memory, transcript, context, or agent state drift.issue-rating: 🦀 challenger crabExceptional issue quality: high-confidence current-main reproduction and actionable evidence.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions