Layer 1 — 认证规则
认证规则控制应用允许使用哪些认证方式。系统会在向用户展示可选方式时(/reason/email)以及每次实际认证尝试中检查这些规则。
| 方式 | Payload | 说明 |
|---|---|---|
PASSKEY_USERNAMELESS | {} | WebAuthn / FIDO2 passkey 的 discoverable-credential / 无用户名(usernameless) 流程。它 gate 在任何邮箱输入之前显示的那个独立「使用 passkey 登录」按钮。两种 passkey 方法登录时用的是用户注册的同一个 passkey —— 区别只在于由哪个按钮唤起。详见下文的 无用户名 passkey。 |
PASSKEY_REASONED | {} | WebAuthn / FIDO2 passkey 的邮箱优先流程。它 gate 在用户输入邮箱之后提供的 passkey 选项。 |
EMAIL_VERIFICATION | {} | 向用户邮箱发送一次性验证码。 |
STEAM_TICKET | { "allowedSteamAppIds": number[] } | 在 Steam 发行的游戏客户端内由 Steamworks SDK 发起的一次性 Steam ticket 交换,由 native-api POST /direct-issue/steam-ticket 消费。allowedSteamAppIds 把规则收紧到具体的 Steam App ID 列表。端到端用法见 原生客户端。 |
STEAM_OPENID | {} | 浏览器端的 “Sign in with Steam” 按钮,使用 Steam 的 OpenID 2.0 OP。此处解析得到的 Steam 身份与 STEAM_TICKET 共用同一条用户身份行,所以一个用户从游戏端首次登录后可以接着从网页按钮登录(反之亦然)。这条路径不需要 client ID、client secret、Web API key —— 通过 openid.mode=check_authentication 做无密钥验证。 |
ACCESS_KEY_DIRECT | {} | 一次性的 AccessKey 凭据登录,由 native-api POST /direct-issue/access-key 消费。凭据格式与端到端流程见 原生客户端。 |
GOOGLE_OAUTH | {} | 通过 Google 作为上游 OIDC 提供方登录。Sudomimus 在此扮演 OIDC 接入方角色。Phase 1 的 payload 为空;未来阶段会加入 allowedHostedDomains 以支持 Google Workspace 域名限制。 |
GITHUB_OAUTH | { "allowedGitHubOrgs": string[] } | 通过 GitHub 作为上游 OAuth 2.0 提供方登录(没有 id_token;profile 与已验证邮箱列表通过 GitHub REST API 拉取)。allowedGitHubOrgs 是 GitHub Organization login 的精确匹配列表(大小写不敏感);空数组表示不限制组织,非空表示用户必须属于其中至少一个组织。只有匹配规则包含非空允许列表时,才会请求 read:org scope。未配置组织限制时,授权页与 Phase 2 相同,只请求 read:user user:email。 |
DISCORD_OAUTH | {} | 通过 Discord 作为上游 OAuth 2.0 提供方登录(没有 id_token;profile 与邮箱通过 GET /users/@me 拉取)。Scopes 为 identify email。一封邮箱只有当 Discord 同时返回非空 email 与 verified: true 时才被视为已验证;否则账户会被创建但不会记录已验证邮箱,因此 Layer 2 EMAIL 规则会 fail-closed(这是有意为之,与 Google / GitHub 一致)。未来阶段将加入 allowedDiscordGuilds 用于服务器(guild)限制。 |
BATTLENET_OAUTH | {} | 通过 Battle.net 作为上游 OIDC-shaped OAuth 提供方登录。Battle.net 的 /userinfo 只返回 subject 和 BattleTag —— 没有邮箱 —— 因此账户会被创建但不记录已验证邮箱,Layer 2 EMAIL 规则对 Battle.net-only 账户 fail-closed(有意为之)。Battle.net 没有 per-application 限制概念,payload 恒为空。 |
X_OAUTH | {} | 通过 X(原 Twitter)作为上游 OAuth 2.0 提供方登录。X 的 v2 /2/users/me 不提供邮箱,因此与 Battle.net、Steam 一样,账户会被创建但不记录已验证邮箱,Layer 2 EMAIL 规则 fail-closed(有意为之)。payload 为空;无 per-application 限制。 |
ENTERPRISE_FEDERATION_APPLICATION_MANAGED | { "connectorAnchor": string } | 通过你自己组织注册为外部连接的 OIDC 身份提供方进行「使用 …… 登录」。connectorAnchor 指向一个归该应用所属组织所有的外部连接;一条规则渲染一个按钮。登录走标准的鉴权与 realize 流水线。详见使用你的 IdP 登录。 |
ENTERPRISE_FEDERATION_DOMAIN_MANAGED | {} | 让应用 opt-in,接受强制 SSO 登录。payload 为空——外部连接在登录时由用户的邮箱域名解析得出(一个由其所有者设置了 SSO_ONLY 登录策略的已验证域名),不在规则里指名。没有这条规则的应用会拒绝被 SSO 管控的用户。详见使用你的 IdP 登录。 |
STEAM_TICKET 与 ACCESS_KEY_DIRECT 的端到端用法见 原生客户端;两种企业联合方法见 域名与联合登录 一节。
应用规则结构
Section titled “应用规则结构”每条认证规则描述一种认证方式。要允许多种方式,请分别创建多条规则。
{ "method": "PASSKEY_REASONED", "payload": {}, "accessTokenTtlSeconds": null, "refreshTokenTtlSeconds": null}STEAM_TICKET 的 payload 包含允许的 App ID 列表:
{ "method": "STEAM_TICKET", "payload": { "allowedSteamAppIds": [480, 730] }, "accessTokenTtlSeconds": null, "refreshTokenTtlSeconds": null}两个 TTL 字段是可选的;如果提供,它们会参与发放令牌时的 最小值 fold。
在 /establish 上收紧
Section titled “在 /establish 上收紧”/establish 中的 authenticationConstraints 使用相同结构,用于限制单次认证请求:
{ "applicationAnchor": "my-app", "authenticationConstraints": [ { "method": "PASSKEY_REASONED", "payload": {} } ]}- 字段缺失 → 不收紧;只看应用规则。
- 字段存在且为空数组 → 拒绝。
- 字段存在且非空 → 和应用规则做 AND。
某应用配置了两条认证规则:PASSKEY_REASONED 与 EMAIL_VERIFICATION。管理员入口在一次认证请求中传入 authenticationConstraints: [{ "method": "PASSKEY_REASONED", "payload": {} }]。
| 方式 | 应用允许? | Inquiry 允许? | 结果 |
|---|---|---|---|
PASSKEY_REASONED | 是 | 是 | 提供 |
EMAIL_VERIFICATION | 是 | 否 | 隐藏 |
用户在这次会话里只看得到 passkey 选项,尽管应用本身也是允许邮箱方式的。
无用户名 passkey
Section titled “无用户名 passkey”passkey 登录拆分成两个相互独立的 Layer-1 方法,而不是一条带 flag 的规则:
PASSKEY_REASONED是邮箱优先流程:用户先输入邮箱,/reason/email解析出其账户,然后用注册过的凭据完成挑战。PASSKEY_USERNAMELESS是 discoverable-credential 流程:在认证 UI 顶部、邮箱框之前,显示一个独立的「使用 passkey 登录」按钮。用户点一下,浏览器弹出原生 passkey 选择器,挑一个凭据并完成验证(指纹 / 面部 / PIN),整个过程无需输入邮箱。
「仅无用户名」的应用 —— 即没有任何邮箱框提供 passkey 选项 —— 只需仅允许 PASSKEY_USERNAMELESS 即可表达:
{ "method": "PASSKEY_USERNAMELESS", "payload": {}}要点:
- 这两个方法是独立的规则,payload 均为空。要邮箱优先入口就配
PASSKEY_REASONED,要独立按钮就配PASSKEY_USERNAMELESS,两者都要就都配。已不再有allowUsernameless这个 flag。 - 两个方法都解析到同一条共享的
PASSKEY凭据行,因此通过其中一个流程注册的凭据可被另一个流程使用。 - 每次 inquiry 的
authenticationConstraints可以携带PASSKEY_USERNAMELESS,把单次 inquiry 收紧到只剩 discoverable-credential 按钮(与应用规则做 AND,和其它方法一样)。 - Discoverable-credential 流程要求 authenticator 设置 User Verified (UV) 标志(指纹 / PIN)。没有用户验证的无用户名登录会被拒绝 —— 因为没有输入的邮箱可以作为第二因素。
- 这两条规则不影响 passkey 的注册流程:用户仍然通过常规的”邮箱验证后注册 passkey”路径来添加凭据。
PASSKEY_USERNAMELESS只控制登录时是否提供这个独立按钮。
三层规则模型 整体模型:允许列表、默认拒绝、评估顺序,以及三层如何组合。
Layer 2 —— 身份准入规则 认证后的身份准入检查,可按邮箱等身份信息建立允许列表。
Layer 3 —— 返回规则 把 realize 后的会话如何回传给应用。