Skip to content

OAuth refresh uses MCP endpoint instead of discovered protected-resource audience - #1084

Merged
DaleSeo merged 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-refresh-protected-resource-audience
Jul 29, 2026
Merged

OAuth refresh uses MCP endpoint instead of discovered protected-resource audience#1084
DaleSeo merged 1 commit into
modelcontextprotocol:mainfrom
tsarlandie-oai:codex/oauth-refresh-protected-resource-audience

Conversation

@tsarlandie-oai

Copy link
Copy Markdown
Contributor

Description

AuthorizationManager correctly resolves the OAuth resource from protected-resource metadata:

async fn oauth_resource(&self) -> String {
    self.discovered_resource
        .read()
        .await
        .clone()
        .unwrap_or_else(|| self.base_url.to_string())
}

Both initial authorization and authorization-code exchange correctly use:

.add_extra_param("resource", self.oauth_resource().await)

However, AuthorizationManager::refresh_token() uses:

.add_extra_param("resource", self.base_url.to_string())

Consequently, refresh can request a different token audience than the one used for the original authorization.

Reproduction

  1. Configure an MCP endpoint:

    https://server.example/mcp
    
  2. Return protected-resource metadata identifying:

    {
      "resource": "https://server.example",
      "authorization_servers": [
        "https://auth.example"
      ]
    }
  3. Complete OAuth authorization successfully.

  4. Refresh the resulting access token.

Actual behavior

The refresh request contains:

grant_type=refresh_token
resource=https://server.example/mcp

Authorization servers enforcing audience binding can reject this with:

invalid_target

Expected behavior

The refresh request should use the validated, discovered protected-resource identifier:

grant_type=refresh_token
resource=https://server.example

This preserves the same audience across authorization, code exchange, and refresh, consistent with RFC 8707 and RFC 9728.

Fix

 let mut refresh_request = oauth_client
     .exchange_refresh_token(&refresh_token_value)
-    .add_extra_param("resource", self.base_url.to_string());
+    .add_extra_param("resource", self.oauth_resource().await);

The existing endpoint fallback remains unchanged when protected-resource metadata does not provide a discovered resource.

Regression coverage

Add refresh_token_uses_discovered_protected_resource, which runs the complete protected-resource discovery, authorization, authorization-code exchange, and refresh flow using the existing recording OAuth HTTP client. It verifies:

  • The discovered root resource differs from the /mcp endpoint.
  • Initial authorization, authorization-code exchange, and refresh all use exactly the same discovered audience.
  • The existing refresh_token_includes_resource_parameter test continues to cover endpoint fallback when no resource is discovered.

The new regression test fails before the fix because the refresh audience is https://mcp.example.com/mcp while the other two audiences are https://mcp.example.com.

Downstream impact

Clients currently have to intercept and rewrite SDK-generated refresh requests to maintain the correct audience. Fixing this upstream allows that workaround to be removed.

Validation

  • cargo test -p rmcp --features auth --lib
  • cargo fmt --all -- --check
  • cargo clippy --all-targets --all-features -- -D warnings

@github-actions github-actions Bot added T-core Core library changes T-transport Transport layer changes labels Jul 29, 2026
@tsarlandie-oai
tsarlandie-oai marked this pull request as ready for review July 29, 2026 20:21
@tsarlandie-oai
tsarlandie-oai requested a review from a team as a code owner July 29, 2026 20:21

@DaleSeo DaleSeo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix, @tsarlandie-oai!

@DaleSeo
DaleSeo merged commit d82c94a into modelcontextprotocol:main Jul 29, 2026
22 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-transport Transport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants