authentication-and-sessions

认证与会话

YouTeacher 的 Web 应用是一个面向浏览器、代理独立 Auth 服务的客户端。它本身从不持有身份,而是引导用户走完登录、注册与账户安全等流程,并把每一步转交给 Auth 服务。这套子系统值得一提的地方在于,它同时支持多种认证要素——密码、基于时间的一次性验证码第二因子(TOTP)、绑定设备的通行密钥(WebAuthn passkey),以及社交登录(Google、LinkedIn),而且允许同一个账户把它们混用。

把要素看成一个栈

设计文档把整套系统建模成一个下推自动机:用户已启用的要素集合表现得像一个栈,哪些状态转移合法,取决于此刻栈里有什么。一个刚用密码注册的账户带着密码 + TOTP;一个纯社交账户只带着它的登录提供方;一个"完整"账户可以同时持有密码、TOTP、通行密钥以及两个社交提供方。启用一个要素就是压栈,解绑一个要素就是弹栈。正因如此,同一个安全设置页面会对不同用户呈现不同的操作——可用路径是从栈里读出来的,而不是逐页写死的。

各条流程

  • 登录——如果浏览器记得该用户有通行密钥,就先弹出通行密钥提示,并保留退回密码表单的通道;密码表单同时提供社交登录与一个通行密钥按钮。
  • 注册——先用一次性验证码验证邮箱,再让用户设置密码,然后选择并启用一个第二因子(验证器 App 或通行密钥)。社交注册则整体跳过密码与 MFA 步骤。
  • 账户安全——一个统一的枢纽:重置密码、重置验证器、给纯社交账户补一个密码、绑定与解绑登录提供方、管理通行密钥、列出并吊销会话、以及注销账户。

端口与适配器

应用采用六边形架构。用例依赖网关接口(端口),而具体的 API 网关适配器针对 Auth 服务实现这些端口。于是通行密钥用例对接通行密钥端口,会话用例对接会话端口,OAuth 路径对接 OAuth 端口。这样就把认证的逻辑(一条流程做什么)与接线(它如何抵达后端)分开,也让同样的流程在开发时能跑在一个本地 mock 出来的 Auth 服务上。

会话接管

任意流程认证成功后,应用会先尽力吊销当前已有的会话,再在本地接管新会话——如果那次吊销失败,它仍然接管,因此一个陈旧的旧会话绝不会挡住一次全新的登录。别处的会话管理让用户可以吊销某一台设备、登出所有其他设备,或结束当前这台。

护栏与保护隐私的行为

有几条规则是刻意为之,而非偶然。一个登录提供方或通行密钥在它是唯一剩余登录方式时不可被移除。邮箱是否存在、验证码是否发出这类步骤在失败时静默继续,好让外部无法探测哪些邮箱已注册。邮箱错误与密码错误返回同一句通用错误,出于同样的考虑。敏感操作在抵达后端之前会先过一道人机校验(reCAPTCHA)。

身份在各服务间如何分布

Auth 服务是身份以及用户市场角色的来源;验证与审批状态则放在 Profiles 服务里,这样它们可以在不重新签发令牌的情况下变化。当 Web 应用代表用户去调用另一个后端服务时,它会发起一次携带服务凭据以及用户上下文的服务间调用,由下游服务信任这份上下文。

本组件内容

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

authentication-and-sessions