news 2026/9/13 5:37:39

OpenWork 第三方 MCP 客户端 OAuth 接入指南:资源指示符(RFC 8707)与 invalid_target 排障全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenWork 第三方 MCP 客户端 OAuth 接入指南:资源指示符(RFC 8707)与 invalid_target 排障全解析

OpenWork 第三方 MCP 客户端 OAuth 接入指南:资源指示符(RFC 8707)与 invalid_target 排障全解析

【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork

本文围绕 OpenWork 部署的/mcp/agent公共 OAuth 端点,完整讲解第三方 MCP 客户端如何完成 RFC 9728 受保护资源发现、PKCE 授权码流程与resource参数(RFC 8707)的正确传递,并结合 den-api 源码与回归测试深入剖析invalid_target报错的根因、修复方法与安全上报规范。读完你将掌握一套可直接落地的 MCP OAuth 接入清单和排障方法论。

1. 背景:为什么第三方 MCP 客户端需要走 OAuth 接入

OpenWork 的 Den API 将组织能力以 MCP(Model Context Protocol)工具形式暴露给 AI 编码代理(如 OpenCode、Claude Code、Codex 等)与桌面端。仓库中 MCP 暴露策略 明确了工具面:所有带API KeysConnectorsWorkers等标签的 Den API 产品面默认允许暴露,而AdminAuthenticationSystemWebhooks等标签被有意排除,避免管理面与鉴权管道被当作 Agent 工具调用。

对于第三方(非首方桌面端)MCP 客户端,接入点是一个公共 OAuth 端点/mcp/agent。该端点只接受携带公共 OAuth 访问令牌的请求,与首方桌面端的/mcp(opaque token + 会话耦合)走完全不同的认证路径。认证实现 中明确区分了这两种 token 源:JWT 型公共令牌只能用于agent路由,且受众必须严格匹配唯一的DEN_MCP_OAUTH_RESOURCE;而首方 opaque 令牌则走会话存活性校验。因此,第三方客户端接入时,必须严格遵循下述 OAuth 流程,不能复用桌面端的令牌格式。

2. 接入前提:正确配置/mcp/agent端点

2.1 端点的组成

OpenWork 部署对外提供 MCP 服务时,公共 OAuth 接入端点是部署 URL 下的/mcp/agent

https://api.example.com/mcp/agent

配置客户端时需要注意:

  • 使用部署后的完整 URL,包括任何反向代理前缀(如/api/den),不要省略路径段;
  • 不要替换成 Web 控制台(Dashboard)的 URL——web 前端只在根路径提供页面,并不会在/mcp下提供 MCP 服务;
  • 公共 OAuth 访问令牌就是为这个端点签发的,不要在其它路由上使用。

从源码看,资源派生逻辑 区分了两种部署形态:托管 Web 应用域名(app.**.run.app或配置的DEN_WEB_APP_HOSTS)会在 den-api 前面加/api/den代理前缀,MCP 资源相应变为<origin>/api/den/mcp;直接 API 域名则保持裸形态<origin>/mcp。而公共 OAuth 的/mcp/agent资源由 auth.ts 中的deriveDenMcpAgentResource基于env.apiPublicUrl派生。也就是说,部署拓扑(是否走 web 代理)会决定实际可用的 MCP 端点路径,配置客户端前应先确认自己的部署形态。

2.2 公共令牌与资源绑定

公共 OAuth 令牌只有一个合法受众:DEN_MCP_OAUTH_RESOURCE(即/mcp/agent资源)。auth.ts 的认证上下文 中写明:

Public OAuth has exactly one allowed audience,DEN_MCP_OAUTH_RESOURCE(the advertised/mcp/agentresource), and those JWTs authenticate only on the agent route.

也就是说,oauthResources只允许agent路由持有这一个受众值,令牌被绑定到唯一资源,杜绝了多受众泛化。这是理解后续resource参数为何必须精确传递的关键。

3. 五步接入流程:从发现到调用

官方文档给出的接入流程共五步,下面逐一展开,并结合源码说明每一步在服务端的落点。

3.1 第一步:发起未认证请求,跟随WWW-Authenticate挑战

向 MCP 端点发送一个不带令牌的请求:

GET /mcp/agent HTTP/1.1 Host: api.example.com

服务端返回401,并在WWW-Authenticate头中携带resource_metadata参数。由 auth.ts 中的 bearerChallenge 生成,格式形如:

HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer error="invalid_token", resource_metadata="https://api.example.com/.well-known/oauth-protected-resource/mcp/agent", scope="mcp:read mcp:write"

注意:

  • 挑战头中同时给出了scope(MCP 请求所需作用域)与resource_metadataURL;
  • 对于合成端点https://api.example.com/mcp/agent,发现 URL 为https://api.example.com/.well-known/oauth-protected-resource/mcp/agent。该路径由 mcpProtectedResourceMetadataUrl 生成:取资源的 origin,将路径替换为/.well-known/oauth-protected-resource后拼接原路径;
  • 服务端同时在 agent.ts 注册了两种元数据路径(/.well-known/oauth-protected-resource/mcp/agent/mcp/agent/.well-known/oauth-protected-resource),客户端应优先使用挑战头中给出的地址。

3.2 第二步:读取元数据,发现授权服务器

请求上一步得到的发现 URL(无需认证),会得到 RFC 9728 受保护资源元数据,核心字段:

  • resource:受保护资源的标识符(即 MCP 端点对应的资源值,后续要原样回传);
  • authorization_servers:授权服务器标识符列表。

要点:

  • 授权服务器的 origin 可能与 MCP 端点不同,属于正常现象,按元数据指示继续发现即可;
  • 不要把元数据 URL 或 issuer URL 当作resource值使用——它们不是受保护资源标识符。

接着从授权服务器发现 OAuth 端点:OpenWork 在 auth 路由 中于多处注册了 RFC 8414 授权服务器元数据与 OpenID Connect 发现文档(如/.well-known/oauth-authorization-server/.well-known/openid-configuration),客户端可据此拿到authorization_endpointtoken_endpointregistration_endpointissuer等。

3.3 第三步:动态客户端注册 + PKCE 授权码流程

使用发现到的注册端点(OpenWork 支持 RFC 7591 动态客户端注册,路径为/register/api/auth/oauth2/register)注册客户端,回调 URI 必须是客户端实际的 callback

服务端对回调 URI 有严格校验,oauth-client-policy.ts 实现如下规则:

  • 必须是 HTTPS 回调,或 HTTP 环回(loopback)回调,如http://127.0.0.1:PORT/callbackhttp://localhost:PORT/callback(含*.localhost::1);
  • 不允许带 fragment(#);
  • 特例:cursor://anysphere.cursor-mcp/oauth/callback这类 RFC 8252 私有使用方案回调被显式放行(因为 MCP 规范只允许 HTTPS/loopback,但部分主流原生客户端只支持私有 scheme;服务端以强制 PKCE S256 作为该场景的补偿控制)。

然后发起授权码流程 + PKCE(S256,并保持以下安全机制开启:

  • 回调校验(callback validation);
  • state 校验(state verification);
  • 用户同意(consent)。

源码中动态注册请求还会被 rewriteMcpClientRegistrationRequest 预处理:校验回调 URI 白名单策略、规范化 scope,再交给授权服务器。

3.4 第四步:两处都传resource参数(最容易出错的一步)

授权 URL 查询参数表单编码的令牌请求体中,都必须且只能各传恰好一个resource参数,值为第二步发现到的resource。只在注册时提供、或只放在浏览器 URL 里,都不算数。

对于合成端点https://api.example.com/mcp/agent,编码后的参数为:

resource=https%3A%2F%2Fapi.example.com%2Fmcp%2Fagent

服务端在 normalizeMcpOAuthResourceParams 中对两处统一校验:

  • 缺失resource→ 返回invalid_target("MCP OAuth requests must include the protected resource.");
  • 多于一个resource→ 返回invalid_target("must include exactly one protected resource.");
  • 值无法被 normalizeMcpOAuthResource 识别(未知资源)→ 返回invalid_target("not recognized by this deployment.")。

normalizeMcpOAuthUrl(授权端点)与normalizeMcpOAuthRequest(令牌端点)分别在 路由注册处 调用该校验,确保只有携带正确资源的请求才会进入真正的授权/换码阶段。这也解释了官方文档强调的:"Supplying it only during registration or only in the browser URL is insufficient."

3.5 第五步:Bearer 令牌调用与刷新

拿到访问令牌后,以Authorization Bearer 头形式调用 MCP 端点:

POST /mcp/agent HTTP/1.1 Host: api.example.com Authorization: Bearer <access_token> Content-Type: application/json {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"my-client","version":"1.0.0"}}}

服务端 verifyMcpRequest 的完整校验链包括:读取 Bearer → JWT 签名/受众校验(aud必须等于DEN_MCP_OAUTH_RESOURCE,仅允许额外的userinfo受众)→ scope 必须含mcp:readmcp:writetoken_useclaim 必须是mcp→ 资源 claim 匹配 → 主体(user/org)存在 → 授权存活性(grant 或 session)→ 组织成员关系仍然有效。任何一环失败都会得到对应的 401/403 与WWW-Authenticate挑战。

刷新令牌时:客户端也应在刷新请求中携带resource。不过 Den 提供了一条兼容路径——当刷新(grant_type=refresh_token)请求省略resource时,normalizeMcpOAuthRequest 会为注册了 MCP scope 的客户端推断其单例受众并自动补上resource=DEN_MCP_OAUTH_RESOURCE。源码注释明确指出:

MCP clients should send resource on every token request, but some clients omit it while refreshing. Public MCP has exactly one valid audience, so defaulting only this grant preserves audience binding without widening it.

但要特别注意:这种推断仅适用于刷新阶段,绝不意味着初始授权或换码阶段可以省略resource。服务端在授权/换码阶段是强制要求该参数的。

4.invalid_target深度排障

4.1 报错含义

invalid_target说明 MCP OAuth 请求在resource参数上出了问题。依据 RFC 8707 §2(原文文档引用,此处不展开外部链接),该错误覆盖缺失、未知、格式错误或其他无效的资源指示符。

4.2 根因一:完全没有传resource

最常见的情况是通用 OAuth 客户端只支持在授权请求中附加额外参数,却无法在令牌请求体中附加额外字段。这类客户端换码时漏掉了resource,服务端直接返回:

{ "error": "invalid_target", "error_description": "MCP OAuth requests must include the protected resource." }

官方文档给出明确结论:改 scope 或重新注册客户端都无法补上这个缺失字段。唯一出路是:

  1. 升级客户端,使其支持在令牌请求体中携带额外参数;或
  2. 改用实现了 MCP 资源指示符(resource indicators)语义的客户端。

4.3 根因二:资源值未知或重复

  • 未知资源:传了resource但值不是本部署发现到的那个(例如用了元数据 URL、issuer URL,或任意自定义受众)→invalid_target("not recognized by this deployment.");
  • 多个资源:一次传了两个及以上resourceinvalid_target("must include exactly one protected resource.")。

正确做法:只使用发现到的那个单例值,不要添加任意受众,更不要试图关闭服务端校验。服务端的受众白名单是收敛的(仅DEN_MCP_OAUTH_RESOURCE一个公共受众),认证校验 甚至要求 JWT 中除DEN_MCP_OAUTH_RESOURCE外最多只能出现userinfo受众。

4.4 支持上报规范

向支持团队提交报告时,只记录以下最小信息集:

  • 失败的阶段(授权?换码?还是刷新/调用?);
  • HTTP 状态码;
  • 错误码(如invalid_target);
  • resource参数是否存在。

严禁在报告中分享:完整的授权 URL、Cookie、授权码、令牌、客户端密钥或租户标识符。

5. 回归验证:mcp-auth-rate-limit-recovery旅程

仓库在 evals/specs/mcp-auth-rate-limit-recovery.e2e.test.ts 中提供了端到端回归证据,覆盖官方文档描述的完整链路。测试关键断言如下:

  1. 401挑战头解析resource_metadata,并断言其等于${den.ref.apiUrl}/.well-known/oauth-protected-resource/mcp/agent(对应源码行);
  2. 分别以缺失未知重复resource发起授权与换码,均被拒绝并返回invalid_target(第 81、107 行);
  3. 发现到的正确资源+ 同一个授权码完成换码,再用令牌访问 MCP 端点成功。

运行回归:

pnpm evals:e2e mcp-auth-rate-limit-recovery

该测试同时验证了“missing, unknown, and repeated resources rejected at authorization and code exchange; no redirect or token was issued”这一行为,是判断部署行为是否符合预期的权威手段。

6. 接入自检清单

检查项要求
端点 URL使用部署 URL + 反向代理前缀,指向/mcp/agent,不用 Dashboard URL
发现401resource_metadata获取 RFC 9728 元数据
资源值使用元数据中的resource不是元数据 URL / issuer URL
授权授权码 + PKCE S256;回调必须是 HTTPS 或 loopback(或白名单私有 scheme)
resource(授权 URL)恰好一个,URL 编码
resource(令牌请求体)恰好一个,表单编码,与授权时一致
调用Authorization: Bearer <access_token>请求/mcp/agent
刷新尽量带resource;可依赖刷新兼容路径,但初始授权/换码绝不省略
排障缺参数/多参数/未知值 →invalid_target;先查两处是否都传对

7. 延伸阅读

  • MCP 暴露策略与工具白名单:了解哪些 Den API 面会成为 MCP 工具、哪些被拦截;
  • MCP 认证实现:Bearer 挑战、JWT/opaque 双路径校验、资源与作用域校验链;
  • MCP 资源派生:不同部署形态下/mcp/mcp/agent资源与元数据 URL 的生成规则;
  • OAuth 路由与参数规范化:注册、授权、换码三个阶段的resource强制校验与刷新推断逻辑;
  • 回调 URI 策略:HTTPS/loopback/私有 scheme 白名单与 PKCE 强制要求。

【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 5:34:58

AI代理技能封装:将人脉资源转化为可复用token

1. 项目背景与核心概念 "colleague-skill--将冰冷的前同事变成温暖的token"这个项目名称乍看有些抽象&#xff0c;但结合当前AI代理和技能开发的热潮&#xff0c;其实揭示了一个非常实用的场景&#xff1a;如何将过往职场中积累的人脉资源转化为可复用的数字化资产。…

作者头像 李华
网站建设 2026/9/13 5:32:28

Wand-Enhancer:一次离线补丁,免费解锁 Wand 全部增强功能

Wand-Enhancer&#xff1a;一次离线补丁&#xff0c;免费解锁 Wand 全部增强功能 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer 你的修改器&#…

作者头像 李华
网站建设 2026/9/13 5:28:52

C++代码编译为iOS Framework的完整指南

1. 项目概述&#xff1a;为什么需要将C代码编译为iOS Framework&#xff1f;在iOS开发生态中&#xff0c;Objective-C和Swift是官方推荐的语言&#xff0c;但许多高性能计算、游戏引擎或跨平台库的核心模块都是用C编写的。将C代码编译为Framework可以带来三个关键优势&#xff…

作者头像 李华
网站建设 2026/9/13 5:28:24

1.3 万条检测规则一览:Nuclei Templates 安全漏洞扫描完全指南

1.3 万条检测规则一览&#xff1a;Nuclei Templates 安全漏洞扫描完全指南 【免费下载链接】nuclei-templates Community curated list of templates for the nuclei engine to find security vulnerabilities. 项目地址: https://gitcode.com/GitHub_Trending/nu/nuclei-tem…

作者头像 李华