AuthKeystone IDaaS 下游接入指南
AuthKeystone 是 OIDC 身份提供方(IDaaS),下游业务服务(ExoMind/wechat/…)通过标准 OIDC 接入。 本文档是下游接入的契约与规范——照着做,身份层零踩坑。定稿 2026-07-11。
1. 四层身份模型(务必理解)
AuthKeystone 的身份架构是四层正交,下游接入前必须分清:
| 层 | 字段 | 语义 | 下游怎么用 |
|---|---|---|---|
| 身份(Who) | sub | 用户唯一稳定标识(= user_id,跨改名/换组织不变) | 用户主键:个人知识库、配额、偏好、会话——所有「按用户隔离」的场景都用它 |
| 组织(Whose) | org_id | 组织容器(个人/团队/企业) | 仅团队/企业数据隔离;个人场景不需要。绝不用 org_id 当用户身份 |
| 权限(What) | permissions | RBAC 权限列表 | 授权判断(或调 auth check_permission) |
| 客户端(Which) | aud | 目标业务(client_id) | 验 token 时校验 aud == 自己的 client_id,防 token 跨业务误用 |
铁律:sub = 用户身份主键,org_id = 组织容器。两者正交,绝不混用。
- ❌ 错误:用 org_id 做个人知识库 key(org_id=‘default’ 对所有 default 用户一样 → 串用户)
- ✅ 正确:用 sub 做个人知识库 key(每人唯一),org_id 仅做团队空间隔离
2. 接入流程
2.1 在 auth 注册 OAuth client
平台管理员在 AuthKeystone 后台为你的服务创建 OAuth client,得到:
client_id/client_secret(机密客户端)或仅client_id(公开客户端,前端 SPA,强制 PKCE)redirect_uri(你的回调地址)grant_types(authorization_code / password / client_credentials / refresh_token)
2.2 声明用哪些 scope(字段契约)
在 AuthKeystone 后台「OAuth 客户端 → Scope 配置」勾选你的服务需要的 scope。每个 scope 对应一组 claim(token 字段):
| scope | 暴露的 claim | 谁该要 |
|---|---|---|
openid(默认,所有 client) | sub | 所有 |
profile | preferred_username, name | 要展示用户名的(picture 头像字段规划中,暂不签发) |
email | email, email_verified | 要邮箱的 |
auth:permissions | permissions[] | 要做 RBAC 授权的 |
auth:org | org_id | 要做团队/企业隔离的(个人场景不要) |
业务自定义(如 exomind:wiki) | 业务约定 | 特定业务,向 SaaS 申请 |
不声明的 scope → token 里没有对应 claim(最小暴露)。例如只要 openid + profile → token 只有 sub + preferred_username,拿不到 org_id/email/permissions。
2.3 OIDC 授权码流程(Web 应用,推荐)
用户访问你的服务
→ 你重定向到 AuthKeystone 的 /authorize:
https://auth.ai-as.cc/authorize?
client_id=你的client_id
&redirect_uri=你的回调
&response_type=code
&scope=openid+profile+email(你需要的)
&state=随机(防CSRF)
&code_challenge=PKCE(公开客户端必填)
→ 用户在 auth 登录(或已登录则自动 SSO)
→ auth 回调你的 redirect_uri?code=xxx&state=xxx
→ 你的后端用 code 换 token:
POST https://auth.ai-as.cc/oauth/token
grant_type=authorization_code
code=xxx
client_id / client_secret(机密)或 code_verifier(公开 PKCE)
→ 返回 {access_token, id_token, refresh_token}
→ 你的后端用 JWKS 公钥本地验签 id_token/access_token
→ 取 sub(user_id)做用户身份 + 按 permissions 授权
→ 发你自己的 session cookie
⚡ 前端性能:预热到 auth.ai-as.cc 的连接(建议加)
用户点“AuthKeystone 登录“时,浏览器要从你的域名跳转到 auth.ai-as.cc。若此前没访问过该域名(或 DNS 缓存已过期),浏览器要现做 DNS 解析 + TCP 建连 + TLS 握手;跨公网、尤其移动网络 / 运营商 DNS 抖动时,这一步可能耗时 1 秒以上,用户会感觉“点了登录卡一下“。
在你的页面 <head> 加两行,让浏览器在用户浏览时就提前把这些做完:
<link rel="dns-prefetch" href="//auth.ai-as.cc">
<link rel="preconnect" href="https://auth.ai-as.cc" crossorigin>
dns-prefetch:提前解析auth.ai-as.cc的 DNSpreconnect:进一步提前完成 DNS + TCP + TLS 握手(比dns-prefetch更彻底)- 两者叠加,用户点登录时直接复用已建立的连接,省掉那次 1s。
加在哪:所有“有 AuthKeystone 登录入口“的页面 <head>,或全局 layout 的 <head> 里。
有没有副作用:没有。纯性能优化,浏览器空闲时执行,不阻塞页面渲染、不影响功能和安全——加了只有好处,建议各业务都加。
2.4 登出 / 单点登出(SLO,必须做)
⚠️ 务必做:业务应用 logout 时,必须让浏览器跳到 AuthKeystone 的 /oidc/end_session 清掉 AuthKeystone 侧的 SSO 会话。只清业务应用自己的 session 不够。
为什么必须做:首次登录后 AuthKeystone 会在浏览器种一个 HttpOnly 的 auth_session SSO cookie(存活到 access_token 过期,默认 24h)。业务 logout 若不调 end_session:
auth_sessioncookie 残留在浏览器- 用户再点登录 →
/authorize读到 cookie 有效 → 免密直接发 code(“自动登录”) - token 未过期(24h 内)就一直免密;过 24h 才重新要密码——这就是“有时候自动登录“的根因
怎么调(业务 logout 最后一步,浏览器顶层跳转):
https://auth.ai-as.cc/oidc/end_session?
id_token_hint=<登录时拿到的 id_token>
&post_logout_redirect_uri=<登出后回业务应用的地址>
| 参数 | 必填 | 作用 |
|---|---|---|
id_token_hint | 推荐 | 登录时 /oauth/token 返回的 id_token。带它,auth 撤销该用户 access/refresh token + 删 session;不带只清 cookie |
post_logout_redirect_uri | 可选 | 登出后回业务应用的地址(须 http(s)://) |
- 必须
window.location浏览器跳转,不能fetch——跨域请求不带 auth.ai-as.cc 的 cookie,清不掉auth_session。 - 标准库可自动:发现文档已声明
end_session_endpoint,标准 OIDC SDK(Nodeopenid-client/ Pythonauthlib/ Javapac4j)发现后会自动在 logout 调用,通常无需手写。
2.5 验 token(JWKS 本地验签,推荐)vs introspect(实时查)
- 本地验签(推荐):用
/.well-known/jwks.json的 RS256 公钥本地验 token 签名 + 校验aud=你的 client_id +exp未过期。无状态,高性能。 - introspect(备选):
POST /oauth/introspect实时查 token 有效性 + 权限。适合需要 token 实时状态(如已吊销)的场景。
3. OIDC 端点速查
| 端点 | 用途 |
|---|---|
GET /.well-known/openid-configuration | OIDC 发现文档(所有端点 + 支持的 scope/claim/grant) |
GET /.well-known/jwks.json | RS256 验签公钥(本地验 token) |
GET /authorize | 授权码签发(用户登录入口) |
POST /oauth/token | 换 token(authorization_code / password / refresh_token / client_credentials) |
GET /api/userinfo | 用户信息(Bearer token → 标准 OIDC claims) |
POST /oauth/introspect | token 内省(实时查有效性 + 权限) |
POST /oauth/revoke | token 吊销 |
GET /oidc/end_session | 单点登出(SLO) |
4. token claim 详解
核心层(所有 token 必有,OIDC 协议级)
{
"sub": "1", // user_id(字符串),用户唯一稳定主键
"iss": "https://auth.ai-as.cc",
"aud": "your_client_id", // 你的 client_id(验 token 时校验)
"exp": 1735689600,
"iat": 1735603200,
"jti": "unique-token-id"
}
扩展层(按 scope 暴露)
{
"preferred_username": "zhangsan", // scope=profile
"email": "zs@example.com", // scope=email
"email_verified": true,
"org_id": "default", // scope=auth:org(个人场景别要这个)
"permissions": ["wiki:read", "wiki:write"] // scope=auth:permissions
}
5. 关键规范(务必遵守)
- 用
sub做用户主键——这是 OIDC 标准,跨组织/改名/换租户都稳定。知识库、配额、会话、偏好,全用它。 org_id仅用于团队/企业隔离——做团队空间、企业数据隔离。个人场景(每个用户独立空间)用sub,不要org_id。- 验 token 时校验
aud= 你的 client_id——防止别的业务的 token 被你误用。 - 只声明你需要的 scope——最小暴露,AuthKeystone 会追踪谁用了哪些字段(迭代时评估影响)。
- 业务自定义 scope 走申请制——向 auth SaaS 管理员申请(如
exomind:wiki),不要擅自用未注册的 scope。 - logout 必须走 SLO——业务 logout 跳
/oidc/end_session(带id_token_hint),否则 AuthKeystone 的 SSO cookie 残留,用户 24h 内点登录会免密“自动登录“。详见 2.4。
6. API Key(服务间,machine-to-machine)
如果你的服务是后端调用 auth(无用户交互),用 API Key:
- 在 AuthKeystone 后台创建 API Key(绑权限范围 + 可选 account_key 资源隔离)。
- 调用时
Authorization: Bearer sk_xxx或X-API-Key: sk_xxx。 POST /api/keys/validate验证 key + 拿权限。
7. 常见错误(避坑)
| 错误 | 后果 | 正确 |
|---|---|---|
用 org_id/tenant_id 做用户主键 | 串用户(default 下所有人共享一个 key) | 用 sub |
用 username 做主键 | 改名后身份断裂 | 用 sub(user_id 稳定) |
不校验 aud | 别的业务 token 被你误用 | 验 aud = 你的 client_id |
声明 auth:org scope 但个人场景 | 多此一举(拿到 org_id=default 没用) | 个人场景只声明 openid+profile |
| token 当永久 session | 权限变更后不生效 | token 有 exp,过期 refresh;或用 introspect 实时查 |
| logout 不调 /oidc/end_session | AuthKeystone 的 SSO cookie 残留,用户 24h 内点登录免密自动登录 | logout 末尾浏览器跳 end_session(带 id_token_hint),见 2.4 |
8. 联调检查清单
下游接入联调时逐项确认:
- OAuth client 注册(client_id/secret/redirect_uri/grant_types)
- scope 声明(勾选你需要的)
- 授权码流程跑通(/authorize → callback → /oauth/token)
- token 验签(JWKS 本地 + aud 校验)
- sub 读法正确(= user_id 字符串,做用户主键)
- /userinfo 调通(按 scope 返 claim)
- refresh_token 续期
- 登录时保存 id_token(供 logout 作 id_token_hint)
- 登出走 SLO(/oidc/end_session,带 id_token_hint)——必须,否则 SSO cookie 残留致免密自动登录(见 2.4)