Skip to content

feat(tui): M0A.5b TUI 登录 - #84

Merged
iancaoo merged 3 commits into
datagallery-lab:mainfrom
wangjk9527:feat/m0a5b-tui-login
Jul 28, 2026
Merged

feat(tui): M0A.5b TUI 登录#84
iancaoo merged 3 commits into
datagallery-lab:mainfrom
wangjk9527:feat/m0a5b-tui-login

Conversation

@wangjk9527

@wangjk9527 wangjk9527 commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • 为 TUI 增加正式账号登录、Session 持久化、交互注册引导、账号切换与 /logout
  • REST、AG-UI 与启动 preflight(含 run-defaults)共用 AuthenticatedTransport;身份探测走 GET /api/v1/me
  • 删除离线 Demo 模式,并增加 Web/TUI 会话共享 smoke
  • 复审加固:Cookie Max-Age=0 吊销、session-invalid 清理失败仍通知、logout/auth-required 后强制交互登录

Test plan

  • npm --workspace @datafoundry/tui test(100 passed)
  • npm run test:auth-foundation(24 passed)
  • npm run smoke:tui-auth-sharing(需本地栈)
  • CI green on this PR

@wangjk9527
wangjk9527 force-pushed the feat/m0a5b-tui-login branch 2 times, most recently from 114eba5 to 04a9a1c Compare July 23, 2026 16:58
@wangjk9527
wangjk9527 force-pushed the feat/m0a5b-tui-login branch 3 times, most recently from ffc491b to 81f2269 Compare July 24, 2026 16:32
wangjk9527 added a commit to wangjk9527/datafoundry that referenced this pull request Jul 26, 2026
接入正式认证登录/会话恢复,并按复审加固:
- 认证请求 redirect:manual,逐跳拒绝 HTTPS→HTTP 与跨 origin
- AbortController 覆盖完整 body 读取;session 隔离/重置与保存同锁,锁支持过期回收
- CSRF 403 若 jar token 已更新则直接重试;session-invalid 去重先于清理副作用

基于 main reset 后重提原 datagallery-lab#84,rebase 到 DataLink 外置 deploy + M0A.5a 栈上;
同步清理 TUI 指南中残留的离线 Demo 模式表述。

Co-authored-by: wing <wangjk9527@users.noreply.github.com>
@wangjk9527
wangjk9527 force-pushed the feat/m0a5b-tui-login branch from 81f2269 to 62167a5 Compare July 26, 2026 08:11
@wangjk9527
wangjk9527 marked this pull request as ready for review July 26, 2026 08:11
wangjk9527 added a commit to wangjk9527/datafoundry that referenced this pull request Jul 27, 2026
接入正式认证登录/会话恢复,并按复审加固:
- 认证请求 redirect:manual,逐跳拒绝 HTTPS→HTTP 与跨 origin
- AbortController 覆盖完整 body 读取;session 隔离/重置与保存同锁,锁支持过期回收
- CSRF 403 若 jar token 已更新则直接重试;session-invalid 去重先于清理副作用

基于 main reset 后重提原 datagallery-lab#84,rebase 到 DataLink 外置 deploy + M0A.5a 栈上;
同步清理 TUI 指南中残留的离线 Demo 模式表述。

Co-authored-by: wing <wangjk9527@users.noreply.github.com>
@wangjk9527
wangjk9527 force-pushed the feat/m0a5b-tui-login branch from 62167a5 to 323d32b Compare July 27, 2026 06:45
wangjk9527 added a commit to wangjk9527/datafoundry that referenced this pull request Jul 27, 2026
接入正式认证登录/会话恢复,并按复审加固:
- 认证请求 redirect:manual,逐跳拒绝 HTTPS→HTTP 与跨 origin
- AbortController 覆盖完整 body 读取;session 隔离/重置与保存同锁,锁支持过期回收
- CSRF 403 若 jar token 已更新则直接重试;session-invalid 去重先于清理副作用

基于 main reset 后重提原 datagallery-lab#84,rebase 到 DataLink 外置 deploy + M0A.5a 栈上;
同步清理 TUI 指南中残留的离线 Demo 模式表述。

Co-authored-by: wing <wangjk9527@users.noreply.github.com>
@wangjk9527
wangjk9527 force-pushed the feat/m0a5b-tui-login branch from 323d32b to 893ea81 Compare July 27, 2026 08:45
wangjk9527 and others added 2 commits July 28, 2026 11:02
接入正式认证登录/会话恢复,并按复审加固:
- 认证请求 redirect:manual,逐跳拒绝 HTTPS→HTTP 与跨 origin
- AbortController 覆盖完整 body 读取;session 隔离/重置与保存同锁,锁支持过期回收
- CSRF 403 若 jar token 已更新则直接重试;session-invalid 去重先于清理副作用

基于 main reset 后重提原 datagallery-lab#84,rebase 到 DataLink 外置 deploy + M0A.5a 栈上;
同步清理 TUI 指南中残留的离线 Demo 模式表述。

Co-authored-by: wing <wangjk9527@users.noreply.github.com>
Max-Age=0/过期 Expires 的 Set-Cookie 现在会删除 jar 条目;session-invalid
清理失败仍通知 auth-required;logout/auth-required 后强制交互登录,避免
磁盘残留会话被静默恢复。
@wangjk9527
wangjk9527 force-pushed the feat/m0a5b-tui-login branch from 893ea81 to 76d4666 Compare July 28, 2026 03:02
AuthenticatedTransport 默认 redirect:follow 时,开放重定向目标的
Set-Cookie 会写入 jar 并覆盖 df_session。改为 redirect:manual,与
auth-client 一致,避免跨 origin 响应毒化会话。
@wangjk9527
wangjk9527 requested review from SymbolZH, Wistomize and iancaoo and removed request for SymbolZH July 28, 2026 03:19
@iancaoo

iancaoo commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review overview — M0A.5b TUI 登录

整体审了 47 个文件,并在 PR 分支的临时 worktree 里实跑了 tsc --noEmit -p apps/tui/tsconfig.json(exit 0,零错误)和 TUI 单测(101 pass / 0 fail,PR body 写 100,实际 101)。未发现阻断性功能问题,未提 changes-requested,下面是结论。

做了什么

新增 apps/tui/src/auth/

  • TuiAuthClientGET /auth/statusPOST /auth/login(带 client: "tui")、GET /mePOST /auth/csrf/refreshPOST /auth/logout;手动跟随重定向,assertSafeAuthRedirect 拒绝降级 / 跨域 / 带凭据 / 非回环 HTTP。
  • AuthenticatedTransport — REST + AG-UI + preflight 共用的 fetch 层;自动挂 cookie/CSRF、CSRF_INVALID 单次重试、CSRF refresh 单飞、late-subscriber 的 sticky onAuthRequired 回放;全程 redirect: "manual"(防 open-redirect cookie 投毒)。
  • TuiSessionStore — 按 API base URL 分键的 JSON 缓存;文件锁(PID 失活回收 + token 守护释放)、原子 0o600 写、符号链接拒绝、坏文件隔离、并发 save 合并。
  • TuiCookieJar — 认 Max-Age=0 / 过期 Expires 以在 logout 时清 jar;畸形 percent-encoding 回退原值不抛错。
  • interactive-login — readline 提示;密码回显抑制走 stdin-TTY setRawMode(无 TTY 时直接拒绝读密码);注册开浏览器;限流不自动循环。
  • bootstrap / createAuthController — 缓存会话经 /me 恢复(带过期容差);账号切换时 best-effort 远端吊销旧 session;logout 三态(complete / remote-failed / local-cleanup-failed)。
  • runTui 装配 — 可重入认证循环、--no-auto-login、API 不可达恢复菜单(retry / other-address / quit)、auth-required → 强制交互重登(绝不静默恢复可能过期的磁盘 session)。
  • 删除离线 demo 模式(DemoCopilotKitClient / seedDemoState 删除;--demo 现在报错并退出)。新增 no-bare-fetch.guard.test.ts 强制非 auth 的 TUI 模块都走注入的 fetch。

正确性 / 安全 — 通过

  • Open-redirect / cookie 投毒防御正确AuthenticatedTransport.send 始终 redirect: "manual",跳到别的 origin 不会把它的 Set-Cookie 写进 jar 覆盖 df_session/df_csrf(测试 uses redirect:manual so open redirects cannot poison the cookie jar 锁住)。TuiAuthClient.performFetch 逐跳校验(先查降级再查跨域,顺序对),跳数上限 5,非 GET 拒绝 method-changing 重定向,跨域 307 不转发 password body(测试 bodies === 1)。
  • 超时覆盖到 body 读完withTimeout 把 fetch + readJson 一起包住,慢 body 流也会被 abort(测试 keeps timeout armed through body parse 证明 abort 在 body 中途触发)。
  • CSRF 重试正确fetchresponse.clone().json()CSRF_INVALID,不消费掉 retry 判定用的 body;不可重放的流 body 走 invalidate 而非重试;单飞 refresh + "peer 已推进 jar" 检查避免重复 refresh。并发单飞用例覆盖。
  • Sticky auth-requiredsessionInvalidNotifiedrunSessionInvalid 之前置位,并发 401 共享一条清理路径;晚注册的 listener 恰好回放一次。测试覆盖含 onSessionInvalid 清理抛错的场景。
  • Session store 并发:排他 open(lock, "wx") + PID 失活回收 + token 守护释放(原始持有者不会误删被回收的锁);隔离在锁内做;原子 temp+rename+fchmod 0o600;符号链接拒绝。并发 quarantine+save、stale-lock 回收均有测试。
  • 密码处理createSecurePrompt 在 stdin 无法隐藏回显时直接拒绝读密码;用 muted stdout 接收密码;finally 还原 raw mode。测试确认密码不出现在 stdout。sanitizeErrorMessagedf_session=/df_csrf= 从给用户看的错误文本里 redact。
  • logout 三态createAuthController.logout 区分远端失败(网络)与本地清理失败(磁盘),给 UI 明确的 retry 路径;cookieJar.clear() 无论如何都做。

次要观察(非阻断,未提 comment)

  • completeInteractiveLoginloginClient.login()(设 jar)在 sessionStore.save 之前;若 save 抛错,函数 reject、内存里的新 cookie 被丢弃,用户看到 "Login failed"。磁盘缓存不会被写坏(测试 does not overwrite cache when login fails 锁住),重试时 bootstrap 重新提示——是清晰的失败信息而非静默损坏。
  • smoke:tui-auth-sharing 在 CI core-smoke-tests 跑,但 body 里勾的是 [ ](需本地栈)。它实际是同进程起 createApiServer 跑,CI 应该能过;勾选只是作者没在 runner 上确认,CI 会给结论。
  • no-bare-fetch.guard.test.tsglobalThis.fetch( 启发式偏窄(只匹配含 /api/ 的),但构造函数里默认 globalThis.fetch 是有意为之,没问题。

结论

无阻断性功能缺陷。 TUI typecheck 干净、101 单测全过;安全设计(manual-redirect 防 cookie 投毒、CSRF 单飞重试、超时覆盖 body 读取、sticky auth-required、session-store 文件锁)正确且有测试覆盖;离线 demo 删除干净并有 guard 测试防回归。仅 smoke:tui-auth-sharing 一项待 CI 确认,合并前等 CI 绿即可。

@iancaoo
iancaoo merged commit f2536cc into datagallery-lab:main Jul 28, 2026
4 checks passed
iancaoo pushed a commit that referenced this pull request Jul 28, 2026
接入正式认证登录/会话恢复,并按复审加固:
- 认证请求 redirect:manual,逐跳拒绝 HTTPS→HTTP 与跨 origin
- AbortController 覆盖完整 body 读取;session 隔离/重置与保存同锁,锁支持过期回收
- CSRF 403 若 jar token 已更新则直接重试;session-invalid 去重先于清理副作用

基于 main reset 后重提原 #84,rebase 到 DataLink 外置 deploy + M0A.5a 栈上;
同步清理 TUI 指南中残留的离线 Demo 模式表述。

Co-authored-by: wing <wangjk9527@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants