登录流程文档与实际代码对比

> 注意:本文件是「开发记录 / 博客」用途,不是代码执行规则 AGENTS.md。

by 于尘 ·
知与行 CloudBase

注意:本文件是「开发记录 / 博客」用途,不是代码执行规则 AGENTS.md

博客记录原则:简洁、清晰、直接。


1. 问题背景

本轮在 CloudBase 登录 / 静默恢复 / 手机号与密码 等路径上做了实现收敛与行为修正,并 重写 docs/流程文档/登录流程.md 与代码一致。此前存在三类痛点:实现与文档/旧笔记各说各话SDK 返回字段与分支判断不一致导致「看似失败」补设密与多路同步竞态导致误踢或重复请求。本文按问题记录根因链、方案与采纳结果,便于以后排障与改代码时对齐事实。


2. 问题列表

2.1 身份字段与「是否已登录」判断混乱

根因(链式,最多 3 轮)
why1:为什么业务里对「是否有效用户」说法不一?因为文档或旧对比里曾写 uid → user_id → sub 等多源兜底。
why2:为什么多源兜底会误导?因为 当前仓库策略已定为只认 CloudBase 的 uid,混用 sub / user_id 会与真实分支不一致。
why3:为什么必须写死策略?因为否则 Context、isValidCloudbaseUid 与线上日志无法对齐,排障成本高。

方案(每个问题不超过两个)

  • 短期方案:在 AuthContext 中统一 isValidCloudbaseUid 与写入 Context 的 user.id 只使用 user.uid;主文档删除「代码读 sub」类过时描述。

    • 优点:行为单一、可验证。
    • 缺点:若未来 SDK 契约再变,需再改一处并更文档。
  • 长远方案:把「有效用户判定」收口为单一模块 + 单测/契约注释。

    • 优点:变更可回归。
    • 缺点:本轮未单独拆包。

采纳方案:短期方案;主流程文档已与 uid 为准的实现对齐。

补充用例:冷启动、密码登录、验证码登录后看 Context user.id[Auth] 日志是否一致。


2.2 密码/验证码登录成功却仍判失败(id / uid

根因
why1:为什么 signIn 有时拿不到「已登录」?因为 signInWithPassword 等成功分支里 await getCurrentUser() 返回的对象常见为 id 而无 uid
why2:为什么只认 uid 会踩坑?因为若未做 uid ?? id 归一,会把已成功会话误判为失败。

方案

  • 短期方案:成功分支统一 await getCurrentUser()AuthContext.signInuid ?? id 判断是否真正登录成功。

    • 优点:与 SDK 实际返回一致。
    • 缺点:依赖 SDK 字段命名,需在文档标明。
  • 长远方案:封装 normalizeCloudbaseUser(),单测覆盖仅有 id 的返回体。

    • 优点:调用方不再散落 ??
    • 缺点:多一层抽象。

采纳方案:短期方案 + 已写入 登录流程.md(含 getCurrentUser await 与 signIn 校验说明)。

补充用例:邮箱密码、手机号密码、手机验证码登录各走一遍。


2.3 手机号历史账号未设登录密码:补设密与「误踢」

根因
why1:为什么补设密后仍可能被强制登出?因为若在 SIGNED_IN / AppState / syncUserFromSDK 等路径也做「缺密码就踢」,容易撞上 updateUser 设密后元数据尚未刷新 的窗口。
why2:为什么只收紧触发点能缓解?因为 仅在 checkInitialState(含冷启动静默重登后的再次判断) 强制补设密,可避免刚写完密码仍被旧元数据判缺。

方案

  • 短期方案:判定 legacyPhoneUserMissingPasswordhasPassword !== true 且有 phone / phone_number);强制流程只挂 checkInitialState;登录页 resetPasswordForPhone + updateUser,成功后 scheduleSyncAfterLogin

    • 优点:减少竞态误踢。
    • 缺点:若未来新增入口绕开 checkInitialState,需复查。
  • 长远方案:服务端或统一 User 元数据版本号,客户端以版本 gate 再判 hasPassword

    • 优点:语义最稳。
    • 缺点:依赖后端能力。

采纳方案:短期方案;主文档已写清触发面与登录页流程。

补充用例:冷启动命中补设密 → 设密成功 → 再切前后台,确认不再误踢。


2.4 回前台与静默重登:顺序、并发与 credentials_error

根因
why1:为什么会出现重复静默请求或错误登出?因为 refreshSessiontrySilentReloginWithStoredPassword 若并行或顺序颠倒,会与订阅回调打架;credentials_error 若一律映射为登出,会在 仍有 hasLoginState().user 时误伤。

方案

  • 短期方案仅当 refreshSession 失败trySilentReloginWithStoredPasswordcloudbase.tssilentReloginInFlight 合并并发;onAuthStateChangecredentials_error 且仍有 user 直接 return,否则与 sign_out 一样映射 SIGNED_OUT

    • 优点:行为可预期、减少闪退登录态。
    • 缺点:包装层逻辑需随 SDK 事件再验证。
  • 长远方案:把会话刷新策略画成状态机并加集成测试。

    • 优点:回归稳定。
    • 缺点:成本高。

采纳方案:短期方案;主文档已记先后与 credentials_error 分支。

补充用例:回前台、多 Tab/快速切换 AppState、弱网下 refreshSession 失败路径。


2.5 排查成本高:双源日志与关键前缀

根因
why1:为什么线上难对齐「SDK 里到底是谁」?因为 getLoginState().usergetCurrentUser() 可能短暂不一致,若无日志只能猜。

方案

  • 短期方案checkInitialState 中双源并存时打 [Auth] checkInitialState 双源对比不参与分支);文档列 [Auth] 常用关键字(与 [AuthProbe] 区分)。

    • 优点:低成本可观测。
    • 缺点:日志量需控制,避免刷屏。
  • 长远方案:可采样或分级日志。

    • 优点:长期可运维。
    • 缺点:本轮未做。

采纳方案:短期方案;已写入 登录流程.md「关键日志点」。

补充用例:复现一次双源不一致时 grep [Auth] checkInitialState


2.6 记住密码仅恢复邮箱,手机号需手填

根因
why1:为什么手机用户重进登录页不省事?因为 REMEMBER_EMAIL_KEY 名义上存「账号」,但 UI 只把值灌进 email,未识别 11 位国内手机号 写入 phoneNumber

方案

  • 短期方案:加载记住账号时,除 email 外,若为 11 位手机号则写入 phoneNumber
    • 优点:改动小、体验立竿见影。
    • 缺点:国际号码规则未扩展。

采纳方案:短期方案。

补充用例:勾选记住密码 → 用手机号登录 → 杀进程重开 → 手机号应出现在手机登录流程。


3. 小结

  • 主文档 docs/流程文档/登录流程.md 已按 本轮修复后的代码 重写并对齐:uid 有效用户双源日志refreshSession 与静默重登顺序静默重登并发合并credentials_error 包装层补设密仅 checkInitialState 强制getCurrentUser await 与 signInuid ?? id记住密码预填手机号showInfo 短时去重 等。
  • 若旧笔记仍写「代码读取 sub」等,视为过时;以 仓库 + 主流程文档 为准。
  • 维护建议:今后再改 AuthContext / cloudbase 认证行为时,同步更新 docs/流程文档/登录流程.md;若需要,再在根 AGENTS.mdzhiyuxing-app/AGENTS.md 加一句提醒。
← 所有文章