Skip to content

Commit 43e954f

Browse files
author
Charlie Tonneslan
committed
fix: preserve existing refresh_token when server omits it in refresh 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: #2270
1 parent 75a80b6 commit 43e954f

File tree

1 file changed

+11
-0
lines changed

1 file changed

+11
-0
lines changed

src/mcp/client/auth/oauth2.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,17 @@ async def _handle_refresh_response(self, response: httpx.Response) -> bool: # p
458458
content = await response.aread()
459459
token_response = OAuthToken.model_validate_json(content)
460460

461+
# Per RFC 6749 Section 6, the server MAY issue a new refresh token.
462+
# If the response omits it, preserve the existing one.
463+
if (
464+
not token_response.refresh_token
465+
and self.context.current_tokens
466+
and self.context.current_tokens.refresh_token
467+
):
468+
token_response = token_response.model_copy(
469+
update={"refresh_token": self.context.current_tokens.refresh_token}
470+
)
471+
461472
self.context.current_tokens = token_response
462473
self.context.update_token_expiry(token_response)
463474
await self.context.storage.set_tokens(token_response)

0 commit comments

Comments
 (0)