You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
bug(mcp): MCP OAuth fails to obtain refresh_token and drops client_secret during background token refresh #29577
When configuring and using remote MCP servers that utilize OAuth 2.0 with Google endpoints (such as Google Workspace APIs like Docs, Sheets, Slides, Drive) or other confidential OAuth providers requiring a client_secret for token refresh:
No Refresh Token Issued: On initial interactive authentication via /mcp auth <server>, Google's authorization server only issues a 1-hour access_token and no refresh_token. This occurs because buildAuthorizationUrl (packages/core/src/utils/oauth-flow.ts) constructs a generic RFC 6749 authorization URL without requesting access_type=offline and prompt=consent for Google endpoints (unlike the CLI's internal login flow in packages/core/src/code_assist/oauth2.ts).
client_secret Discarded During Refresh: When a refresh token is present, background token refresh fails against Google's token endpoint (https://oauth2.googleapis.com/token) because Google strictly requires client_secret for OAuth clients registered with a secret. The CLI completely drops clientSecret:
packages/core/src/tools/mcp-client.ts: getStoredOAuthToken() only passes { clientId: credentials.clientId } to authProvider.getValidToken().
packages/core/src/mcp/token-storage/types.ts: OAuthCredentials does not store clientSecret.
As a result, refreshAccessToken() posts without client_secret, and Google rejects the request: {"error": "invalid_request", "error_description": "client_secret is missing."}
Destructive Wipe on Refresh Failure: In packages/core/src/mcp/oauth-provider.ts (getValidToken), any failure during token refresh immediately triggers:
This deletes the stored credentials and traps the user in a continuous re-authentication loop where they are forced to log in via browser every single hour.
What did you expect to happen?
For Google authorization endpoints, buildAuthorizationUrl should request access_type: "offline" and prompt: "consent" so that a long-lived refresh_token is returned on initial authentication.
clientSecret provided in MCP configuration should be preserved in OAuthCredentials and forwarded by getStoredOAuthToken to getValidToken, allowing refreshAccessToken to successfully authenticate against endpoints that enforce client secret validation.
What happened?
When configuring and using remote MCP servers that utilize OAuth 2.0 with Google endpoints (such as Google Workspace APIs like Docs, Sheets, Slides, Drive) or other confidential OAuth providers requiring a
client_secretfor token refresh:/mcp auth <server>, Google's authorization server only issues a 1-houraccess_tokenand norefresh_token. This occurs becausebuildAuthorizationUrl(packages/core/src/utils/oauth-flow.ts) constructs a generic RFC 6749 authorization URL without requestingaccess_type=offlineandprompt=consentfor Google endpoints (unlike the CLI's internal login flow inpackages/core/src/code_assist/oauth2.ts).client_secretDiscarded During Refresh: When a refresh token is present, background token refresh fails against Google's token endpoint (https://oauth2.googleapis.com/token) because Google strictly requiresclient_secretfor OAuth clients registered with a secret. The CLI completely dropsclientSecret:packages/core/src/tools/mcp-client.ts:getStoredOAuthToken()only passes{ clientId: credentials.clientId }toauthProvider.getValidToken().packages/core/src/mcp/token-storage/types.ts:OAuthCredentialsdoes not storeclientSecret.As a result,
refreshAccessToken()posts withoutclient_secret, and Google rejects the request:{"error": "invalid_request", "error_description": "client_secret is missing."}packages/core/src/mcp/oauth-provider.ts(getValidToken), any failure during token refresh immediately triggers:What did you expect to happen?
buildAuthorizationUrlshould requestaccess_type: "offline"andprompt: "consent"so that a long-livedrefresh_tokenis returned on initial authentication.clientSecretprovided in MCP configuration should be preserved inOAuthCredentialsand forwarded bygetStoredOAuthTokentogetValidToken, allowingrefreshAccessTokento successfully authenticate against endpoints that enforce client secret validation.Client information
Client Information
• CLI Version: 0.62.0
• Operating System: macOS Darwin arm64
• Node Version: v26.8.1
Login information
Google Account & Remote MCP Servers with OAuth 2.0 (Google Workspace APIs / Google Cloud OAuth 2.0 Web & Desktop Client IDs)
Anything else we need to know?
We have audited the relevant code paths in
packages/core:packages/core/src/utils/oauth-flow.ts:441(buildAuthorizationUrl)packages/core/src/tools/mcp-client.ts:1656(getStoredOAuthToken)packages/core/src/mcp/token-storage/types.ts(OAuthCredentials)packages/core/src/mcp/oauth-token-storage.ts(MCPOAuthTokenStorage.saveToken)packages/core/src/mcp/oauth-provider.ts:620(MCPOAuthProvider.getValidToken)A pull request addressing this with tests will be linked shortly.