站点域角色(site-scoped roles,权威定义,Tier A)
返回 README
本文是「站点域角色」
site_rolesclaim 的唯一权威来源(Tier A)。 它是 11-roles.md 五角色契约的加法扩展,不改动其任何既有语义:让一个账号可以只在某一个站点持有职务 (如「letmoe 的 moderator」),而不获得任何跨站权力。下游未升级前完全不受影响(未知 claim 被忽略)。
1. 为什么需要它
全局五角色是全站的:授予 moderator 意味着 kungal / moyu / letmoe 三站全境版主。当多站并存,
「仅在 letmoe 任职」这一真实需求无法用五角色表达。站点域角色补上这一层:授予中央化(仍归 OAuth
后台),但只对被授予的那个站点生效。
与 §11 的关系:
- 全局角色(
rolesclaim)不变:管理轴moderator ⊂ admin ⊂ ren、creator正交,一切照旧。 - 站点角色(
site_rolesclaim)是新增的正交层:只在签发 token 的那个 client 的站点生效。 - 两者在下游取并集后喂给同一套能力判定(见 §5)。
2. claim 形状与出现点(权威)
site_roles 是一个扁平的角色名字符串数组,与 roles claim 同构:
{ "roles": ["creator"], "site_roles": ["moderator"], "site_id": 3, "...": "..." }
- 按签发 client 的站点定界:发给 letmoe client 的 token,
site_roles只含该用户在 letmoe 的授予,绝不含别站的。无跨站泄漏,token 不膨胀。 - 空则省略:用户在该站无授予时,整个
site_roles字段不出现(omitempty)。非站点绑定的 client(如 OAuth 站自身、first-party 登录 token)也不带。 - 只含未过期授予:带
expires_at且已过期的授予不出现。 - 出现在三处,语义一致:
- access token 的
site_rolesclaim(/oauth/token授权码/刷新两条路都带)。 GET /oauth/userinfo响应的site_roles字段(按调用所用 token 的站点)。GET /users/batch(S2S,见 03)每个 brief 的site_roles字段 (按请求方 client 的站点)。
- access token 的
- wire 模式无关:legacy 与标准 OIDC wire(
KUN_OIDC_STANDARD_WIRE)下携带方式一致——它就是 access token 里的一个自定义 claim。
3. 可授予的角色名策略(权威)
站点角色名由 IdP 在授予时按策略校验(IdP 只验策略,不认识各站的具体词汇):
- 禁止
user/admin/ren:user是隐式基座;admin/ren是全局专属管理层。这条禁令 是安全保证的核心(见 §5)——站点角色永不可能是admin/ren。 - 允许
moderator/creator:即「该站上等同于全局同名角色的本站切片」(letmoe 的 moderator = 只在 letmoe 有版主权)。 - 允许站点自定义名:pattern
^[a-z][a-z0-9_]{1,49}$(小写字母开头、2–50 位a-z0-9_),如event_organizer。这些自定义名对应站点自行定义其含义;IdP 不解释它们。 - 站点对不认识的名字天然 fail-closed:下游能力映射查无此名 = 零授予。
4. 时效(同 §11 §2)
站点角色变更在下一次 token 刷新后生效。下游必须每次从最新 claim 重新解析,不得长期缓存到
失去时效——提权/降权要能在会话中途生效。expires_at 到期后,刷新出的新 token 自动不再含该角色。
5. 下游必须遵守的规则(MUST)
- 联合解析:把
site_roles并入你已有的角色集(effective = roles ∪ site_roles),再喂给你 既有的能力函数/权限判定。不需要为它写新的判定路径。 - 只对当前站生效是天然的:claim 已由 OP 按 client 站点定界,你拿到的
site_roles本就只属本站。 你不需要、也拿不到别站的站点角色。 - 安全不变量(为什么联合是安全的):
site_roles永不含admin/ren(§3 禁令 + IdP 校验), 所以把它并进角色集只可能新增moderator/creator/自定义名,绝不可能让一个站点授予触达admin/ren专属能力。管理面(用户管理、站点/client 配置、PII、artifact 运维)只认admin/ren, 站点角色结构上够不着。 - 自定义捆名各站自治:
moderator/creator沿用 §11 的语义;站点自定义名的含义由该站自己在其 权限映射里定义(IdP 不参与)。 - 不得在下游实现授予/撤销:站点角色的授予/撤销只在 OAuth 后台(与 §11 §4 规则 5 一致)。
6. 授予入口(仅 OAuth 后台)
| 操作 | 端点 | 门 |
|---|---|---|
| 授予 | POST /admin/users/:uuid/site-roles {site_id, role_name, note?, expires_at?} |
admin / ren |
| 撤销 | DELETE /admin/users/:uuid/site-roles?site_id=&role_name=(幂等) |
admin / ren |
- 不能给自己授予/撤销(与全局角色矩阵一致)。
- 授予记录
granted_by(操作者)、granted_at,可选expires_at(过期)/note。 admin即可授予站点角色——站点角色的天花板(moderator)低于全局admin本身能授的范围。
7. 下游接入清单
- kungal / moyu / letmoe 后端:在把
rolesclaim 映射进内部能力时,并入site_roles(CanModerate之类的判定对「本站 moderator」自然放行);自定义捆名各站在本仓定义其权限。 - 只读展示:
GET /users/batch的 brief 现带site_roles(本站),可用于「本站版主」标记等。 - 无需改动:不使用站点角色的站点零改动——未知/缺失的
site_rolesclaim 被忽略。
8. 关联文档
- 11-roles.md —— 全局五角色契约(本文是它的加法扩展)。
- 04-tokens-and-errors.md —— access token claims 位置。
- 03-cross-service.md ——
/users/batchS2S 面。 - infra 内部设计(引擎/词汇/纪律):
docs/auth/04-permission-first-authz.md。
源:kun-galgame-infra/docs/integration/oauth/12-site-roles.md