🚧 Roadmap — 本页描述的能力尚在路线图中,概念与设计已定型,接口与操作步骤以正式发布为准。
保护你的 API:资源侧集成
前面几页讲的都是"怎么把权限安全地交给 Agent"。这一页讲最后一跳,也是最容易被忽略的一跳:令牌到了你的 API,你怎么校验它。
这一跳没做对,前面所有的缩权、审批、审计都会失效——因为最终决定"放不放行"的是你的资源服务,不是签发方。
读完本指南,你将知道:
- 五步校验清单,缺任何一步会漏什么
- 为什么必须显式判断
act.type,不能按 RFC 8693 标准形状解析 - 怎么让你的业务日志成为审计链的最后一环
前置条件
- 已理解访问令牌的字段结构(见 Token 与 Claim 参考)
- 你的 API 已有 JWT 校验能力(验签、过期判断)
五步校验清单
| 步骤 | 校验什么 | 漏掉会怎样 |
|---|---|---|
| 1 | 验签与有效期 | 伪造或过期令牌被接受 |
| 2 | aud 是你的资源 | 发给其他资源的令牌能打你的 API(受众混淆) |
| 3 | 读 sub 做业务鉴权 | 把 Agent 当成超级用户,绕过你原有的数据权限 |
| 4 | 读 act 识别行动者并记录 | 审计里分不清本人操作与 Agent 代操作,无法追责 |
| 5 | 校验 scope 覆盖本次操作 | 只读授权被用来写入——缩权彻底失效 |
第 3 步是最容易犯的错
很多团队在集成时把"令牌有效"等同于"可以做这件事",于是 Agent 拿着一枚只读令牌照样走通了写接口——因为业务鉴权那一层被跳过了。
正确的做法:把 sub 当作发起人喂进你原有的鉴权逻辑。 Agent 的权限上界是委托人本人的权限,你原有的数据权限规则一条都不能省。scope 是额外的收窄,不是替代品。
伪代码
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 的校验,我这边不校验? 不能。签发方只能保证"这枚令牌是我发的、内容没被改",只有你知道这次请求要动哪条数据。资源侧校验不可外包。
下一步
- Token 与 Claim 参考 —— 字段的权威定义。
- 吊销与应急处置 —— 拒绝列表在应急流程里的位置。
- 安全考量 —— 受众混淆与令牌透传的完整威胁分析。