feat(access): 冷启动 httpOnly cookie 静默续期接线 - #65
Merged
Merged
Conversation
- refresh() 改 cookie 优先:去早退,无内存 token 也发空 body 续期,内存 token 作 body 兜底 - login/refresh/logout 加 withCredentials,收发后端 __Host-gocell_rt cookie - bootstrap.ts/main.ts 注释同步:bootstrapSession 经 cookie 主动续期,await-before-mount 不闪 /login - access README 同步;单测覆盖 cookie 模式 + 冷启动;e2e 冷启动整页重载冒烟 依赖后端 BR-005 (ghbvf/gocell#1278,已交付)。契约未改(请求体未类型化,{} 可编译); 跨源 CORS 联调后端未实装(backend #1680)。 Refs #27 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 tasks
Owner
Author
|
契约漂移跟踪 issue:#66(多租户 |
六维度 review(2 reviewer,diff 216 行 → 2 档)Cx2 修复: - refresh() 请求体显式标注 HttpAuthRefreshV1Request | Record<string,never>, 契约重命名时编译期暴露(去回了对 @gocell/contracts 的引用) - refresh() 加 timeout 兜底:bootstrapSession await 在 app.mount() 前, 无超时的 http 实例遇后端挂起会永久白屏;本 PR 让 mount 依赖网络故放大该既有风险 - bootstrap.ts JSDoc 注明 refresh() 永不 reject(.finally(mount) 依赖此不变量) a11y 维度无 finding;withCredentials per-request 归属经确认合理(HTTP 单点未破)。 遗留(见 PR 评论):@gocell/request 全局 timeout、bootstrap 测试重置钩、e2e __Host- cookie 保真度。 Refs #27 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Owner
Author
Review 汇总(六维度,2 reviewer,diff 216 行 → 200–600 档)维度切分 A:Vue 模板 + TS + 设计/产品;B:包边界 + a11y + 性能。两位 reviewer 一致认定核心逻辑(withCredentials 接线、bootstrap 幂等单飞、refreshPath 排除递归、best-effort logout、cookie-first 分支)正确无 P0 阻断。a11y 维度无 finding; 已修(本 PR
|
| Cx | Finding | 修复 |
|---|---|---|
| Cx2 | refresh() 请求体未引用契约类型,{} 分支逃逸约束 |
body 显式标注 HttpAuthRefreshV1Request | Record<string, never>,schema 重命名编译期暴露 |
| Cx2 | app.mount() await refresh 但 http 实例无 timeout → 后端挂起永久白屏(本 PR 让 mount 依赖网络,放大既有风险) |
refresh() 加 timeout: 10s 兜底 |
| Cx3 | .finally(mount) 依赖 refresh 永不 reject 的隐式不变量 |
bootstrapSession JSDoc 注明 |
遗留(不在本 PR,已记录)
| Cx | Finding | 处置 |
|---|---|---|
| Cx2/Cx3 | @gocell/request http 实例无全局默认 timeout(既有问题,全请求面) |
建议另开 issue(本 PR 已就 refresh 路径兜底) |
| Cx3 | bootstrapSession 无 _resetBootstrapForTesting 钩(测试靠 vi.resetModules(),现状可用) |
暂不加,避免 YAGNI 导出 |
| Cx3 | e2e 用 plain gocell_rt 而非真实 __Host-gocell_rt(localhost http 无法设 __Host-/Secure),保真度有限 |
测试注释已说明;真·保真需 HTTPS dev proxy |
| Cx3 | e2e sessionData() 返回 Record<string,unknown> 未对齐契约 |
已评估后不改:stub 故意不强约束于契约;且 e2e 现无任何 @gocell/* import,为此引入跨包 import 性价比低 |
| OUT_OF_SCOPE | bootstrap.spec 内联类型 / useAuthStore.spec mock 双重强转 |
既有测试脚手架,非本 PR 引入,不顺手重构 |
Owner
Author
CI 说明
两个 readonly 检查的红与本 PR 无关:本分支 |
This was referenced Jun 17, 2026
…tart-cookie # Conflicts: # packages/access/src/stores/useAuthStore.ts
This was referenced Jun 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
落地 #27 / #12 H2「冷启动无感续期」:依赖后端 BR-005(ghbvf/gocell#1278,已交付 httpOnly refresh cookie
__Host-gocell_rt)完成前端接线。useAuthStore.refresh()改为 cookie 优先:去掉if (!_refreshToken.value) return null早退——无内存 token(冷启动/刷新/新标签)也发POST /sessions/refresh(空 body)靠 cookie 续期;有内存 token 时作 body 兜底(对接后端双通道)。加withCredentials。login()/logout()加withCredentials:分别收后端Set-Cookie与 cookie 清除(Max-Age=0)。main.tsawait bootstrapSession()再app.mount()),本 PR 让它真正生效;同步bootstrap.ts/main.ts过时注释。await-before-mount 时序保证首个守卫在续期之后跑 → 冷启动不闪/login。@gocell/accessREADME 同步。涉及包
@gocell/access:stores/useAuthStore.ts(核心)+ READMEapps/web:bootstrap.ts/main.ts注释 +bootstrap.spec.ts+e2e/auth.spec.ts是否触及
packages/contracts/否。 请求体未类型化(
http.post的data为any),{}可正常编译,无需改契约。Test plan
pnpm -F @gocell/access typecheckpnpm -F @gocell/access test --run(292)pnpm -w lint/pnpm -w typecheckpnpm -w test --run(1755)pnpm -F @gocell/web buildnpx playwright test e2e/auth.spec.ts(6,含 2 个冷启动冒烟:重载经 cookie 续期停留受保护页 / 无 cookie 落/login)验收对照
登录后整页重载 → 会话经 cookie 自动续期、停留受保护页(e2e 实测通过);无有效 cookie → 落
/login。token 全程不入 localStorage / sessionStorage(铁律单测守门)。遗留(非本 PR 可做)
withCredentials+Access-Control-Allow-Credentials联调:后端 CORS 中间件未实装(backend #1680)。本 PR 完成同源(dev proxy / prod gateway,实际部署模型)+ 前端接线;withCredentials为跨源 forward-compat 预留。Closes #27