GitHub

站点域角色(site-scoped roles,权威定义,Tier A)

返回 README


本文是「站点域角色」site_roles claim 的唯一权威来源(Tier A)。 它是 11-roles.md 五角色契约的加法扩展,不改动其任何既有语义:让一个账号可以只在某一个站点持有职务 (如「letmoe 的 moderator」),而不获得任何跨站权力。下游未升级前完全不受影响(未知 claim 被忽略)。


1. 为什么需要它

全局五角色是全站的:授予 moderator 意味着 kungal / moyu / letmoe 三站全境版主。当多站并存, 「仅在 letmoe 任职」这一真实需求无法用五角色表达。站点域角色补上这一层:授予中央化(仍归 OAuth 后台),但只对被授予的那个站点生效

与 §11 的关系:

  • 全局角色(roles claim)不变:管理轴 moderator ⊂ admin ⊂ rencreator 正交,一切照旧。
  • 站点角色(site_roles claim)是新增的正交层:只在签发 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 且已过期的授予不出现。
  • 出现在三处,语义一致:
    1. access tokensite_roles claim(/oauth/token 授权码/刷新两条路都带)。
    2. GET /oauth/userinfo 响应的 site_roles 字段(按调用所用 token 的站点)。
    3. GET /users/batch(S2S,见 03)每个 brief 的 site_roles 字段 (按请求方 client 的站点)。
  • 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)

  1. 联合解析:把 site_roles 并入你已有的角色集(effective = roles ∪ site_roles),再喂给你 既有的能力函数/权限判定。不需要为它写新的判定路径。
  2. 只对当前站生效是天然的:claim 已由 OP 按 client 站点定界,你拿到的 site_roles 本就只属本站。 你不需要、也拿不到别站的站点角色。
  3. 安全不变量(为什么联合是安全的):site_roles 永不含 admin/ren(§3 禁令 + IdP 校验), 所以把它并进角色集只可能新增 moderator/creator/自定义名,绝不可能让一个站点授予触达 admin/ren 专属能力。管理面(用户管理、站点/client 配置、PII、artifact 运维)只认 admin/ren, 站点角色结构上够不着。
  4. 自定义捆名各站自治:moderator/creator 沿用 §11 的语义;站点自定义名的含义由该站自己在其 权限映射里定义(IdP 不参与)。
  5. 不得在下游实现授予/撤销:站点角色的授予/撤销只在 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 后端:在把 roles claim 映射进内部能力时,并入 site_roles (CanModerate 之类的判定对「本站 moderator」自然放行);自定义捆名各站在本仓定义其权限。
  • 只读展示:GET /users/batch 的 brief 现带 site_roles(本站),可用于「本站版主」标记等。
  • 无需改动:不使用站点角色的站点零改动——未知/缺失的 site_roles claim 被忽略。

8. 关联文档

  • 11-roles.md —— 全局五角色契约(本文是它的加法扩展)。
  • 04-tokens-and-errors.md —— access token claims 位置。
  • 03-cross-service.md —— /users/batch S2S 面。
  • infra 内部设计(引擎/词汇/纪律):docs/auth/04-permission-first-authz.md

源:kun-galgame-infra/docs/integration/oauth/12-site-roles.md