fix auth login state race
This commit is contained in:
37
docs/technical/AUTH_GATE_LOGIN_RACE_GUARD_FIX_2026-05-09.md
Normal file
37
docs/technical/AUTH_GATE_LOGIN_RACE_GUARD_FIX_2026-05-09.md
Normal 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`
|
||||
@@ -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`
|
||||
@@ -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 占位链,以及前端点击路径、日志观察点和通过标准。
|
||||
|
||||
Reference in New Issue
Block a user