第三方接入认证指南
读者画像:第三方公司的工程师,初次接触外脑(WaiNao.AI)API。本指南帮你回答四个问题——"我的场景该用哪种 Token?怎么申请?怎么写第一行代码?Token 泄漏怎么办?"
与其他文档的关系
- 本文档:接入引导视角——第三方"该选哪种 Token、怎么走完接入流程"。
/dev/token-types:实现视角——仓内开发者写新端点时挑认证中间件、看 401/403/429 区分。/api/authentication:端点参考视角——每个端点接受哪种Authorization。/guide/oauth+/api/oauth:OAuth 教程与字段参考。
三者互补,不重复。OAuth 字段细节、端点列表请直接看上面三篇,本指南只负责"决策树 + 申请流程 + 第一行代码"。
1. 接入决策树
外脑对外暴露 4 种凭证(Token),第三方接入只会用到其中 3 种(API Key / OAuth / Agent Token);第 4 种 Supabase JWT 仅用于浏览器登录态,不在本指南范围。
你的接入场景是?
├── 我有一个第三方系统,用我自己的账户调外脑 API
│ → API Key(前缀 wn-)。流程见 §2
├── 我有一个第三方应用,让我的用户授权后由我代用户调
│ → OAuth 2.0(前缀 wno-)。流程见 §3
├── 我在写长寿命服务 / 桌面 Agent / 设备程序
│ → Agent Token(前缀 wna-)。流程见 §4
└── 我只是在浏览器里测试
→ Supabase JWT,浏览器登录后自动持有,不在本指南范围| 选型维度 | API Key | OAuth 2.0 | Agent Token |
|---|---|---|---|
| 代谁调 | 代调用方自己 | 代用户(用户授权过) | 代 Agent 所属团队 |
| 前缀 | wn- | wno- | wna- |
| 谁创建 | Team Owner(团队设置页) | 站点管理员审核(无自助注册) | Team Owner(Agent 管理页) |
| 过期机制 | 中间件支持过期检查,但创建路由当前不开放 expires_at 配置 | Access Token 短期 + Refresh Token 续期 | 永久有效(除非主动吊销) |
| 撤销 | 团队设置中删除 | /oauth/revoke 或管理员禁用/删除 OAuth App | Agent 管理页吊销 |
| 典型场景 | 第三方系统接 API、CI/CD、爬虫 | SaaS 集成、用户登录"使用 WaiNao 账号" | 长跑 Worker、桌面 Agent、IoT 设备 |
| 额度模型 | usage_credits / limit_credits(按 Key 计) | 按用户额度 | 按 Agent 所属团队额度 |
不确定?把"我代谁调"想清楚就够了:代你自己 → API Key;代你的用户 → OAuth;代 Agent 自治 → Agent Token。
2. API Key 申请与使用
申请流程
API Key 由 Team Owner 在团队设置中创建,不是站点管理员后台。
- 登录编辑器 → 进入团队 → 团队设置 → API Keys 页面(编辑器路径:
/o/:teamSlug/settings/api-keys,是编辑器内部产品路径,不是 docs-site 链接) - 点击"创建 API Key",填写名称、描述、限额(
limitCredits,单位为毫积分;不填则不限额) - 后端返回明文 token(前缀
wn-),只在创建时返回一次——立即保存到密钥管理器或环境变量
创建路由当前不开放 expires_at
认证中间件支持 expires_at 过期检查(参考 apps/backend/src/middleware/authenticate-api-key.ts:130-143),但 当前创建路由(POST /teams/:teamId/api-keys,参考 apps/backend/src/routes/teams.ts:246-346)只暴露 name / description / limitCredits 三个字段,不开放 expires_at 配置。如需"短期 Token"语义,请改用 OAuth Access Token,或手动定期轮换 API Key。
可调用端点范围
API Key 可调用的端点列表请见 /api/authentication 中的『API Key 认证』章节(不写死 anchor,VitePress 自动生成的锚点含数字前缀,链接锚点容易失效)。常用端点包括 /my、/api/released-app/*、ROMP 会话/消息等。
注意 JWT-only 端点
部分端点(例如 /runs 列表与详情)仅接受 Supabase JWT,不接受 API Key——API Key 调用会返回 403。具体哪个端点接受哪种 Token,永远以 /api/authentication 为准,不要按"看起来该可以"猜测。
第一行代码
调 GET /my 验证 Token 有效(API Key 可访问该端点):
curl -H "Authorization: Bearer wn-xxxxxxxx" \
https://block2-api.wainao.chat/myconst res = await fetch('https://block2-api.wainao.chat/my', {
headers: { Authorization: 'Bearer wn-xxxxxxxx' },
})
const me = await res.json()
console.log(me)import requests
res = requests.get(
'https://block2-api.wainao.chat/my',
headers={'Authorization': 'Bearer wn-xxxxxxxx'},
)
print(res.json())3. OAuth 2.0 接入
一句话决策
如果你需要 代用户调用 而不是 代你自己调用,用 OAuth。
例如你做了一个 SaaS 工具,希望用户在你的产品中"用 WaiNao 账号登录",并由你的服务端代用户访问其在 WaiNao 上的资源,就该走 OAuth。
时序图
第三方应用 用户浏览器 外脑 (WaiNao)
│ │ │
│ 1. 跳转 /oauth/authorize │ │
│ ───────────────────────────> │ │
│ │ 2. 显示授权页 │
│ │ <───────────────────────── │
│ │ 3. 用户点同意 │
│ │ ─────────────────────────> │
│ │ 4. 重定向到 redirect_uri │
│ │ <───────────────────────── │
│ 5. 收到 ?code=xxx │ │
│ <─────────────────────────── │ │
│ │
│ 6. POST /oauth/token (code → access_token + refresh_token)│
│ ──────────────────────────────────────────────────────────>│
│ <──────────────────────────────────────────────────────────│
│ │
│ 7. 用 wno- token 调 API │
│ ──────────────────────────────────────────────────────────>│
│ │
│ 8. (Token 过期) POST /oauth/token (refresh_token) │
│ ──────────────────────────────────────────────────────────>│
│ <──────────────────────────────────────────────────────────│接入文档导航
完整接入流程与 API 字段细节请直接查阅以下文档(不在本指南重复):
- 完整接入教程 →
/guide/oauth - API 端点字段参考 →
/api/oauth - 外脑自身 OAuth Callback 页面 →
/oauth/callback(仅当你做"反向 OAuth"——让外脑作为客户端去回调外部 OAuth 提供方时才需要,普通接入不涉及)
OAuth App 创建
目前没有自助注册 API
当前站点管理员在管理后台创建/审核 OAuth App,暂无对外自助注册接口。申请方需联系站点管理员获取 client_id。
外脑 OAuth App 是 Public Client + PKCE:不会发放 client_secret,授权流程必须使用 PKCE(code_verifier / code_challenge)。如果你看到任何"开发者后台/自助申请页"或"获取 client_secret"的说法,那是计划中而未上线 / 不适用本平台的功能,请以本文为准。
第一行代码
获取 access_token 后调 GET /oauth/userinfo 验证(需要 token 持有 profile:read scope,否则返回 insufficient_scope;外脑当前仅支持 profile:read 与 chat:completions 两个 scope,不使用 OpenID 标准 scope):
curl -H "Authorization: Bearer wno-xxxxxxxx" \
https://block2-api.wainao.chat/oauth/userinfoconst res = await fetch('https://block2-api.wainao.chat/oauth/userinfo', {
headers: { Authorization: 'Bearer wno-xxxxxxxx' },
})
const userinfo = await res.json()
console.log(userinfo)import requests
res = requests.get(
'https://block2-api.wainao.chat/oauth/userinfo',
headers={'Authorization': 'Bearer wno-xxxxxxxx'},
)
print(res.json())4. Agent Token 接入
申请流程
Agent Token 适合"长寿命服务 / 桌面 Agent / 设备程序"——需要永久有效、不需要 refresh、可以放在配置文件中的场景。
- 登录编辑器 → 进入团队 → Agent 管理页(编辑器路径:
/o/:teamSlug/agents,是编辑器内部产品路径,不是 docs-site 链接) - Team Owner 创建 Agent:填写名称、描述、绑定项目权限
- 签发 Token:后端返回明文 Token(前缀
wna-),只在签发时返回一次
行为特点
- 永久有效:除非主动吊销,否则不过期,没有 refresh 概念
- 撤销机制:在团队 Agent 管理页面"吊销 Token"——保留 Agent 配置,仅将后端的
token_hash置为null,被吊销后该 Token 立即失效 - 额度归属:调用消耗 Agent 所属团队的积分,不消耗个人额度
- 使用建议:写在配置文件 / 环境变量中,明确告知运维"明文只能拿一次,丢了只能吊销重发"
第一行代码
Agent Token 的典型使用场景是调"已发布应用的运行时端点"。前提:你的团队对该已发布应用有访问权限,且你有应用的 alias 与 version:
curl -X POST \
-H "Authorization: Bearer wna-xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"input": "hello"}' \
https://block2-api.wainao.chat/api/released-app/r/<alias>/<version>/<endpoint>const res = await fetch(
'https://block2-api.wainao.chat/api/released-app/r/<alias>/<version>/<endpoint>',
{
method: 'POST',
headers: {
Authorization: 'Bearer wna-xxxxxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({ input: 'hello' }),
},
)
console.log(await res.json())import requests
res = requests.post(
'https://block2-api.wainao.chat/api/released-app/r/<alias>/<version>/<endpoint>',
headers={
'Authorization': 'Bearer wna-xxxxxxxx',
'Content-Type': 'application/json',
},
json={'input': 'hello'},
)
print(res.json())注意:
/my端点不接受 Agent Token,因为 Agent 不是"个人用户",没有个人 profile。要拿 Agent 自身信息请调 Agent 管理 API。
5. 代码示例(实战对比)
如果你想让三类 Token 调同一个端点对比行为,请用 /api/released-app/* 系列运行时端点——它们同时支持 API Key、OAuth、Agent Token(前提是有发布应用 + 对应的权限/scope)。当前同时挂在该认证链上的运行时端点包括:
POST /api/released-app/:deploymentId/runPOST /api/released-app/d/:documentId/runALL /api/released-app/r/:alias/:version/:path(本文以下示例统一用此别名版本路径形式)
/my 端点只接受 API Key 与 Supabase JWT(不接受 OAuth/Agent);/oauth/userinfo 只接受 OAuth Token。下面 9 段示例按"端点合法性"挑选。
5.1 API Key 调 /my
curl -H "Authorization: Bearer wn-xxxxxxxx" \
https://block2-api.wainao.chat/myconst res = await fetch('https://block2-api.wainao.chat/my', {
headers: { Authorization: 'Bearer wn-xxxxxxxx' },
})
console.log(await res.json())import requests
res = requests.get(
'https://block2-api.wainao.chat/my',
headers={'Authorization': 'Bearer wn-xxxxxxxx'},
)
print(res.json())5.2 Agent Token 调已发布应用
curl -X POST \
-H "Authorization: Bearer wna-xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"input": "hello"}' \
https://block2-api.wainao.chat/api/released-app/r/<alias>/<version>/<endpoint>const res = await fetch(
'https://block2-api.wainao.chat/api/released-app/r/<alias>/<version>/<endpoint>',
{
method: 'POST',
headers: {
Authorization: 'Bearer wna-xxxxxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({ input: 'hello' }),
},
)
console.log(await res.json())import requests
res = requests.post(
'https://block2-api.wainao.chat/api/released-app/r/<alias>/<version>/<endpoint>',
headers={
'Authorization': 'Bearer wna-xxxxxxxx',
'Content-Type': 'application/json',
},
json={'input': 'hello'},
)
print(res.json())5.3 OAuth Token 调 /oauth/userinfo
curl -H "Authorization: Bearer wno-xxxxxxxx" \
https://block2-api.wainao.chat/oauth/userinfoconst res = await fetch('https://block2-api.wainao.chat/oauth/userinfo', {
headers: { Authorization: 'Bearer wno-xxxxxxxx' },
})
console.log(await res.json())import requests
res = requests.get(
'https://block2-api.wainao.chat/oauth/userinfo',
headers={'Authorization': 'Bearer wno-xxxxxxxx'},
)
print(res.json())6. 常见问题(FAQ)
Q1: Token 泄漏怎么办?
立即撤销 + 检查日志两步走。
撤销入口(这些是编辑器内部产品路径,不是 docs-site 链接,复制到编辑器域名访问即可):
- 当前可用:
- API Key → 编辑器内
/o/:teamSlug/settings/api-keys - Agent Token → 编辑器内
/o/:teamSlug/agents("吊销 Token")
- API Key → 编辑器内
- F3(凭证管理 UI)上线后:
- 三类凭证聚合页 → 编辑器内
/o/:teamSlug/settings/credentials
- 三类凭证聚合页 → 编辑器内
撤销后立即检查后端日志,确认是否有未授权调用,必要时联系运维拉取 IP / UA 维度的访问统计。
Q2: 如何撤销?
按 Token 类型选择对应入口(路径不一致,不要套用同一个端点):
| Token 类型 | 撤销方式 | 入口 |
|---|---|---|
| API Key | 编辑器 UI(当前由前端调 Supabase RPC delete_api_key,没有公开 REST 端点) | 编辑器内 /o/:teamSlug/settings/api-keys |
| Agent Token | 编辑器 UI 或后端 REST 端点 | 编辑器内 /o/:teamSlug/agents("吊销 Token");后端 POST /teams/:teamId/agents/:bindingId/revoke-token(实现见 apps/backend/src/routes/team-agents.ts:730+) |
| OAuth Access / Refresh Token | RFC 7009 撤销端点 | POST /oauth/revoke(详见 /api/oauth) |
| OAuth App 整体 | 站点管理员后台禁用 / 删除 OAuth App,所有发给该 client 的 Token 立即失效 | 站点管理员操作 |
Q3: 我收到 4xx 错误,是额度耗尽还是限流?
403 与 429 含义完全不同,不要混淆:
| 状态码 | 含义 | 触发条件 | 自愈方式 |
|---|---|---|---|
403 API key credit limit exceeded | 额度耗尽(按月/总量) | usage_credits >= limit_credits | 提高 limit_credits 或等下个计费周期 |
429 Too Many Requests | 频率限流(短时间内 QPS 过高) | 触发 X-RateLimit-* headers 中的窗口阈值 | 客户端做指数退避重试,降低 QPS |
判别要点:
403:响应体里能看到credit limit exceeded关键字,说明你这个月(或这个 Key 的限额)用光了——这是用量问题,不是网络问题,重试也没用。429:响应头里能看到X-RateLimit-Limit/X-RateLimit-Remaining/X-RateLimit-Reset,说明你刚才发得太快,按Retry-After或X-RateLimit-Reset等待后重试即可。
Q4: API Key vs OAuth,到底选哪个?
回到 §1 的决策树:
代调用方自己 ────→ API Key
代第三方用户 ────→ OAuth
代自治 Agent ────→ Agent Token如果你的产品里"用户必须先登录 WaiNao",那一定是 OAuth;如果"用户不知道你在背后调 WaiNao",那一定是 API Key。两者不可互换——OAuth Token 不能调 /my(属主是 OAuth User,不是 API Key 持有者),API Key 也不能代替你的用户的额度。
Q5: 一个 Team 可以同时持有多种 Token 吗?
可以。同一个 Team 可以有:
- 多个 API Key(按用途区分,比如
cron、ci、dev) - 多个 Agent(每个 Agent 一个 Token)
- 任意数量的 OAuth Client(如果该 Team 是 OAuth 应用的所有者)
每种 Token 独立计费、独立撤销,互不影响。
Q6: 测试环境怎么办?
外脑当前没有公开的"沙箱环境"。建议你在自己的 Team 下创建一个 dev API Key 设置较低的 limitCredits(比如 1000 毫积分 = 1 积分),用完后自然报 403,不会越权消耗主额度。
下一步
- 想看每个端点接受哪种 Token:
/api/authentication - 想了解后端实现 / 写新端点的认证选型:
/dev/token-types - OAuth 完整教程:
/guide/oauth - OAuth API 字段参考:
/api/oauth