Skip to content

fix(auth): preserve refresh_token when refresh response omits it#949

Open
SarthakB11 wants to merge 1 commit into
modelcontextprotocol:mainfrom
SarthakB11:fix/issue-921
Open

fix(auth): preserve refresh_token when refresh response omits it#949
SarthakB11 wants to merge 1 commit into
modelcontextprotocol:mainfrom
SarthakB11:fix/issue-921

Conversation

@SarthakB11

Copy link
Copy Markdown

Title: fix(auth): keep existing refresh token when refresh response omits it

Motivation and Context

This fixes token loss on refresh responses that omit refresh_token. RFC 6749 §6 and the OAuth 2.1 draft make rotation optional, so when the server omits the field the client should keep the token it already holds; AuthorizationManager::refresh_token() was persisting the response verbatim, wiping the stored refresh token and forcing full re-authorization on the next call. The same bug was fixed in the Python SDK (modelcontextprotocol/python-sdk#2270).

Fixes #921.

How Has This Been Tested?

Added regression tests in crates/rmcp/src/transport/auth.rs covering both the omit case (server drops refresh_token, client keeps the existing one) and the rotate case (server issues a new refresh_token, client replaces). cargo test --features auth --lib refresh_token_ passes.

Breaking Changes

None. Public API is unchanged. The only behavior change is that a refresh response without a
refresh_token no longer wipes the stored refresh token.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Per RFC 6749 section 6 and OAuth 2.1: when the server does not issue a new
refresh_token on a refresh response, the client MUST keep the existing one.
AuthorizationManager::refresh_token() was persisting the response verbatim,
dropping the previous refresh_token and forcing full re-authorization on
the next refresh.

Match the fix from python-sdk#2270: preserve the existing refresh_token
when the response omits it, replace when the server rotates.

Fixes modelcontextprotocol#921

Signed-off-by: SarthakB11 <sarthak.bhardwaj21b@iiitg.ac.in>
@SarthakB11 SarthakB11 requested a review from a team as a code owner July 1, 2026 21:25
@github-actions github-actions Bot added T-core Core library changes T-transport Transport layer changes labels Jul 1, 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.

OAuth refresh drops existing refresh_token when refresh response omits it

2 participants