Skip to content

fix: preserve existing refresh_token when server omits it in refresh response#2304

Open
ctonneslan wants to merge 1 commit intomodelcontextprotocol:mainfrom
ctonneslan:fix/preserve-refresh-token-on-refresh
Open

fix: preserve existing refresh_token when server omits it in refresh response#2304
ctonneslan wants to merge 1 commit intomodelcontextprotocol:mainfrom
ctonneslan:fix/preserve-refresh-token-on-refresh

Conversation

@ctonneslan
Copy link

Problem

_handle_refresh_response() replaces current_tokens with the parsed refresh response as-is. When the authorization server does not return a new refresh_token in the response, the previously stored one is lost.

After the first successful refresh, can_refresh_token() returns False and all subsequent refreshes fail, forcing full re-authentication.

Per RFC 6749 Section 6, issuing a new refresh token in the refresh response is optional. Many OAuth providers omit it by default (Google, Auth0 without rotation enabled, Okta in persistent token mode).

Fix

Before overwriting current_tokens, check if the refresh response omits refresh_token. If so, preserve the existing one using model_copy(update=...).

if (
    not token_response.refresh_token
    and self.context.current_tokens
    and self.context.current_tokens.refresh_token
):
    token_response = token_response.model_copy(
        update={"refresh_token": self.context.current_tokens.refresh_token}
    )

This matches the RFC wording: if a new refresh token is issued, replace the old one; otherwise keep using the existing one.

Fixes #2270

…response

Per RFC 6749 Section 6, the authorization server MAY issue a new
refresh token in the refresh response. When it does not, the client
must preserve the existing one. The current implementation replaces
current_tokens with the parsed response as-is, which discards the
stored refresh_token when the server omits it.

After the first successful refresh, can_refresh_token() returns False
and all subsequent refreshes fail, forcing full re-authentication.

Many OAuth providers omit refresh_token from refresh responses by
default (Google, Auth0 without rotation, Okta in persistent mode).

Github-Issue: modelcontextprotocol#2270
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

_handle_refresh_response discards existing refresh_token when server omits it

1 participant