Files
Genarrative/.hermes/skills/genarrative-auth-session-flow/SKILL.md
历冰郁-hermes版 7e35231dfe
Some checks failed
CI / verify (pull_request) Has been cancelled
docs: sync genarrative shared skills
2026-05-08 17:39:49 +08:00

133 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: genarrative-auth-session-flow
description: 在 Genarrative 中排查或修改登录、access token、refresh cookie、AuthGate 会话恢复、登录态刷新、认证埋点链路时使用。
version: 1.0.0
author: Hermes Agent
license: MIT
metadata:
hermes:
tags: [Genarrative, auth, session, cookie, refresh-token, AuthGate, tracking]
related_skills: [systematic-debugging, test-driven-development, genarrative-profile-features]
---
# Genarrative 认证会话与登录埋点链路
用于 Genarrative 中登录、会话恢复、refresh cookie 续期、access token 补票、AuthGate 恢复登录态,以及每日登录/认证相关埋点的排查与修改。
## 适用场景
- 用户反馈登录态、cookie、自动续期、刷新页面后状态异常。
- 修改 `AuthGate``apiClient``authService` 或 Rust `api-server` 认证接口。
- 排查“已登录但打开网页没有触发登录埋点”等 session restore 场景。
- 修改手机验证码登录、密码登录、微信登录、重置密码后自动登录、refresh session rotate。
- 需要判断某个前端动作是否真正调用了后端 refresh/session 或埋点 procedure。
## 关键代码路径
前端:
- `src/components/auth/AuthGate.tsx`
- 登录态 hydrate / restore 的入口。
- 监听 `AUTH_STATE_EVENT` 后重新 hydrate。
- 是否先 refresh、再 `/api/auth/me`,决定打开页面是否进入后端 refresh 链路。
- `src/services/apiClient.ts`
- access token 本地保存、`ensureStoredAccessToken()``refreshStoredAccessToken()``fetchWithApiAuth()`
- `ensureStoredAccessToken()` 有 token 时会直接复用,不一定触发后端 refresh。
- `refreshStoredAccessToken()` 应直接调用 refresh 接口,用于必须轮换 cookie / 写续期埋点的场景。
- `src/services/authService.ts`
- `getCurrentAuthUser()` 请求 `/api/auth/me`
- 登录、登出、账号安全相关 API client。
后端:
- `server-rs/crates/api-server/src/auth_session.rs`
- 创建 refresh cookie / access token。
- `record_daily_login_tracking_event_after_auth_success(...)` 统一写每日登录埋点;失败 warning不阻断认证流程。
- `server-rs/crates/api-server/src/refresh_session.rs`
- `POST /api/auth/session/refresh`
- rotate refresh session、签发新 access token、记录每日登录埋点。
- `server-rs/crates/api-server/src/auth_me.rs`
- `/api/auth/me` 只读取当前 access token 对应用户,不应假设它会触发 refresh 或登录埋点。
- `server-rs/crates/api-server/src/phone_auth.rs`
- `server-rs/crates/api-server/src/password_entry.rs`
- `server-rs/crates/api-server/src/password_management.rs`
- `server-rs/crates/api-server/src/wechat_auth.rs`
- 各真实认证成功入口。
- `server-rs/crates/spacetime-client/src/runtime.rs`
- `record_daily_login_tracking_event(user_id)` 调用 SpacetimeDB procedure。
- `server-rs/crates/spacetime-module/src/runtime/profile.rs`
- `record_daily_login_tracking_event_and_return` procedure。
- 任务中心读取不应污染每日登录埋点;如看到 `get_profile_task_center` 顺手写 `daily_login`,优先复核是否回归。
## 调试顺序
1. 先明确用户场景属于哪类:
- 新登录成功。
- cookie/access token 已过期后的自动刷新。
- 已登录且 cookie/access token 未过期时打开网页。
- 只调用 `/api/auth/me` 或某个受保护业务接口。
2. 查前端实际调用链,不要只看后端埋点点位:
- `AuthGate` hydrate 是否调用 `refreshStoredAccessToken()`
- 是否只是 `ensureStoredAccessToken()` + `/api/auth/me`
- `fetchWithApiAuth()` 是否因为已有 access token 而跳过 refresh
3. 查后端实际埋点点位:
- 登录成功入口是否在 session 创建后调用 helper。
- refresh session 是否在 rotate 与 access token 签发成功后调用 helper。
- 失败策略是否只 warning、不阻断响应。
4. 如涉及 SpacetimeDB procedure/table/binding按项目 SpacetimeDB skills 与文档同步检查绑定生成、`migration.rs`、private table 限制。
5. 修改前补齐 `docs/technical/` 中对应方案/根因;修改后同步更新。
## 关键经验:已登录打开网页也要主动 refresh 才能写登录埋点
常见误判:后端已经在 refresh cookie 续期时写每日登录埋点,就以为“打开网页”会触发埋点。
实际链路中,如果用户已经登录且本地 access token 还有效:
1. `ensureStoredAccessToken()` 会直接返回已有 token。
2. `AuthGate` 随后请求 `/api/auth/me`
3. `/api/auth/me` 只校验/读取用户,不会 rotate refresh session。
4. 因此后端 refresh/session 埋点不会触发。
若产品要求“已登录且 cookie 没过期时打开网页也记录登录埋点”,`AuthGate` 的 restore/hydrate 应主动调用 `refreshStoredAccessToken()`,再调用 `getCurrentAuthUser()`
## 每日登录埋点原则
- 真实登录成功:在 refresh session / access token 创建成功后记录。
- cookie refresh 续期:在 rotate refresh session 成功且新 access token 签发成功后记录。
- 已登录打开网页:前端必须主动走 refresh 续期链路,不能只请求 `/api/auth/me`
- `login_method` 对于 refresh 场景使用 refresh session 保存的 `issued_by_provider`
- 埋点失败不阻断登录、续期、会话恢复或 token 返回,只记录 warning。
- 任务中心读取不应作为登录埋点来源,避免后台查看/刷新任务中心污染登录数据。
## 测试与验证命令
按改动范围选择:
```bash
npm run test -- AuthGate.test.tsx
npm run typecheck
cd server-rs && cargo test -p api-server auth_session -- --nocapture
cd server-rs && cargo test -p api-server refresh_session_rotates_cookie_and_returns_new_access_token -- --nocapture
cd server-rs && cargo check -p api-server
cd server-rs && cargo check -p spacetime-client
cd server-rs && cargo check -p spacetime-module
npm run check:encoding
git diff --check
```
注意Vitest 0.34 不支持 Jest 的 `--runInBand`;不要把 `--runInBand` 加到 `npm run test -- AuthGate.test.tsx` 后面。
## 常见坑
1.`/api/auth/me` 当作 refresh它只读当前 access token不会写 refresh 埋点。
2. 只在后端 refresh handler 加埋点,但前端有有效 access token 时根本不调用 refresh。
3. `ensureStoredAccessToken()` 有 token 时会直接返回;需要强制 refresh 时应使用 `refreshStoredAccessToken()`
4. 在埋点 helper 中返回错误并阻断登录/续期,会破坏认证主链路。
5. refresh 场景把 `login_method` 写死为 password会丢失手机/微信来源。
6. 修改中文文件后忘记 `npm run check:encoding`
7. `cargo fmt -p api-server` 或前端测试可能让 `.env.local``.gitignore` 出现非业务改动;提交前用 `git status --short` 检查并撤回无关敏感/环境文件。
## 参考资料
- `references/session-restore-daily-login-tracking-2026-05-08.md`:已登录且 cookie 未过期时打开网页未触发每日登录埋点的根因与修复案例。