docs: sync genarrative shared skills
Some checks failed
CI / verify (pull_request) Has been cancelled
Some checks failed
CI / verify (pull_request) Has been cancelled
This commit is contained in:
132
.hermes/skills/genarrative-auth-session-flow/SKILL.md
Normal file
132
.hermes/skills/genarrative-auth-session-flow/SKILL.md
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
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 未过期时打开网页未触发每日登录埋点的根因与修复案例。
|
||||
@@ -0,0 +1,75 @@
|
||||
# Session restore 每日登录埋点案例(2026-05-08)
|
||||
|
||||
## 现象
|
||||
|
||||
用户反馈:已经登录且 cookie 没过期时,打开网页没有触发每日登录埋点。
|
||||
|
||||
## 根因
|
||||
|
||||
当本地 access token 仍有效时,前端 `AuthGate` 恢复登录态只会复用现有 token 并请求 `/api/auth/me`。`/api/auth/me` 只读取当前用户,不会进入 `POST /api/auth/session/refresh`,因此后端 refresh handler 中的每日登录埋点不会执行。
|
||||
|
||||
关键误判点:后端已经在 refresh cookie 续期写埋点,不等于“打开网页”一定会触发。前端必须实际调用 refresh/session 接口。
|
||||
|
||||
## 修复模式
|
||||
|
||||
1. 在 `src/services/apiClient.ts` 暴露强制 refresh 方法,例如:
|
||||
|
||||
```ts
|
||||
export async function refreshStoredAccessToken() {
|
||||
return refreshAccessToken();
|
||||
}
|
||||
```
|
||||
|
||||
2. 在 `src/components/auth/AuthGate.tsx` 的 hydrate/restore 中,使用:
|
||||
|
||||
```ts
|
||||
await refreshStoredAccessToken();
|
||||
const nextSession = await getCurrentAuthUser();
|
||||
```
|
||||
|
||||
而不是只调用:
|
||||
|
||||
```ts
|
||||
await ensureStoredAccessToken();
|
||||
const nextSession = await getCurrentAuthUser();
|
||||
```
|
||||
|
||||
3. 保留 `ensureStoredAccessToken()` 给普通受保护请求兜底;不要把所有请求都改成强制 refresh。
|
||||
|
||||
4. 确认 `server-rs/crates/api-server/src/refresh_session.rs` 在 rotate refresh session 成功且新 access token 签发成功后调用每日登录埋点 helper。
|
||||
|
||||
5. 确认 `server-rs/crates/spacetime-module/src/runtime/profile.rs` 中 `get_profile_task_center` 不再顺手写 `daily_login`,避免任务中心读取污染登录埋点。
|
||||
|
||||
## 测试
|
||||
|
||||
前端测试重点:
|
||||
|
||||
- `AuthGate` 会等待 `refreshStoredAccessToken()` 完成后才暴露已恢复用户内容。
|
||||
- `AUTH_STATE_EVENT` 触发 hydrate 时仍保持已挂载平台内容和本地 tab 状态。
|
||||
|
||||
命令:
|
||||
|
||||
```bash
|
||||
npm run test -- AuthGate.test.tsx
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
后端/SpacetimeDB 编译:
|
||||
|
||||
```bash
|
||||
cd server-rs && cargo check -p spacetime-module
|
||||
cd server-rs && cargo check -p api-server
|
||||
```
|
||||
|
||||
全局检查:
|
||||
|
||||
```bash
|
||||
npm run check:encoding
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## 注意
|
||||
|
||||
- Vitest 0.34 不支持 Jest 的 `--runInBand` 参数;命令里不要加。
|
||||
- 埋点失败只能 warning,不能阻断登录态恢复。
|
||||
- 如果后续发现打开页面产生过多 refresh 请求,需要在产品口径和埋点口径之间重新设计节流;但不能退回“只读 `/api/auth/me` 却期待写登录埋点”的状态。
|
||||
Reference in New Issue
Block a user