两个主体,一直都在
sub 永远是人,act 链承载 agent 实例和上游跳。审计行必须能读成「Alice,通过 Claude Code 会话 X,在任务 Y 中,于 T 时刻」,而不是「一个叫 GitHub 的 bearer token」。自主运行的 agent 省略 subject_token,此时 sub 是 agent 实例,问责由 sponsor claim 承担。
Architecture
这一页是给要评估我们的工程师和安全架构师看的。所有内部授权上下文都用标准 claim 名承载 —— 自造 claim 会打断你既有的 token 校验器和 SIEM 解析规则。
Token chain · RFC 8693
全链只有委派(delegation)语义,没有冒充(impersonation)。每次访问同时携带人类主体与 agent 主体两个独立身份 —— 冒充会摧毁审计链。
[1] Human -> your IdP
OIDC. The human's id/access token = Tu.
TTL ~1h -- their policy, not ours.
[2] Agent -> AgentPAM /token RFC 8693 token exchange
grant_type = urn:ietf:params:oauth:grant-type:token-exchange
subject_token = Tu <- the human (delegation, NOT impersonation)
actor_token = agent instance assertion
resource = <upstream API> RFC 8707 resource indicators
authorization_details = [{ RFC 9396 rich authorization requests
type: "agent_task",
task_id, purpose, sensitivity,
lifecycle_binding: { task_id, termination_states }
}]
--> Ts : session token. TTL 5-15 min. cnf.jkt (DPoP-bound).
claims:
sub = the human
act = { sub: agent_instance_id, act: { ...upstream hop... } }
agent_blueprint_id audit only -- never a policy input
agent_session_id, task_id, sponsor
scope already narrowed
[3] Gateway -> AgentPAM /token
subject_token = Ts
--> Tup : upstream credential. TTL 60-300s. Header-injected.
*************** THE AGENT NEVER SEES THIS ***************
[4] Sub-agent delegation
Repeat [2] with Ts as the subject_token.
scope MUST be a strict subset. Monotonic attenuation. act chain nests.sub 永远是人,act 链承载 agent 实例和上游跳。审计行必须能读成「Alice,通过 Claude Code 会话 X,在任务 Y 中,于 T 时刻」,而不是「一个叫 GitHub 的 bearer token」。自主运行的 agent 省略 subject_token,此时 sub 是 agent 实例,问责由 sponsor claim 承担。
网关会终止 TLS,这会打断 agent → 网关这一段的 mTLS 绑定。DPoP(RFC 9449)在应用层做逐请求签名,穿得过网关。禁止签发裸 bearer token:目标是让被窃的 token 变成废纸。
TTL design
| 令牌 | TTL | 绑定 | 为什么是这个数 |
|---|---|---|---|
| Tu | ~1h | 你的 IdP 决定 | 你的策略,不是我们的。我们只消费断言。 |
| Ts | 5–15 min | DPoP(cnf.jkt) | 有 DPoP 绑定 且 逐调用做 PDP 撤销检查,被窃后没有私钥不可用 —— 因此可以容忍相对长的寿命。 |
| Tup | 60–300 s | 无(上游 API 的格式) | 一份没有绑定的上游凭据,是整条链上价值最高的东西,谁拿到谁能用。所以它必须最短命,而且不落盘。 |
| Ts′ | ≤ 父 TTL | DPoP | 子委派既不能更久,也不能更宽。 |
lifecycle_binding: {task_id, termination_states[]} —— 任务一旦进入终止态,这条链上的所有凭据立刻作废,不必等 TTL 走完。这直接命中「长跑 agent 权限过大」这个每个安全团队都会提的问题。
agent 之间的提权是一个已经被标准工作组具名列出的需求,草案要求强制降权 —— 但据我们所知没有任何在售产品把它端到端实现。我们在 broker 里强制 scope(child) ⊂ scope(parent),交换时拒绝任何越界请求。
Policy
默认拒绝:未被策略显式允许的工具调用一律拒绝,禁止 fail-open。
| 级别 | 例子 | 评价 |
|---|---|---|
| Server 级 | 这个 agent 可以连 Stripe MCP server | 表驱动,简单。也是逐工具 JIT 在实践中会塌缩到的那一级 —— 这是一条可攻击的缝。 |
| Tool 级 | allow refund · deny payout | glob / regex,默认拒绝。多数在售网关到此为止。 |
| 参数级 ←差异化 | refund.amount ≤ 500 · db.query 仅允许只读 replica | RFC 9396 的 authorization_details 是正确的容器,schema 由我们定义。真正的 PAM 粒度在这里,不在「能不能连」。 |
OIDF AuthZEN 工作组的 PDP 接口。我们实现它,因此可以被注册为你既有网关的外部授权器。
据我们所知是唯一针对 MCP 工具授权的标准机构工作,把工具调用映射到 Subject-Action-Resource-Context 模型。
允许 PDP 返回「需要满足什么前置条件」(审批、同意、委派授权、证明、风险评估),而不是一个平坦的拒绝。这是把人在回路表达成标准的原语。
Human in the loop
每小时几百次弹窗,等于保证橡皮图章。任何「人在回路」的设计如果不先解决频率问题,就只是在制造合规剧场。
lifecycle_binding 的用途。Audit
传统 PAM 的会话录制在这里彻底失效,原因不只是「没有会话」,而是更根本的一条。
// A normalized agent authorization decision event. // Emitted to any SIEM (syslog / file / Kafka -- local, no cloud assumed). // Correlated to OTel gen_ai.* spans via W3C Trace Context. { human_subject : "[email protected]", // who is accountable agent_identity : "ai-000-3f9c", // which registered instance agent_blueprint : "bp-claude-code-2.x", // what kind of agent sponsor : "[email protected]", // mandatory human guarantor task : { id, purpose, sensitivity }, tool : "stripe.refund", resource : "https://api.stripe.com", decision : "deny", obligations : ["step_up_ciba"], // not a flat refusal delegation_chain : [ human -> agent -> sub_agent ], binding : { method: "dpop", jkt }, traceparent : "00-4bf92f...-00f067...-01", // W3C Trace Context ts : "2026-07-21T09:14:02.118Z" }
Gateway compatibility
下表是 2026-07 实测结果:12 家网关,源码审读 + agentgateway 运行时执行。Q1「干净支持外部 AS 委托」只有 4 家。 我们此前写过「任何合规网关本来就能做」—— 那是错的,已删除。
| 网关 | 主路径 / 外部 AS 委托 | 并列主路径 / 外部 PDP(可改 header) | stdio | 自托管 / 气隙 |
|---|---|---|---|---|
| Solo.io agentgateway | ✓✓ 运行时验证 | ✓ 支持 extAuthz http+grpc | ✓ 支持 原生 | ✓ 支持 |
| IBM ContextForge | ✓ 支持 逐 server,任意 URL | ✓ 支持 OPA/Cedar,可改 header | ✕ 不支持 需 translate sidecar | ✓ 支持 |
| Kong AI Gateway | ✓ 支持 仅企业版 | ✓ 支持 企业版 opa | ✕ 不支持 | ⚠ 有条件 |
| AWS Bedrock AgentCore | ✓ 支持 discoveryUrl 模式为 .+ | ⚠ 有条件 interceptor,仅限 Lambda | ✕ 不支持 端点须 https:// | ✕ 不支持 AWS only |
| Envoy / Istio | ⚠ 有条件 任意 issuer 可,PRM 需自建静态文件 | ✓ 支持 业界最佳 ext_authz | ✕ 不支持 | ✓ 支持 |
| Azure APIM | ⚠ 有条件 任意 issuer 可,PRM 手写 | ⚠ 有条件 DIY,有 fail-open 风险 | ✕ 不支持 | ⚠ 有条件 需连 Azure |
| Pomerium | ✕ 不支持 硬编码 r.Host | ✕ 不支持 它自己就是 PDP | ✕ 不支持 | ✓ 支持 |
| Obot | ✕ 不支持 硬编码 h.baseURL | ✓ 支持 webhook,可改 body | ✓ 支持 容器包装 | ✓ 支持 |
| MintMCP | ✕ 不支持 MintMCP 自任 AS | ⚠ 有条件 沙箱 JS,非 PDP 回调 | ✓ 支持 | ✕ 不支持 SaaS |
| Cloudflare MCP Portals | ✕ 不支持 Access 恒为 AS | ✕ 不支持 | ✕ 不支持 明确不支持 | ✕ 不支持 SaaS |
| Docker MCP Gateway | ✕ 不支持 仅客户端发现 | ⚠ 有条件 before:http: 不能改 header | ✓ 支持 原生 | ⚠ 有条件 |
| Traefik | ✕ 不支持 OSS 版无 JWT 中间件 | ✓ 支持 ForwardAuth | ✕ 不支持 | ✓ 支持 |
并列主路径(外部 PDP)的覆盖面远大于外部 AS 委托,而且它很健康 —— Envoy/Istio 的 ext_authz 是业界最佳,加上 Kong OPA、ContextForge 插件、Obot webhook、agentgateway extAuthz。除 Obot 外全部支持 allow 时改写 header —— 那正是凭据注入的原语。所以诚实的说法是「两条可行路径,其中一条覆盖大多数网关」,而不是「规范逼着所有人来找我们」。
mcpAuthentication 配置块 —— 网关随即把我们作为它的 authorization server 公布出去,并签发了正确的 RFC 9728 challenge。零代码、零 SDK、一个配置块。 这是 12 家里唯一一家我们能在本机原生执行的,所以也是唯一一条被运行时证明的路径 —— 其余均为源码审读结论。
Roadmap
超过一半的 CISO 数不清自己环境里有多少 agent。没人会为一个数不清的群体买执法。
| 阶段 | 目标 | 关键交付 | 验证标准 |
|---|---|---|---|
| V1 楔子 | 发现与归属 | Collector + Portal 只读视图 + 审计师就绪报告 | 一条 docker run 出报告,全程无出站访问;产出「N 个 agent、各持什么凭据、上周碰了什么、哪些在 SOC 2 CC6 下不可归属」 |
| V1.5 执法 | Tier 0 + Tier 1 | PDP + 凭据 Broker + 主路径集成 | 凭据不再落到 agent;逐工具授权生效;P99 延迟 < 200ms |
| V2 护城河 | 本地证明 + 跨云 | Local Attestor + CAEP 闭环 + 多 IdP | 进程证明可用;撤销延迟 ≤ 一个 Ts TTL |
| V2+ 企业版 | Tier 3 | eBPF / Endpoint Security Framework 执行 | 回答「非 MCP 动作」竞标攻击 |