fix auth login state race

This commit is contained in:
2026-05-09 01:03:56 +08:00
parent 23ba2703b4
commit 9ca66715a4
11 changed files with 219 additions and 11 deletions

View File

@@ -0,0 +1,37 @@
# `AuthGate` 登录后又回到未登录状态修复
日期:`2026-05-09`
## 背景
本地联调中,手机号验证码登录有时会先显示登录成功,随后又瞬间回到未登录态。
## 根因
`AuthGate` 首次挂载时会异步 hydrate
1. 先轮换 refresh cookie
2. 再请求 `/api/auth/me`
3. 再根据结果写入 `user``status`
如果用户在这轮 hydrate 尚未完成时已经完成了登录,后到达的旧 hydrate 结果仍可能把刚写入的 `user` 覆盖回 `null`,导致登录态闪回未登录。
## 修复
`AuthGate` 增加 hydrate 版本号保护:
1. 每次启动 hydrate 都分配独立版本号。
2. 登录成功、退出登录、收到全局 auth state 事件时递增版本号。
3. 旧版本 hydrate 的结果到达后直接丢弃,不再覆盖当前 `user` / `status`
## 验证
1. `npm run test -- src/components/auth/AuthGate.test.tsx`
2. `npm run test -- src/services/apiClient.test.ts src/services/authService.test.ts`
3. `npm run check:encoding`
## 关联
- `src/components/auth/AuthGate.tsx`
- `src/components/auth/AuthGate.test.tsx`
- `.hermes/shared-memory/pitfalls.md`

View File

@@ -0,0 +1,66 @@
# 手机验证码短信 Provider 错误 HTTP 映射修复
日期:`2026-05-08`
## 背景
本地登录弹窗点击手机号验证码登录时,浏览器报:
```text
POST /api/auth/phone/login 500
```
排查发现当前 `.env.local` 使用:
```text
SMS_AUTH_PROVIDER=aliyun
```
因此 `send-code` 会走真实阿里云短信 provider。真实 provider 返回 `UNKNOWN``biz.FREQUENCY / check frequency failed` 时,`module-auth` 曾把 provider 失败统一折叠成 `PhoneAuthError::Store``api-server` 再映射为 `500 Internal Server Error`,前端只能看到登录失败。
## 根因
短信 provider 失败不是认证仓储内部错误:
1. 阿里云配置缺失或配置非法属于服务配置问题。
2. 阿里云返回频控、网关失败或业务失败属于上游短信 provider 问题。
3. 这些错误不应被映射成 `Store`,否则 HTTP 层无法区分真实内部错误与外部 provider 失败。
## 修复
`module-auth` 新增短信 provider 错误分类:
1. `PhoneAuthError::SmsProviderInvalidConfig`
2. `PhoneAuthError::SmsProviderUpstream`
`api-server` 映射规则调整为:
1. provider 配置错误返回 `503 Service Unavailable`
2. provider 上游失败返回 `502 Bad Gateway`
3. 验证码不存在、错误、过期仍返回 `400`
4. 本地仓储或签发错误仍返回 `500`
## 本地排查
如果本地只想验证登录 UI 和账号链路,可以临时用 shell 环境覆盖真实短信 provider
```powershell
$env:SMS_AUTH_PROVIDER="mock"
npm run api-server
```
若要验证真实短信链路,保持 `SMS_AUTH_PROVIDER=aliyun`,并查看 `api-server` 日志中的:
1. `阿里云短信发送接口返回响应`
2. `阿里云短信发送接口返回业务失败`
3. `手机号验证码发送失败`
看到 `biz.FREQUENCY` / `check frequency failed` 时,说明请求已到达短信 provider但被 provider 频控或业务规则拒绝。
## 验收
1. `cargo test -p api-server phone_auth_sms_provider_errors_keep_upstream_http_semantics --manifest-path server-rs/Cargo.toml`
2. `cargo test -p api-server send_phone --manifest-path server-rs/Cargo.toml`
3. `cargo test -p api-server phone_login_creates_user_and_sets_refresh_cookie --manifest-path server-rs/Cargo.toml`
4. `cargo check -p api-server --manifest-path server-rs/Cargo.toml`
5. `npm run check:encoding`

View File

@@ -5,6 +5,7 @@
## 文档列表
- [RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md](./RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md):记录 `server-rs` Cargo 依赖集中配置口径,第三方版本和 workspace 内部 crate path 统一维护在根 `server-rs/Cargo.toml`,成员 crate 只保留 feature/optional 差异。
- [AUTH_GATE_LOGIN_RACE_GUARD_FIX_2026-05-09.md](./AUTH_GATE_LOGIN_RACE_GUARD_FIX_2026-05-09.md):记录 `AuthGate` 登录成功后又被旧 hydrate 覆盖回未登录态的竞态根因、版本号保护修复与回归测试。
- [VOLCENGINE_SPEECH_STREAMING_INTEGRATION_2026-05-08.md](./VOLCENGINE_SPEECH_STREAMING_INTEGRATION_2026-05-08.md):记录火山引擎大模型 ASR 双向流式、TTS WebSocket 双向流式和 TTS HTTP SSE 单向流式的后端代理、环境变量、协议帧和验收边界。
- [VECTOR_ENGINE_AUDIO_GENERATION_SUNO_VIDU_2026-05-08.md](./VECTOR_ENGINE_AUDIO_GENERATION_SUNO_VIDU_2026-05-08.md):记录视觉小说结果页接入 VectorEngine Suno 文生背景音乐与 Vidu 文生音效的接口、环境变量、后端路由、OSS 资产回写和前端弹层交互边界。
- [PROFILE_FEEDBACK_BACKEND_INTEGRATION_2026-05-08.md](./PROFILE_FEEDBACK_BACKEND_INTEGRATION_2026-05-08.md):冻结“我的”页签帮助与反馈入口的后端接入方案,覆盖 `POST /api/profile/feedback``profile_feedback_submission`、凭证图片 Data URL 校验和前端预览/提交边界。
@@ -163,6 +164,7 @@
- [PHONE_AUTH_AXUM_REAL_SMS_PROVIDER_DESIGN_2026-04-22.md](./PHONE_AUTH_AXUM_REAL_SMS_PROVIDER_DESIGN_2026-04-22.md):冻结 Rust `api-server + module-auth + platform-auth` 接入真实阿里云短信 provider 的 crate 边界、发送与校验职责、配置项和错误语义。
- [PHONE_SMS_ALIYUN_RESPONSE_FIELD_MAPPING_FIX_2026-04-23.md](./PHONE_SMS_ALIYUN_RESPONSE_FIELD_MAPPING_FIX_2026-04-23.md):记录 Rust `platform-auth` 把阿里云 PascalCase 响应字段误判成空值的问题根因,并冻结字段映射修复与回归标准。
- [PHONE_SMS_SEND_CODE_OBSERVABILITY_FIX_2026-04-23.md](./PHONE_SMS_SEND_CODE_OBSERVABILITY_FIX_2026-04-23.md):冻结手机号验证码发送链路的日志补强口径,确保 `api-server``module-auth``platform-auth` 能直接暴露发送前后与错误分类关键字段。
- [PHONE_SMS_PROVIDER_ERROR_HTTP_MAPPING_FIX_2026-05-08.md](./PHONE_SMS_PROVIDER_ERROR_HTTP_MAPPING_FIX_2026-05-08.md):记录真实短信 provider 返回 `UNKNOWN` / `biz.FREQUENCY` 时被误映射成登录 `500` 的根因,冻结 provider 配置错误 `503`、上游失败 `502` 的 HTTP 映射。
- [PHONE_SMS_DELIVERY_OBSERVABILITY_AND_RECEIPT_DESIGN_2026-04-22.md](./PHONE_SMS_DELIVERY_OBSERVABILITY_AND_RECEIPT_DESIGN_2026-04-22.md):冻结短信平台受理成功与最终送达状态的区分方式、追踪字段、送达回执接口和前端提示文案边界。
- [PHONE_SMS_REAL_PROVIDER_MANUAL_VERIFICATION_RUNBOOK_2026-04-23.md](./PHONE_SMS_REAL_PROVIDER_MANUAL_VERIFICATION_RUNBOOK_2026-04-23.md):冻结验证清单第一项“真实短信验证码链路”的本地启动、前端操作、日志观察点、通过标准与失败排查步骤。
- [ASSET_EXTERNAL_GENERATION_MANUAL_VERIFICATION_RUNBOOK_2026-04-23.md](./ASSET_EXTERNAL_GENERATION_MANUAL_VERIFICATION_RUNBOOK_2026-04-23.md):冻结验证清单第四项“图片、视频、动作真实外部生成”的人工联调口径,明确哪些入口已接真实外部图片服务、哪些入口仍是 Stage 1 占位链,以及前端点击路径、日志观察点和通过标准。