Skip to content

🧪 Beta — 能力已可用,接口契约可能微调。

让 Agent 代表用户调用你的 API

读完本指南,你将能够:

  • 让 Agent 以"代表某个具体用户"的身份调用你自己的 API,而不是共享一个服务账号
  • 在你的 API 侧准确知道是谁授权的、由哪个 Agent 在执行、允许做什么
  • 让每一次这样的调用都进审计链,事后可追溯

前置条件

完整链路

四方分工先看清楚:

责任方在这条链路里做什么
你的应用发起委托、处理回调、兑换访问令牌、把令牌交给 Agent
用户在授权确认页做出决定
GenAuth校验、签发委托令牌、执行令牌兑换
Agent持访问令牌调用你的 API

步骤 1 · 发起委托(你的应用 → GenAuth)

typescript
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)

typescript
// 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 只认令牌兑换端点。换出目标资源的访问令牌:

typescript
// 通过 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.expiresIn
bash
curl -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 侧要做的校验:

  1. 验签
  2. 校验 aud 是你的资源
  3. sub 得到被代表的用户,据此做业务鉴权
  4. actact.type === "eak_delegation")得到执行的 Agent,记入日志
  5. 校验 scope 覆盖本次操作

完整清单与伪代码见 保护你的 API;令牌字段结构见 Token 与 Claim 参考

验证

三个检查点,逐个确认:

typescript
// ① 委托令牌确实有效,且授权事实与预期一致
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)。

下一步