auth: refresh_token() always sends `scope`, so refresh fails against Salesforce ("scope parameter not supported")
維護者通常 3 天內回覆
@gocamille 已經在處理了。
開始於 2026年10月29日。
評估
這個 Issue 還沒有評估資料。
描述
Summary
AuthorizationManager::refresh_token() always adds the stored granted_scopes (plus offline_access when advertised) to the refresh request as scope. Salesforce's token endpoint rejects any refresh request that includes scope:
400 {"error":"invalid_request","error_description":"scope parameter not supported"}
So every refresh fails against a Salesforce-hosted MCP server, and the user has to go through the full authorization flow again each time the access token expires. Clients built on rmcp, including Codex, can't do anything about it, because there's no way to leave scope out of the refresh request.
Where it happens
crates/rmcp/src/transport/auth.rs on main (9e0783d):
let mut refresh_scopes = stored_credentials.granted_scopes.clone();
self.add_offline_access_if_supported(&mut refresh_scopes);
let requested_scopes = refresh_scopes.clone();
for scope in refresh_scopes {
refresh_request = refresh_request.add_scope(Scope::new(scope));
}
Clearing the stored granted_scopes doesn't help either. Salesforce returns scope in every token response ("scope": "refresh_token mcp_api"), and resolve_granted_scopes stores it again, so the next refresh sends scope once more.
Reproduction
Server: a Salesforce Hosted MCP server (https://api.salesforce.com/platform/mcp/v1/...) with a pre-registered public client (PKCE, no secret). The External Client App has "Require secret for Refresh Token Flow" turned off.
Direct calls to the org's /services/oauth2/token with the same refresh token:
| Request | Result |
|---|---|
grant_type=refresh_token&client_id=…&refresh_token=…&scope=mcp_api refresh_token |
400 invalid_request: scope parameter not supported |
grant_type=refresh_token&client_id=…&refresh_token=… |
200, new access token; response includes "scope":"refresh_token mcp_api" |
The same failure through Codex CLI 0.161.0 (rmcp 3.5.1, rev 98a5478), with RUST_LOG=codex_rmcp_client=debug,rmcp=debug:
refresh_transaction{... refresh_reason="expiry"}: rmcp::transport::auth: refresh token present, attempting refresh
WARN ... MCP OAuth provider refresh failed error=OAuth token refresh failed: Server returned error response: invalid_request: scope parameter not supported
WARN ... MCP OAuth access token is expired and refresh failed; reauthorization required
For comparison, the MCP TypeScript SDK's refreshAuthorization sends no scope, only resource, and refreshes silently against the same Salesforce server and client. That also shows Salesforce accepts the RFC 8707 resource parameter that rmcp now adds to refresh requests. Only scope is the problem.
Why this should be configurable
- RFC 6749 §6 makes
scopeOPTIONAL on refresh. If it's omitted, the server treats the request as asking for the originally granted scope. - Salesforce's OAuth 2.0 Refresh Token Flow documents
grant_type,refresh_token,client_id,client_secret,client_assertion,client_assertion_typeandformatas the request parameters.scopeisn't one of them. - Some providers do need
scopeon refresh. Microsoft Entra is one (see mcp-remote#357). So "never sendscope" isn't a safe default.
Proposed fix
Either of these would work. They aren't mutually exclusive:
- Retry without
scope: if the refresh fails withinvalid_request(orinvalid_scope) andscopewas sent, retry once without it. This works without any client changes and doesn't change behavior for providers that acceptscope. - Opt-out: a setting on
AuthorizationManager/OAuthState, for exampleomit_scope_on_refresh: bool, that clients can expose in their config.
ToolHive hit the same Salesforce error and fixed it with an opt-out: stacklok/toolhive#6830 (PR #6832).
Related
- #1330 / #1331: #1330 is about refresh requesting
offline_accessthat was never granted. PR #1331 refreshes with the granted scopes, so it still sendsscopeand won't fix Salesforce. A fix for this issue should cover the case where the server returnsscopein the token response but won't accept it on refresh. - openai/codex#58103: the downstream Codex issue.
- 主要語言
- Rust
- 星號
- 4.1k
- 分支
- 680
- 平均合併
- 3 天 23 小時
- 30 天內合併 PR
- 34
環境準備
在瀏覽器裡用你自己的 GitHub 帳號啟動這個專案的開發容器。
- 沒有 Dockerfile 或 Docker Compose 檔案
- 沒有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
modelcontextprotocol/rust-sdk 的其他 Issue
-
auth: refresh_token() requests offline_access that was never granted, so refreshes fail with invalid_scope可能已有人在做 @jstar0 於 21 天前認領。 未關閉bug P1 ready for work T-security T-transport
難度 2/5 1-3 小時 新手友好度 65/100
modelcontextprotocol/rust-sdk#1330 ·
維護者通常 3 天內回覆
-
Return merged cache hints from list_all_* client methods可能已有人在做 @gocamille 於 1 天前認領。 未關閉enhancement P2 ready for work T-service
modelcontextprotocol/rust-sdk#1411 · 1 則留言 · 已指派 1 人 ·
維護者通常 3 天內回覆
-
Enterprise-managed auth: ID-JAG `resource` claim should be optional可能已有人在做 @gocamille 於 2 天前認領。 未關閉bug P1 ready for work T-security T-transport
modelcontextprotocol/rust-sdk#1404 · 2 則留言 · 已指派 1 人 ·
維護者通常 3 天內回覆
-
Auto discovery timeout rejects a late probe response during legacy initialization可能已有人在做 @gocamille 於 2 天前認領。 未關閉bug P1 ready for work T-service
modelcontextprotocol/rust-sdk#1400 · 1 則留言 · 已指派 1 人 ·
維護者通常 3 天內回覆
-
URL mode elicitation requires elicitationId, which the 2026-07-28 schema removed可能已有人在做 @gocamille 於 3 天前認領。 未關閉bug P1 ready for work T-model
modelcontextprotocol/rust-sdk#1399 · 1 則留言 · 已指派 1 人 ·
維護者通常 3 天內回覆
查看 modelcontextprotocol/rust-sdk 的全部 Issue
相似的 Issue
-
bug priority: low ui
難度 2/5 1-3 小時 新手友好度 65/100
storytold/photocraft#10370 · 1 則留言 ·
維護者通常 1 天內回覆
-
area:doc bug in progress needs-triage
難度 2/5 1-3 小時 新手友好度 72/100
bherbruck/solvecraft#9634 · 2 則留言 · 1 個 reaction ·
-
inflate: total_in uses plain += on c_ulong (32-bit on Windows), panics in debug for streams > 4 GiB未關閉
難度 1/5 1 小時以內 新手友好度 85/100
trifectatechfoundation/zlib-rs#570 · 1 則留言 ·
-
bug discord-triage
難度 2/5 1-3 小時 新手友好度 84/100
LanternOps/breeze#10755 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 65/100
aranya-project/aranya#803 ·