OAuth scope selection falls back to the authorization server's scopes_supported, which the spec does not allow

未關閉 適合新手
#3,781 2 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

維護者通常 1 天內回覆

還沒有人認領這個 Issue。

評估

難度
2/5
預估耗時
1-3 小時
新手友好度
79/100
Issue 類型
缺陷
描述清晰度
描述清楚
活躍度
活躍
技術堆疊
python

研究方向

從 src/mcp/client/auth/utils.py 的 get_client_metadata_scopes 開始,沿著 scope 選擇分支追蹤,並對照 issue 中引用的規範行為。檢查或新增針對缺少 challenge scope 和 PRM scopes_supported 的測試;完成條件是這些情況在 PRM scopes 存在時選取它們,否則省略 scope,且不使用 authorization server scopes。

由索引模型根據 Issue 內容生成。

描述

bug v1 v2
Initial Checks
Description

When the 401 carries no scope and the Protected Resource Metadata (PRM) has no scopes_supported, get_client_metadata_scopes (src/mcp/client/auth/utils.py) requests every scope the authorization server lists:

# MCP spec scope selection priority:
#   1. WWW-Authenticate header scope
#   2. PRM scopes_supported
#   3. AS scopes_supported (SDK fallback)
#   4. Omit scope parameter
...
elif authorization_server_metadata is not None and authorization_server_metadata.scopes_supported is not None:
    selected_scope = " ".join(authorization_server_metadata.scopes_supported)

The spec has two steps, and the second one ends in omitting scope (2026-07-28, Authorization → Scope Selection Strategy):

  1. Use scope parameter from the initial WWW-Authenticate header in the 401 response, if provided
  2. If scope is not available, use all scopes defined in scopes_supported from the Protected Resource Metadata document, omitting the scope parameter if scopes_supported is undefined.

The same section says clients SHOULD request "only the scopes necessary for their intended operations."

An authorization server's scopes_supported describes the whole authorization server, not this resource. Often that's a shared IdP serving many APIs (openid, profile, email, org-admin and write scopes for unrelated services). Tier 3 therefore turns "the resource didn't say" into "ask for everything the IdP has". The results:

  • an over-privileged token for an MCP server that needed none of it;
  • a broad consent screen for the user;
  • a login failure when the authorization server refuses to issue some of the listed scopes to a public or dynamically registered client (invalid_scope), where omitting scope would have succeeded with the authorization server's default.
Example Code

The selection function on its own (works on 1.26.0, 1.30.0 and 2.3.0):

from mcp.client.auth.utils import get_client_metadata_scopes
from mcp.shared.auth import OAuthMetadata, ProtectedResourceMetadata

prm = ProtectedResourceMetadata(
    resource="https://mcp.example.com/mcp",
    authorization_servers=["https://auth.example.com"],
)  # no scopes_supported
asm = OAuthMetadata(
    issuer="https://auth.example.com",
    authorization_endpoint="https://auth.example.com/authorize",
    token_endpoint="https://auth.example.com/token",
    scopes_supported=["openid", "profile", "email", "admin:org", "repo:write"],
)
print(get_client_metadata_scopes(None, prm, asm))
# 'openid profile email admin:org repo:write'   — expected: None (omit scope)

In the real flow, OAuthClientProvider.async_auth_flow assigns this result to client_metadata.scope on every 401. So a client can't avoid it by configuring a narrower scope: that is #2317, which has several production reports.

Expected behaviour

Follow the spec: (1) the challenge scope, (2) PRM scopes_supported, otherwise omit scope. If a fallback beyond the spec is wanted, make it something a client turns on, not the default.

Relation to existing issues
  • #2317: a configured client_metadata.scope is overwritten. This issue is separate: even with no configured scope, the SDK should omit scope rather than take the authorization server's list. Fixing #2317 alone would leave clients that don't configure a scope on tier 3.
  • #3172 (closed): put the configured scope after tier 3. That doesn't help here, because the AS-wide list still wins whenever the authorization server advertises any scopes.
Python & MCP Python SDK
  • MCP Python SDK: reproduced on 1.26.0, 1.30.0, 2.3.0. The same code is on main at d7845c0.
  • Python 3.12
主要語言
Python
星號
24.7k
分支
4.1k
平均合併
18 小時 27 分鐘
30 天內合併 PR
15

環境準備

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

modelcontextprotocol/python-sdk 的其他 Issue

查看 modelcontextprotocol/python-sdk 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。