身份声明与共享
除了稳定的用户标识,应用有时还需要邮箱、名、姓、静态头像或动态头像。Sudomimus 把它们作为五条独立的身份声明(claim)管理。共享规则分为两部分:
- 声明策略(claim policy) —— 由开发者按应用设置:应用请求哪些声明,以及请求得有多强。
- 声明授权(claim grant) —— 由用户按应用设置:他们同意共享哪些声明。
只有应用策略允许、用户也同意时,UserInfo 才会返回真实资料。OIDC 还要求登录时申请对应的 scope。用户撤销授权后,下一次 UserInfo 请求就不再返回该资料。
声明策略(开发者)
Section titled “声明策略(开发者)”在 With 门户的应用详情页中,每条声明都可以设为以下一种策略:
| 策略 | 含义 |
|---|---|
| Off | 从不请求。无论用户是否愿意,这条声明永不共享。 |
| Optional | 会请求,但用户可以拒绝。一旦拒绝,应用就直接收不到它。 |
| Required | 应用需要它。用户必须授权才能完成登录。 |
仅占位资料 (SYNTHETIC_ONLY) |
始终提供生成的占位值,并且从不请求或共享用户真实数据。 |
占位兜底 (SYNTHETIC_FALLBACK) |
始终提供。用户授权时共享真实值;否则应用收到生成的替身(占位姓名、…@proxy.sudomimus.email 代理地址,或生成头像)。它从不阻塞登录,也从不触发 errand。 |
默认情况下,邮箱、名、姓和静态头像使用仅占位资料,动态头像为 Off。新应用因此可以获得稳定的占位身份,但不会自动收到真实资料。不需要的声明可以直接设为 Off。
用户首次登录一个会请求声明的应用时,Sudomimus 会显示一个同意(consent)界面:
- Required 声明以锁定为开的形式展示 —— 授权它们是完成登录的一部分。
- Optional 声明以复选框展示,默认不勾选 —— 由用户主动选择加入。
- 占位兜底声明同样以可选复选框展示,差别在文案里点明:不勾选它,应用收到的是一个占位值,而不是什么都收不到。
- 仅占位资料声明不会显示为同意选项,因为它从不共享真实数据。
他们的决定会被记为三种状态之一 —— 已授权、已拒绝或尚未决定。已拒绝的可选声明不会再次请求;尚未决定的声明会在下一次交互式登录时显示。
用户可以在账户门户的数据共享页面查看各应用能收到哪些资料,也可以按应用撤销授权。授权状态会实时用于 UserInfo;撤销后,下一次 UserInfo 请求就不再返回相应的真实资料。
UserInfo 何时返回声明
Section titled “UserInfo 何时返回声明”对某条声明而言,规则是:
策略允许提供值;如果请求的是真实数据,则用户授权状态也允许;并且,对 OIDC 而言,请求了相应的 scope。
flowchart TD
Request["为会话权限与 UserInfo 评估一条声明"] --> OIDC{"是否为 OIDC 会话?"}
OIDC -->|是| Scope{"是否请求了对应 scope?"}
Scope -->|否| Omit["省略该声明"]
Scope -->|是| Policy{"声明策略"}
OIDC -->|否 — Session API| Policy
Policy -->|OFF| Omit
Policy -->|SYNTHETIC_ONLY| Placeholder["返回占位值"]
Policy -->|OPTIONAL| Optional{"已授权且真实资料存在?"}
Optional -->|是| Real["返回真实资料"]
Optional -->|否| Omit
Policy -->|REQUIRED| Required{"已授权且真实资料存在?"}
Required -->|是| Real
Required -->|否| Block["在 UserInfo 之前<br/>阻止签发或刷新"]
Policy -->|SYNTHETIC_FALLBACK| Fallback{"已授权且真实资料存在?"}
Fallback -->|是| Real
Fallback -->|否| Placeholder
OIDC 的 scope 把关映射如下:
emailscope → 邮箱声明profilescope → 名、姓与头像
普通应用会话使用 Session API /userinfo,不受 OIDC scope 限制,只看声明策略和用户授权。OIDC 应用使用 discovery 中公布的 /userinfo 端点。
占位模式是「要么授权、要么省略」之外的例外:SYNTHETIC_ONLY 始终以占位值在场;SYNTHETIC_FALLBACK 在用户授权时提供真实数据,否则提供占位值。占位值按账户和扇区保持稳定:生成姓名、…@proxy.sudomimus.email 代理地址,以及生成头像 URL。占位声明从不阻塞登录、也从不被省略 —— 只是对 OIDC 邮箱而言,应用会被告知该地址未经验证。
Required 声明与非交互式登录
Section titled “Required 声明与非交互式登录”Required 声明需要用户在交互式流程中授权,账户也必须具备对应资料。非交互式登录无法满足这些条件时会被拒绝,原因有两种:
ClaimConsentRequired—— 用户没有授权某条 required 声明。RequiredClaimDataMissing—— 用户已经授权了它,但账户缺少底层数据(例如纯 Steam 账户没有邮箱)。
这项检查保证应用不会在缺少 required 资料的情况下建立或续期会话。它适用于原生 direct-issue、令牌刷新和 OIDC token 端点。
用户如何解除阻塞,取决于客户端类型:
- 原生客户端(Steam / AccessKey direct-issue)没有对应用本身的交互式登录,所以
403会带上一个 Errand —— 一个浏览器补全流程,用户在那里登录(如果要写入数据)、补全缺失的数据、并授予同意。随后客户端重试。对 AccessKey 而言,同样的同意也可以提前收集 —— 就在用户于门户里创建该 key 的那一刻。 - 浏览器 / OIDC 客户端在下一次对该应用的常规交互式登录时解除阻塞,同意界面会内联展示。
未授权的可选声明则永远不会阻塞任何东西 —— 它只是被省略。占位模式同样从不阻塞,所以它们是「保证某个值一定在场」而又不强迫用户走一趟浏览器侧行的办法。
claims 块
Section titled “claims 块”Sudomimus 通过 Connect、Session API 或原生 direct-issue 签发、刷新令牌时,响应会在令牌之外附带顶层 claims 块。它说明应用采用的策略和用户当前的授权状态,方便客户端判断下一步是否需要交互式登录或补充资料。
{ "email": { "requirement": "REQUIRED", "state": "GRANTED" }, "firstName": { "requirement": "OPTIONAL", "state": "DENIED" }, "lastName": { "requirement": "OFF", "state": "UNKNOWN" }, "staticAvatar": { "requirement": "SYNTHETIC_ONLY", "state": "UNKNOWN" }, "animatedAvatar": { "requirement": "OFF", "state": "UNKNOWN" }}每个声明都会给出它的 requirement(开发者的策略:SYNTHETIC_ONLY / OFF / OPTIONAL / REQUIRED / SYNTHETIC_FALLBACK)与它的 state(用户的当前决定:UNKNOWN / GRANTED / DENIED)的组合。UNKNOWN(「从未问过」)与 DENIED(「明确拒绝」)之所以要区分,正是这里用三种状态而不是一个可空布尔值的原因。
这个块描述 UserInfo 会采用的策略与同意状态:策略为 OFF、用户从未作出选择、用户拒绝授权,或已经授权但账户缺少数据。direct-issue 因声明检查返回 403 时,同一个块会列出建立 session 前仍需满足的条件。
如需在签发后读取实时状态,请携带 access token 调用 Session
GET /claim-state。它的 claims map 使用 UserInfo 字段名:email、
given_name、family_name、picture 与 picture_animated。OIDC 客户端从
discovery 读取提供方专属的 claim_state_endpoint;响应只包含该 session 的
email 与 profile scope 所覆盖的状态。name 与 email_verified 是派生字段,
没有独立状态。
应用收到什么
Section titled “应用收到什么”- Application access / refresh token —— 不携带个人资料 claim。OIDC ID Token 也不携带个人资料,但
response_type=id_token是例外:它包含 scope、当前策略和用户授权允许披露的声明。已签发的 JWT 是快照;撤销授权不能收回已交付的数据。 - Session
/userinfo—— 由 claim policy 与用户授权实时把关,不受 OIDC scope 控制。 - OIDC
/userinfo—— 由 scope 与授权共同把关:邮箱声明变成email(外加email_verified);名变成given_name;姓变成family_name;静态头像变成picture;动态头像变成picture_animated;name由已授权的姓名部分组合而成。Synthetic 邮箱会以email_verified: false发出 —— 它是一个代理地址、不是已验证的邮箱,请不要当成已验证来处理。
令牌字段与 UserInfo 的使用方式见令牌与验证。头像 URL 还有额外的隔离、撤销与缓存规则,见头像声明与交付。
- 开发者 —— 你的应用在 With 门户的详情页:把每条声明设为 Off / Optional / Required / 仅占位资料 / 占位兜底。
- 用户 —— 账户门户里的数据共享视图:查看并撤销每个应用收到的内容。