Repository navigation
auth: re-authorize when the access token expires without a refresh token - #1418
Merged
guglielmo-san merged 1 commit intoOct 20, 2026
Conversation
When the token response carries no refresh_token, the token source that AuthorizationCodeHandler.Authorize built was oauth2.Config.TokenSource. Once the access token expired, its Token method failed with "oauth2: token expired and refresh token is not set". setMCPHeaders in the streamable client transport only lets an invalid_grant RetrieveError through to the server, so every later request was rejected before it was sent, and Authorize was never called again. modelcontextprotocol#917 handled the same hard-failed state for a rejected refresh token. Issuing a refresh token is up to the authorization server, and the handler only asks for offline_access when RequestRefreshToken is set, so a token without one is a common case. Use oauth2.StaticTokenSource when there is no refresh token. The transport keeps sending the token after it expires, and the server's 401 goes through the existing re-authorization path. Token sources built by a custom NewTokenSource are unchanged. Tests (each fails without the change): - TestAuthorize_ExpiredTokenWithoutRefreshToken: the token source keeps returning an expired access token that has no refresh token. Without the change, Authorize itself fails, because it reads the token back to record the granted scopes. - TestStreamableClientOAuth_ExpiredTokenWithoutRefreshToken: a session authorized by AuthorizationCodeHandler re-authorizes once the server rejects the expired token. Without the change, Connect fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
guglielmo-san
approved these changes
Oct 20, 2026
Contributor
|
@luiacuaniello thank you for the contribution! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1417.
When the token response carries no
refresh_token, the token source thatAuthorizationCodeHandler.Authorizebuilt wasoauth2.Config.TokenSource. Once the access token expired, itsTokenmethod failed withoauth2: token expired and refresh token is not set.setMCPHeadersin the streamable client transport only lets aninvalid_grantRetrieveErrorthrough to the server, so every later request was rejected before it was sent, andAuthorizewas never called again. #917 handled the same hard-failed state for a rejected refresh token.Issuing a refresh token is up to the authorization server, and the handler only asks for
offline_accesswhenRequestRefreshTokenis set, so a token without one is a common case.This uses
oauth2.StaticTokenSourcewhen there is no refresh token. The transport keeps sending the token after it expires, and the server's 401 goes through the existing re-authorization path. Token sources built by a customNewTokenSourceare unchanged.Tests (each fails without the change):
TestAuthorize_ExpiredTokenWithoutRefreshToken: the token source keeps returning an expired access token that has no refresh token. Without the change,Authorizeitself fails, because it reads the token back to record the granted scopes.TestStreamableClientOAuth_ExpiredTokenWithoutRefreshToken: a session authorized byAuthorizationCodeHandlerre-authorizes once the server rejects the expired token. Without the change,Connectfails.go test ./...,go test -raceonauthandmcp,go vet ./..., staticcheck v0.6.1 andscripts/client-conformance.sh(239 passed, same asmain) are clean.AI assistance: prepared with Claude Code; I reviewed and verified the change.
🤖 Generated with Claude Code