feat(mcp): OAuth 2.1 授权码流——发现/动态注册/PKCE/keychain 存储/401 刷新(P1-③) - #611
Open
coder-hhx wants to merge 4 commits into
Open
feat(mcp): OAuth 2.1 授权码流——发现/动态注册/PKCE/keychain 存储/401 刷新(P1-③)#611coder-hhx wants to merge 4 commits into
coder-hhx wants to merge 4 commits into
Conversation
按 MCP Authorization 规范(RFC 9728/8414/7591/8252/8707)实现完整授权码流:
- services/mcp_oauth/:discovery(401 挑战→PRM→AS 元数据候选序列+旧规范
fallback)、register(RFC 7591 动态注册)、flow(PKCE S256+state、
127.0.0.1 随机端口 loopback 回调、token 交换/刷新)、store(keyring v3
三平台 + Linux 无 secret-service 降级 0600 文件 + 进程内缓存)
- transport 集成:http/sse 每请求经 ensure_bearer 注入 Bearer(将过期主动
刷新单飞);401 → Unauthorized 变体 → 被动刷新一次重试,不可行则报带
MCP_OAUTH_AUTHORIZATION_REQUIRED 标记的引导错误;SSE GET 流重连取最新 token
- 命令:mcp_oauth_authorize(系统浏览器,仅用户手势)/status/clear;
test 响应附 oauth 诊断(状态/过期/存储后端,永不含 token 本体)
- 配置:McpServerConfig.auth { type, scope?, clientId? }——token 永不进
settings/同步/备份;normalize 双端;卡片授权徽章+Connect/断开(WebUI 仅
状态展示);编辑弹窗鉴权区;删除路径(卡片+McpManager)清 keychain
依赖新增 keyring 3.6 + getrandom 0.3。cargo test 18 项新用例、前端 2503+631
全绿,clippy/biome/三端 tsc/UI 边界门禁零告警。
coder-hhx
marked this pull request as ready for review
August 25, 2026 05:32
- McpManager create/update/validate 接受 auth {type, scope?, clientId?}:
schema 与 McpAuthConfig 对齐,normalizeAuthInput 严格校验入参形状,
type:"none"/缺省折叠为 undefined(patch 显式覆盖即可清除 oauth 配置),
凭据永不经过本工具;list 行附 auth=oauth 标记
- authOauthHint(zh/en)改为如实描述:keychain 不可用时降级 0600 私有
文件,凭据不参与同步/备份、不离开本机
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.
Closes #610
概述
按 2026 H2 能力路线图 交付 P1-③ MCP OAuth 2.1:remote MCP server(http/sse)从「仅静态 headers」升级为完整 OAuth 授权码流——发现、动态注册、PKCE、keychain 存储、401 自动刷新。附设计文档与完整测试。
设计要点(设计文档)
对 roadmap 假设做了四处现实修正(均已核对代码):仓库无 mcp.rs SDK(transport 为自研阻塞式 JSON-RPC,OAuth 直接挂
HttpTransport/SseTransport);不引 oauth2 crate(适配胶水超过手写协议本身,token 交换只是两个 form POST);auth 不设"headers"枚举值(静态 headers 已是独立字段,避免存量迁移);Linux 无 secret-service 时诚实降级 0600 文件(加密密钥无处安放,加密是安全剧场)。核心取舍:授权流只由显式用户手势触发(卡片 Connect),transport 内 401 只做静默刷新 + 标记性错误,绝不弹浏览器——对话中途并发工具调用不会弹 N 个窗口。
实现
Rust(
services/mcp_oauth/四模块 + transport 集成)discovery:401WWW-Authenticate→ RFC 9728 PRM(挑战参数优先,退 well-known 路径插入推导)→ RFC 8414 AS 元数据(路径感知候选序列 + OIDC fallback)→ 全败退 2025-03-26 旧规范默认端点;强制校验 S256 支持register:RFC 7591 动态注册(token_endpoint_auth_method: "none"公共客户端);静态 clientId 跳过flow:PKCE(S256) + state 恒等校验、127.0.0.1:0loopback one-shot 回调(5 分钟超时,state 不符 400 拒绝但不终止等待)、RFC 8707resource受众绑定、refresh token 轮换即持久化store:keyring v3(macOS Keychain / Windows Credential Manager / Linux secret-service)+ 文件降级 + 进程内缓存(避免每请求 Keychain IPC);TokenRecord 内嵌 server_url,与配置不符即视为无 token(防 audience 串用)ensure_bearer注入(将过期主动刷新,单飞互斥);401 →Unauthorized变体 → 被动刷新一次重试 → 不可行报带MCP_OAUTH_AUTHORIZATION_REQUIRED标记的引导错误;SSE GET 长连每次重连取最新 token(不固化 spawn 时刻值)mcp_oauth_authorize/status/clear;mcp_test_server响应附 oauth 诊断(状态/过期/存储后端,永不含 token 本体)system_proxy::blocking_client_builder(),代理异常 fail fastTS/UI
McpServerConfig.auth: { type: "none"|"oauth", scope?, clientId? }——token 永不进 settings,Gateway 同步与 WebDAV 备份天然不含凭据,零脱敏改造type:"oauth"才落壳对象,"none"/未知/非对象一律不存(旧配置形态零变化)端到端实测(mock OAuth AS + MCP server,全流程闭环)
mock server 端日志(401 挑战 → 发现 → DCR → PKCE 校验 → Bearer 访问):
授权后
mcp_test_server诊断(token 入 keychain,实机验证security find-generic-password可见、断开后清除):{ "ok": true, "toolsCount": 2, "oauth": { "state": "authorized", "refreshable": true, "storage": "keychain", "issuer": "http://127.0.0.1:39170", "scope": "mcp.read mcp.tools" } }Change scope
crates/agent-gui/src-tauri/src/services/mcp_oauth/、crates/agent-gui/src-tauri/src/commands/integration/{mcp.rs,mcp_oauth.rs}、crates/agent-ui/src/lib/mcp/oauthApi.ts、crates/agent-ui/src/pages/mcp-hub/{McpServerCard,McpServerEditModal}.tsx、crates/agent-ui/src/lib/settings/{types,index}.ts、docs/design/mcp-oauth.mdScreenshots / preview
编辑弹窗:http transport 的鉴权配置区(OAuth 2.1 / Scope / Client ID + keychain 提示):
Verification
cargo test -p liveagent --lib:全绿,新增 18 用例(发现链解析/PRM 候选/PKCE 向量/loopback 真实 TCP 往返含 state 防 CSRF/store 文件降级 roundtrip+0600 权限/过期窗口/授权互斥/auth 配置判定与 serde 兼容)cargo clippy -p liveagent --lib:零警告node --test:2503/2503(新增 normalize auth 用例;mcp-hub-tabs 设计约束测试通过)check-ui-boundaries.mjs门禁通过securityCLI 验证)已知边界(详见设计文档 §8)
Pre-submit checklist