🧪 Beta — 能力已可用,接口契约可能微调。
让 Agent 代表用户调用你的 API
读完本指南,你将能够:
- 让 Agent 以"代表某个具体用户"的身份调用你自己的 API,而不是共享一个服务账号
- 在你的 API 侧准确知道是谁授权的、由哪个 Agent 在执行、允许做什么
- 让每一次这样的调用都进审计链,事后可追溯
前置条件
- 已完成 第一次委托:30 分钟跑通(本指南是它的生产化版本)
- 你的 API 已经或即将支持校验 GenAuth 签发的访问令牌(校验清单见 保护你的 API)
- 访问密钥保存在服务端环境变量中
完整链路
四方分工先看清楚:
| 责任方 | 在这条链路里做什么 |
|---|---|
| 你的应用 | 发起委托、处理回调、兑换访问令牌、把令牌交给 Agent |
| 用户 | 在授权确认页做出决定 |
| GenAuth | 校验、签发委托令牌、执行令牌兑换 |
| Agent | 持访问令牌调用你的 API |
步骤 1 · 发起委托(你的应用 → GenAuth)
import { Qoni, AnimaScopes } from "@eazo/anima";
const anima = new Qoni({
host: "https://api.eak.eazo.ai",
accessKey: process.env.ANIMA_ACCESS_KEY!,
secretKey: process.env.ANIMA_SECRET_KEY!,
});
const { data } = await anima.delegateToken({
mode: "interactive",
agent: "report-agent",
scopes: [AnimaScopes.WEB_SEARCH_RUN, AnimaScopes.WEB_SEARCH_READ],
redirectUri: "https://yourapp.example.com/eak/callback",
state: signedState, // 建议放签名后的业务上下文
user: { id: currentUser.id },
expiresIn: 7200, // 秒,60–86400。按任务时长给,别给上限
});
redirect(data.authorizationUrl);scope 怎么选:按当前任务的最小集合。能用 read 就不要申请 run;能用单个 scope 就不要用组合包。用户在授权页看到的就是你申请的这一串——申请得越宽,用户越犹豫。
步骤 2 · 用户确认(用户 → GenAuth)
用户在授权确认页看到 Agent、范围、期限,做出选择。你不需要做任何事,但要处理两种回调结果:同意(带 code)与拒绝或超时。
state 必须校验
回调回来的 state 必须与你发起时的值一致,否则拒绝处理。这是防授权劫持的基本功。
步骤 3 · 兑换委托令牌(你的应用 → GenAuth)
// GET /eak/callback?grantId=...&code=...&state=...
verifySignedState(query.state); // 先验 state
const { data: grant } = await anima.completeDelegateToken({
grantId: query.grantId,
code: query.code,
state: query.state,
});
// grant.token / grant.expiresIn / grant.grantId / grant.auditId / grant.grantedScopes拿到的 grantedScopes 可能小于你申请的 scopes——以实际授予为准,不要假设申请必得。
步骤 4 · 兑换访问令牌(Agent 或你的应用 → GenAuth)
委托令牌不能直接调资源,它的 aud 只认令牌兑换端点。换出目标资源的访问令牌:
// 通过 SDK 的原始请求通道调用令牌兑换端点
const { data: exchanged } = await anima.request<{
token: string;
tokenType: string;
expiresIn?: number;
}>({
method: "POST",
path: "/api/v3/eak/token-exchange",
body: {
subjectToken: grant.token,
resource: "webagent",
scopes: [AnimaScopes.WEB_SEARCH_RUN],
},
});
// exchanged.token / exchanged.tokenType / exchanged.expiresIncurl -X POST https://api.eak.eazo.ai/api/v3/eak/token-exchange \
-H "Authorization: <AK/SK 签名>" \
-H "Content-Type: application/json" \
-d '{
"subjectToken": "<委托令牌>",
"resource": "webagent",
"scopes": ["webagent.web_search:run"]
}'兑换请求的 scopes 必须是委托令牌已授 scope 的子集——这是缩权规则的强制执行点(原理)。
步骤 5 · 调用你的 API(Agent → 你的 API)
Agent 把访问令牌放进 Authorization: Bearer <token> 调用你的 API。你的 API 侧要做的校验:
- 验签
- 校验
aud是你的资源 - 读
sub得到被代表的用户,据此做业务鉴权 - 读
act(act.type === "eak_delegation")得到执行的 Agent,记入日志 - 校验 scope 覆盖本次操作
完整清单与伪代码见 保护你的 API;令牌字段结构见 Token 与 Claim 参考。
验证
三个检查点,逐个确认:
// ① 委托令牌确实有效,且授权事实与预期一致
const { data: info } = await anima.genauth.introspectDelegationToken({ token: grant.token });
console.assert(info.active === true);
console.assert(info.sub === currentUser.id);
console.assert(info.agent_id === "report-agent");② 越权应当失败:用只读 scope 的令牌去调写接口,你的 API 必须拒绝。这条不通过,说明资源侧校验没生效。
③ 审计链完整:拿 grant.auditId 查审计,应看到授权、兑换、访问三类事件(见 审计与追责链)。
常见问题
能跳过用户确认吗? 可以走组织级授权确认(受信服务端集成),但那是另一种授权主体,有额外的加固要求——先读 Consent 与审批 与 安全考量。
委托令牌能缓存复用吗? 可以在有效期内复用,但要按用户隔离存放,并在 token.expired 错误时重新发起委托。不要跨用户复用——那等于打破委托边界。
用户中途撤销了怎么办? 下一次调用会失败并返回相应错误码。你的应用应捕获 AnimaPermissionDeniedError / AnimaTokenExpiredError 并引导用户重新授权(错误类型见 SDK Reference)。
下一步
- 保护你的 API:资源侧集成 —— 令牌到了你的 API 之后怎么校验。
- 吊销与应急处置 —— 需要提前收权时怎么做。
- Delegate Token 与缩权 —— 这条链路背后的规则。