Skip to content

Commit 3159ef0

Browse files
committed
docs(auth): protocol discovery order and auth discovery logging
- authorization-multiprotocol.md: discovery order (PRM → path-relative → root → OAuth fallback), client flow, [Auth discovery] DEBUG logs - auth-analysis.md: multi-protocol discovery order and logging in §4.2, §12.1.3, §13.2.2 - multi-protocol-refactoring-plan.md: §11.4 PRM-first order, §11.5 note, §3.2.2 pseudo-code; auth discovery logging
1 parent b5e3172 commit 3159ef0

File tree

3 files changed

+64
-39
lines changed

3 files changed

+64
-39
lines changed

docs/authorization-multiprotocol.md

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,20 @@ Discovery answers: *Which auth protocols does this resource support, and where i
4242

4343
1. **WWW-Authenticate on 401** — The resource server may include `resource_metadata` (PRM URL), `auth_protocols`, `default_protocol`, `protocol_preferences` (MCP extensions). See RFC 6750 (Bearer) and RFC 9728 (resource metadata).
4444
2. **Protected Resource Metadata (PRM)** — RFC 9728 defines `/.well-known/oauth-protected-resource` (optionally with path). PRM JSON includes `authorization_servers`, and the SDK extends it with `mcp_auth_protocols`, `mcp_default_auth_protocol`, `mcp_auth_protocol_preferences`.
45-
3. **Unified discovery endpoint**`/.well-known/authorization_servers` returns a list of protocol metadata (MCP-style). Clients try this first; if it fails or returns no protocols, they fall back to PRM’s `mcp_auth_protocols`.
45+
3. **Unified discovery endpoint**`/.well-known/authorization_servers` returns a list of protocol metadata (MCP-style). The client tries path-relative first, then root (see protocol discovery order below).
46+
47+
**Protocol discovery order (priority):**
48+
49+
1. **Priority 1: PRM `mcp_auth_protocols`** — If PRM was obtained and contains `mcp_auth_protocols`, use that list.
50+
2. **Priority 2: Path-relative unified discovery**`{origin}/.well-known/authorization_servers{resource_path}` (e.g. `http://localhost:8002/.well-known/authorization_servers/mcp`).
51+
3. **Priority 3: Root unified discovery**`{origin}/.well-known/authorization_servers`.
52+
4. **Priority 4: OAuth fallback** — If unified discovery failed and PRM has `authorization_servers`, attempt OAuth protocol discovery.
4653

4754
**Client-side logic (high level):**
4855

4956
- On 401, extract `resource_metadata` from WWW-Authenticate.
5057
- Build PRM URLs: (1) `resource_metadata` if present, (2) path-based `/.well-known/oauth-protected-resource{path}`, (3) root `/.well-known/oauth-protected-resource`. Request each until PRM is obtained.
51-
- Request `/.well-known/authorization_servers` (path relative to resource URL). Parse `protocols`; on failure or empty, use `prm.mcp_auth_protocols`.
58+
- For protocol list: if PRM has `mcp_auth_protocols`, use it (priority 1). Else try path-relative `/.well-known/authorization_servers{path}`, then root `/.well-known/authorization_servers`. If both fail and PRM has `authorization_servers`, use OAuth fallback.
5259
- Combine protocol list with WWW-Authenticate `auth_protocols` if present, then select one via `AuthProtocolRegistry.select_protocol(available, default_protocol, preferences)`.
5360

5461
**Relationship between authorization URL endpoints**
@@ -66,7 +73,7 @@ There are three distinct URL trees involved:
6673
**URL tree (example: AS on 9000, RS on 8002)**
6774

6875
```
69-
Authorization Server (http://localhost:9000)
76+
OAuth Authorization Server (http://localhost:9000)
7077
├── /.well-known/oauth-authorization-server ← OAuth AS metadata
7178
├── /authorize
7279
├── /token
@@ -86,7 +93,9 @@ MCP Resource Server (http://localhost:8002)
8693
2. If absent, try path-based: `{origin}/.well-known/oauth-protected-resource{resource_path}` (e.g. `http://localhost:8002/.well-known/oauth-protected-resource/mcp`).
8794
3. If absent, try root: `{origin}/.well-known/oauth-protected-resource`.
8895
4. PRM includes `authorization_servers` (AS URL) and `mcp_auth_protocols`; for OAuth, the client then fetches `{AS}/.well-known/oauth-authorization-server`.
89-
5. For protocol list, client tries `{resource_url}/.well-known/authorization_servers` (e.g. `http://localhost:8002/mcp/.well-known/authorization_servers`); many RS mount discovery at origin `{origin}/.well-known/authorization_servers`. If that returns 404 or empty, client uses `prm.mcp_auth_protocols`.
96+
5. For protocol list (in order): (1) If PRM has `mcp_auth_protocols`, use it. (2) Else try path-relative `{origin}/.well-known/authorization_servers{resource_path}` (e.g. `http://localhost:8002/.well-known/authorization_servers/mcp`). (3) Else try root `{origin}/.well-known/authorization_servers`. (4) If all fail and PRM has `authorization_servers`, use OAuth fallback.
97+
98+
**Auth discovery logging:** When discovery runs, the SDK emits debug-level logs (English, `[Auth discovery]` prefix) for each PRM and unified-discovery request: URL, status code, and (on 200) pretty-printed response body. Set `LOG_LEVEL=DEBUG` on the client to see them. Implemented in `mcp.client.auth.utils` (`format_json_for_logging`, `handle_protected_resource_response`, `discover_authorization_servers`) and `mcp.client.auth.multi_protocol` (`_parse_protocols_from_discovery_response`, `async_auth_flow`).
9099

91100
**References:** RFC 9728 (PRM), RFC 8414 (OAuth AS metadata), SDK `mcp.client.auth.utils` (`build_protected_resource_metadata_discovery_urls`, `discover_authorization_servers`).
92101

@@ -101,13 +110,18 @@ flowchart LR
101110
E --> F[Try path-based well-known]
102111
F --> G[Try root well-known]
103112
G --> H[PRM obtained]
104-
H --> I[Unified discovery]
105-
I --> J[GET /.well-known/authorization_servers]
113+
H --> I{PRM.mcp_auth_protocols?}
114+
I -->|Yes| N[Select protocol]
115+
I -->|No| J[Path-relative unified discovery]
106116
J --> K{200 + protocols?}
107-
K -->|Yes| L[Use protocols list]
108-
K -->|No| M[Use PRM.mcp_auth_protocols]
109-
L --> N[Select protocol]
110-
M --> N
117+
K -->|Yes| N
118+
K -->|No| L[Root unified discovery]
119+
L --> M{200 + protocols?}
120+
M -->|Yes| N
121+
M -->|No| O{PRM.authorization_servers?}
122+
O -->|Yes| P[OAuth fallback]
123+
O -->|No| Q[Fail]
124+
P --> N
111125
```
112126

113127
### 2.2 MCP client logic

src/mcp/client/auth/auth-analysis.md

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ flowchart TD
315315
4. `/.well-known/oauth-authorization-server`
316316
5. `/.well-known/openid-configuration`
317317

318+
4. **多协议客户端的协议发现顺序**(使用 `MultiProtocolAuthProvider` 时)
319+
- 协议列表获取优先级:(1)PRM 的 `mcp_auth_protocols`(若已取得 PRM);(2)路径相对统一发现 `/.well-known/authorization_servers{path}`;(3)根路径统一发现 `/.well-known/authorization_servers`;(4)若均未得到协议列表且 PRM 含 `authorization_servers`,则 OAuth 回退。
320+
- **鉴权发现日志**:发现过程在 `mcp.client.auth` 中输出 DEBUG 级别、英文、带 `[Auth discovery]` 前缀的日志;客户端设置 `LOG_LEVEL=DEBUG` 可查看。
321+
318322
#### 阶段 2: Scope 选择
319323

320324
根据 MCP 规范的 Scope 选择策略,按以下优先级选择 scope:
@@ -914,10 +918,15 @@ provider = PrivateKeyJWTOAuthProvider(
914918
- 支持非 Bearer 的认证方案
915919
916920
#### 12.1.3 协议发现机制
917-
- **统一的能力发现端点**(推荐)
918-
- 实现 `/.well-known/authorization_servers` 端点
921+
- **协议发现顺序**(客户端)
922+
- 优先级 1:PRM 的 `mcp_auth_protocols`(若已取得 PRM)
923+
- 优先级 2:路径相对统一发现 `/.well-known/authorization_servers{path}`
924+
- 优先级 3:根路径统一发现 `/.well-known/authorization_servers`
925+
- 优先级 4:若上述均未得到协议列表且 PRM 含 `authorization_servers`,则 OAuth 回退
926+
- **统一的能力发现端点**
927+
- 实现 `/.well-known/authorization_servers`(及路径相对版本 `/.well-known/authorization_servers{path}`)
919928
- 返回服务器支持的所有授权协议列表和能力信息
920-
- 客户端首先访问此端点发现可用协议
929+
- **鉴权发现日志**:发现过程输出 DEBUG 级别、英文、带 `[Auth discovery]` 前缀的日志;客户端设置 `LOG_LEVEL=DEBUG` 可查看。
921930
922931
- **协议特定的元数据发现**
923932
- 每个协议可以提供自己的元数据发现端点
@@ -1313,15 +1322,18 @@ class AuthProtocol(Protocol):
13131322
**当前函数**: `build_protected_resource_metadata_discovery_urls()`, `build_oauth_authorization_server_metadata_discovery_urls()`
13141323

13151324
**改造内容**:
1316-
1. **新增统一能力发现端点支持**
1325+
1. **新增统一能力发现端点支持**(发现顺序:PRM 优先,再路径相对/根路径统一发现,最后 OAuth 回退)
13171326
```python
13181327
async def discover_authorization_servers(
13191328
resource_url: str,
1320-
http_client: httpx.AsyncClient
1329+
http_client: httpx.AsyncClient,
1330+
prm: ProtectedResourceMetadata | None = None,
1331+
resource_path: str = "",
13211332
) -> list[AuthProtocolMetadata]:
1322-
"""统一的授权服务器发现流程"""
1323-
# 1. 首先访问统一的能力发现端点
1324-
# 2. 根据返回的列表,访问每个协议的元数据端点
1333+
"""协议发现:PRM.mcp_auth_protocols → 路径相对统一发现 → 根路径统一发现 → OAuth 回退"""
1334+
# 1. 若已有 PRM 且含 mcp_auth_protocols,直接使用
1335+
# 2. 路径相对 /.well-known/authorization_servers{path},再根路径
1336+
# 3. 若仍无协议列表且 PRM 含 authorization_servers,由调用方走 OAuth 回退
13251337
```
13261338

13271339
2. **新增协议特定的元数据发现**

src/mcp/client/auth/multi-protocol-refactoring-plan.md

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -283,24 +283,21 @@ DPoP作为独立的通用组件,协议可以选择性使用:
283283
```python
284284
async def discover_authorization_servers(
285285
resource_url: str,
286-
http_client: httpx.AsyncClient
286+
http_client: httpx.AsyncClient,
287+
prm: ProtectedResourceMetadata | None = None,
288+
resource_path: str = "",
287289
) -> list[AuthProtocolMetadata]:
288-
"""统一的授权服务器发现流程"""
289-
# 1. 首先访问统一的能力发现端点
290-
discovery_url = f"{resource_url}/.well-known/authorization_servers"
291-
try:
292-
response = await http_client.get(discovery_url)
293-
if response.status_code == 200:
294-
data = response.json()
295-
return [
296-
AuthProtocolMetadata(**proto_data)
297-
for proto_data in data.get("protocols", [])
298-
]
299-
except Exception:
300-
pass
301-
302-
# 2. 回退:从PRM中获取协议信息
303-
# 3. 回退:使用协议特定的well-known URI
290+
"""统一的授权服务器/协议发现流程(PRM 优先,再统一发现,最后 OAuth 回退)"""
291+
# 1. 若已有 PRM 且含 mcp_auth_protocols,直接使用
292+
if prm and getattr(prm, "mcp_auth_protocols", None):
293+
return _protocol_metadata_list_from_prm(prm)
294+
# 2. 路径相对统一发现:/.well-known/authorization_servers{path}
295+
urls = build_authorization_servers_discovery_urls(resource_url, resource_path)
296+
for url in urls:
297+
# 尝试请求,200 且含 protocols 则解析并返回
298+
...
299+
# 3. 若仍无协议列表且 PRM 含 authorization_servers,走 OAuth 回退(由调用方处理)
300+
return []
304301
```
305302

306303
2. **新增协议特定的元数据发现**
@@ -1365,11 +1362,13 @@ graph TD
13651362

13661363
**取舍**:mTLS 在 TLS 握手层处理,不解析 HTTP `Authorization` 头;`Mutual TLS` 验证器从 TLS 连接/握手上下文读取客户端证书并校验。
13671364

1368-
### 11.4 协议发现顺序:统一端点 vs PRM 优先
1365+
### 11.4 协议发现顺序:PRM 优先 vs 统一发现
1366+
1367+
**取舍**:客户端协议发现顺序为:(1)PRM 的 `mcp_auth_protocols`(若已取得 PRM);(2)路径相对统一发现 `/.well-known/authorization_servers{path}`;(3)根路径统一发现 `/.well-known/authorization_servers`;(4)若上述均未得到协议列表且 PRM 含 `authorization_servers`,则 OAuth 回退。
13691368

1370-
**取舍**客户端优先请求 `/.well-known/authorization_servers`(统一发现);若 404 或空,回退到 PRM`mcp_auth_protocols`
1369+
**原因**PRM 为 RFC 9728 标准且常与 401 的 `resource_metadata` 一起使用,优先使用可减少往返;统一发现作为补充;OAuth 回退保证仅实现 RFC 9728RS 仍可被多协议客户端使用
13711370

1372-
**原因**统一端点便于多协议声明与扩展;PRM 回退保证仅支持 RFC 9728 的 RS 仍可被多协议客户端发现
1371+
**鉴权发现日志**发现过程在 `mcp.client.auth` 中输出 DEBUG 级别、英文、带 `[Auth discovery]` 前缀的日志(请求 URL、状态码及 200 时的可读响应体);客户端设置 `LOG_LEVEL=DEBUG` 可查看
13731372

13741373
### 11.5 授权端点归属:AS 与 RS 的 URL 树
13751374

@@ -1380,7 +1379,7 @@ graph TD
13801379
| `/.well-known/oauth-protected-resource{path}` | RS | PRM(RFC 9728) |
13811380
| `/.well-known/authorization_servers` | RS | 统一协议发现(MCP 扩展) |
13821381

1383-
**说明**:AS 与 RS 可能部署在不同主机(如 AS 9000、RS 8002);客户端先向 RS 获取 PRM/协议列表,再根据 `metadata_url` 向 AS 获取 OAuth 元数据。
1382+
**说明**:AS 与 RS 可能部署在不同主机(如 AS 9000、RS 8002);客户端按 11.4 所述顺序向 RS 获取协议列表(PRM 优先,再统一发现),再根据 `metadata_url` 向 AS 获取 OAuth 元数据。
13841383

13851384
### 11.6 TokenStorage 双契约:OAuthToken vs AuthCredentials
13861385

0 commit comments

Comments
 (0)