Skip to content

🚧 Roadmap — 本页描述的能力尚在路线图中,概念与设计已定型,接口与操作步骤以正式发布为准。

保护你的 API:资源侧集成

前面几页讲的都是"怎么把权限安全地交给 Agent"。这一页讲最后一跳,也是最容易被忽略的一跳:令牌到了你的 API,你怎么校验它。

这一跳没做对,前面所有的缩权、审批、审计都会失效——因为最终决定"放不放行"的是你的资源服务,不是签发方。

读完本指南,你将知道:

  • 五步校验清单,缺任何一步会漏什么
  • 为什么必须显式判断 act.type,不能按 RFC 8693 标准形状解析
  • 怎么让你的业务日志成为审计链的最后一环

前置条件

  • 已理解访问令牌的字段结构(见 Token 与 Claim 参考
  • 你的 API 已有 JWT 校验能力(验签、过期判断)

五步校验清单

步骤校验什么漏掉会怎样
1验签与有效期伪造或过期令牌被接受
2aud 是你的资源发给其他资源的令牌能打你的 API(受众混淆)
3sub 做业务鉴权把 Agent 当成超级用户,绕过你原有的数据权限
4act 识别行动者并记录审计里分不清本人操作与 Agent 代操作,无法追责
5校验 scope 覆盖本次操作只读授权被用来写入——缩权彻底失效

第 3 步是最容易犯的错

很多团队在集成时把"令牌有效"等同于"可以做这件事",于是 Agent 拿着一枚只读令牌照样走通了写接口——因为业务鉴权那一层被跳过了。

正确的做法:把 sub 当作发起人喂进你原有的鉴权逻辑。 Agent 的权限上界是委托人本人的权限,你原有的数据权限规则一条都不能省。scope 是额外的收窄,不是替代品。

伪代码

typescript
async function authorizeAgentRequest(req: Request, requiredScope: string) {
  const token = readBearer(req);

  // ① 验签与有效期(用你现有的 JWT 校验库)
  const claims = await verifyJwt(token);

  // ② 受众必须是你的资源
  if (!audienceMatches(claims.aud, MY_RESOURCE_ID)) {
    throw forbidden("audience_mismatch");
  }

  // ③ sub 是被代表的人 —— 喂进你原有的业务鉴权
  const actingFor = claims.sub;
  await assertBusinessPermission(actingFor, req.action, req.resource);

  // ④ act 识别实际行动者
  //    注意:GenAuth 的 act 是带 type 判别字段的扩展结构,
  //    不是 RFC 8693 标准的嵌套 sub 形式 —— 必须显式判断 type
  let agentId: string | null = null;
  if (claims.act && claims.act.type === "eak_delegation") {
    agentId = claims.act.agent_id;
  }
  // act 缺失或 type 不匹配时:按你的策略决定是拒绝,还是当作"非 Agent 调用"处理。
  // 建议对 Agent 专用接口 fail-closed(拒绝),避免结构变化导致校验被静默跳过。

  // ⑤ scope 必须覆盖本次操作
  const scopes = normalizeScopes(claims.scope);
  if (!scopes.includes(requiredScope)) {
    throw forbidden("insufficient_scope");
  }

  // 审计接缝:这三个字段让链条从 GenAuth 延伸到你的业务日志
  logger.info("agent_request", {
    sub: actingFor,
    agent_id: agentId,
    audit_id: claims.audit_id,
    grant_id: claims.grant_id,
    action: req.action,
  });

  return { actingFor, agentId };
}

act 的结构为什么要特别处理

RFC 8693 定义的 act 是嵌套 sub 形式({"act": {"sub": "..."}});GenAuth 的 act{"type": "eak_delegation", "agent_id", "grant_id", "access_key_id"}。如果你用现成的 RFC 8693 库直接解析,会取不到值且不报错——这正是最危险的情况:校验看起来通过了,实际什么都没校验。 显式判断 type 是唯一安全的做法。

留一条应急拦截能力

已签发的委托令牌在有效期内无法被单枚作废(见 吊销与应急处置)。因此建议在资源侧预留一个拒绝列表能力:

  • act.agent_id 拒绝:某个 Agent 出问题时,一键停掉它对你 API 的全部访问
  • grant_id 拒绝:某次授权被判定为误授权时,精确掐断这一次

实现可以很简单——一份可热更新的配置或一个 Redis 集合,在第 ④ 步之后检查。平时不用,出事时它是你唯一能在有效期内截断的手段。

网关形态

如果你的架构里有统一 API 网关,把这五步校验放到网关层比放到每个服务里更划算:

  • 校验逻辑一处维护,新服务接入零成本
  • 拒绝列表在网关上生效,止损速度快
  • 网关把 sub / agent_id / audit_id 透传给下游服务(如放进内部头),下游只做业务逻辑

网关插件形态在规划中;在此之前用网关的通用 JWT/脚本能力即可实现上述逻辑。

常见问题

我的 API 已经支持 OAuth,还需要改吗? 需要改的是第 ③ ④ ⑤ 步的语义:以前 sub 就是调用方,现在 sub 是被代表的人、act 才是调用方。鉴权要按 sub 做、日志要按 act 记。

scope 名字要和我的接口一一对应吗? 不需要一一对应,但要能映射。建议按"资源 + 动作"设计(如 orders:read),并在接口层声明所需 scope,而不是在代码里散落判断。

能不能只信 GenAuth 的校验,我这边不校验? 不能。签发方只能保证"这枚令牌是我发的、内容没被改",只有你知道这次请求要动哪条数据。资源侧校验不可外包。

下一步