认证与会话
YouTeacher 的 Web 应用是一个面向浏览器、代理独立 Auth 服务的客户端。它本身从不持有身份,而是引导用户走完登录、注册与账户安全等流程,并把每一步转交给 Auth 服务。这套子系统值得一提的地方在于,它同时支持多种认证要素——密码、基于时间的一次性验证码第二因子(TOTP)、绑定设备的通行密钥(WebAuthn passkey),以及社交登录(Google、LinkedIn),而且允许同一个账户把它们混用。
把要素看成一个栈
设计文档把整套系统建模成一个下推自动机:用户已启用的要素集合表现得像一个栈,哪些状态转移合法,取决于此刻栈里有什么。一个刚用密码注册的账户带着密码 + TOTP;一个纯社交账户只带着它的登录提供方;一个"完整"账户可以同时持有密码、TOTP、通行密钥以及两个社交提供方。启用一个要素就是压栈,解绑一个要素就是弹栈。正因如此,同一个安全设置页面会对不同用户呈现不同的操作——可用路径是从栈里读出来的,而不是逐页写死的。
各条流程
- 登录——如果浏览器记得该用户有通行密钥,就先弹出通行密钥提示,并保留退回密码表单的通道;密码表单同时提供社交登录与一个通行密钥按钮。
- 注册——先用一次性验证码验证邮箱,再让用户设置密码,然后选择并启用一个第二因子(验证器 App 或通行密钥)。社交注册则整体跳过密码与 MFA 步骤。
- 账户安全——一个统一的枢纽:重置密码、重置验证器、给纯社交账户补一个密码、绑定与解绑登录提供方、管理通行密钥、列出并吊销会话、以及注销账户。
端口与适配器
应用采用六边形架构。用例依赖网关接口(端口),而具体的 API 网关适配器针对 Auth 服务实现这些端口。于是通行密钥用例对接通行密钥端口,会话用例对接会话端口,OAuth 路径对接 OAuth 端口。这样就把认证的逻辑(一条流程做什么)与接线(它如何抵达后端)分开,也让同样的流程在开发时能跑在一个本地 mock 出来的 Auth 服务上。
会话接管
任意流程认证成功后,应用会先尽力吊销当前已有的会话,再在本地接管新会话——如果那次吊销失败,它仍然接管,因此一个陈旧的旧会话绝不会挡住一次全新的登录。别处的会话管理让用户可以吊销某一台设备、登出所有其他设备,或结束当前这台。
护栏与保护隐私的行为
有几条规则是刻意为之,而非偶然。一个登录提供方或通行密钥在它是唯一剩余登录方式时不可被移除。邮箱是否存在、验证码是否发出这类步骤在失败时静默继续,好让外部无法探测哪些邮箱已注册。邮箱错误与密码错误返回同一句通用错误,出于同样的考虑。敏感操作在抵达后端之前会先过一道人机校验(reCAPTCHA)。
身份在各服务间如何分布
Auth 服务是身份以及用户市场角色的来源;验证与审批状态则放在 Profiles 服务里,这样它们可以在不重新签发令牌的情况下变化。当 Web 应用代表用户去调用另一个后端服务时,它会发起一次携带服务凭据以及用户上下文的服务间调用,由下游服务信任这份上下文。
本组件内容
- ddd-hexagonal-architecture ——本子系统所处的分层(领域 / 用例 / 适配器 / 功能 UI)