Skip to content

docs(plugin-auth): 纠正 databaseHooks 的中间件绕过断言 (#4802) - #4942

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4802-auth-hooks-doc-drift
Aug 3, 2026
Merged

docs(plugin-auth): 纠正 databaseHooks 的中间件绕过断言 (#4802)#4942
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4802-auth-hooks-doc-drift

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #4802

先核实:议题的前提今天成立

议题要求「实施前对着当时的 HEAD 重跑逐跳核对」。已在 origin/main @ 6bc93dc5c 重跑,议题的断言成立,注释是错的

# 位置 事实
1 packages/objectql/src/plugin.ts:243,245 registerService('objectql', this.ql)registerService('data', this.ql) 注册的是同一个 this.qlinitif (!this.ql) 只 new 一次)。全仓 grep "registerService('data'" 只有这一处,不存在第二个 data 提供者
2 packages/plugins/plugin-auth/src/auth-plugin.ts:316 const dataEngine = ctx.getService< IDataEngine >('data'),原样传进 createObjectQLAdapterFactory
3 packages/plugins/plugin-auth/src/objectql-adapter.ts:331,522 写入是 await dataEngine.insert(objectName, …)——没有任何 bypass / skip-middleware 选项可传
4 packages/objectql/src/engine.ts:4351,4365 insert() 整个函数体包在 executeWithMiddleware(opCtx, …) 里;executeWithMiddleware(1199)唯一的过滤条件是 !m.object || m.object === '*' || m.object === ctx.object——registerMiddleware push 进的正是它 filter 的那个数组

结论:ql.registerMiddleware(fn, { object: 'sys_user' }) 对 better-auth 的写入触发。生命周期钩子同理(triggerHooksexecuteWithMiddleware 的 executor 内部)——同包内 auth-plugin.ts 的 SCIM 身份来源印戳就建立在这个事实上。

一个议题没提、但更该写进注释的限定

适配器不是裸 engine:objectql-adapter.ts:253withSystemContext() 给每次调用注入 context.isSystem: true(由 objectql-adapter.test.ts 逐调用钉住)。而所有授权类中间件都按设计对 isSystem 提前返回——plugin-security/src/security-plugin.ts:770 开头就是 if (opCtx.context?.isSystem) return next(),sharing 与 ADR-0092 identity write guard 同理。

所以真实边界比「二选一」更细,注释按这个写:链会跑,钩子会跑;但一个 gate 在 isSystem 上的中间件仍然什么都看不见,不 gate 的会跑。 这条是读者真正需要的信息——旧注释把它粗暴地表述成「适配器绕过整条链」,于是把「不 gate 的中间件」这半边也一并判死了。

改法:保留纪律,更换理由

按议题要求不动结论。「用 databaseHooks.user.create.after,不要在 sys_user 上挂中间件」今天依然对,但正当理由换成 ADR-0093 D2:user 生命周期不变量有唯一 owner(reconcile-membership.ts,composed 进 user.create.after),因为那是每条创建路径(自助注册 / admin create-user / import / SSO JIT)都已经流经的同一条缝;每条路径各写一遍,正是 D2 消灭掉的形态。

「refute in place」而不是直接删——理由

那句话存在的目的是提醒后人一个隐蔽边界。边界变了,但这段历史仍有防错价值,所以选择在原地点名反驳、而不是静默删除:

这条断言已被抄进 cloud 的 AGENTS.md「血的教训」与 personal-org-hook.ts 文件头,cloud#1012 的两轮实现调研都因它直接判死了中间件路线。一个从那些抄写件过来的读者,如果在 framework 这边什么都没看到,无法区分「这句被删掉是因为它错了」和「这句本来就不在这」——默认假设反而会是 framework 的注释才是过时的那份。点名 + 逐跳锚点让这个判断可核对。等 cloud 侧(cloud#1022)的两处抄写清掉后,这段可以再收敛成一行。

改动

  • packages/plugins/plugin-auth/src/auth-manager.tsAuthManagerOptions.databaseHooks 的 JSDoc(议题指名的那处)
  • packages/plugins/plugin-auth/src/auth-manager.tscomposeDatabaseHooks 旁的接线注释(同一断言的第二处抄写)
  • packages/plugins/plugin-auth/src/auth-plugin.tsAuthPluginOptions.databaseHooks 的 JSDoc(第三处抄写)
  • .changeset/auth-database-hooks-middleware-claim-corrected.md(空 frontmatter,注释改动不发版)

仅注释,无运行时行为改变。

验证

$ pnpm --filter @objectstack/plugin-auth typecheck
> tsc --noEmit          # 无输出

$ pnpm --filter @objectstack/plugin-auth test
 Test Files  29 passed (29)
      Tests  658 passed (658)

$ npx eslint packages/plugins/plugin-auth/src --no-inline-config    # 无输出

$ node scripts/check-adr-anchors.mjs
check-adr-anchors: OK (18 anchored file(s), every governing ADR still referenced).

首次 typecheck 报了一片 Cannot find module '@objectstack/spec/system' + TS7006——是 worktree 里上游包未构建、缺 .d.ts 所致(AGENTS.md 记过这个陷阱)。pnpm --filter @objectstack/plugin-auth^... build 之后全绿。

未加新测试:本包不依赖 @objectstack/objectqlobjectql-adapter.test.ts 自己注明了这一点并用同形 fake 代替),所以「中间件会触发」这个跨包事实无法在本包内钉住。已覆盖的那半边——适配器只调 dataEngine.insert(...)、第三参数恰好是 { context: { isSystem: true } }、没有别的选项——现有测试用 toHaveBeenCalledWith 精确断言,注释已指向它。

顺带发现(已另开单,未在本 PR 修)

#4940 — 同一包内 admin-user-endpoints.tsadmin-import-users.ts 有两处同类断言:「better-auth writes bypass the ObjectQL lifecycle hooks that plugin-audit subscribes to」。初步反证同上(钩子在 executor 内触发;plugin-audit 的 writeAudit 无 object 过滤且 SKIP_OBJECTS 不含 sys_user)。但那两处的结论大概率仍成立(plugin-audit 是可选插件;显式行的 metadata 与通用行不是一回事),落在不同机制面上,需要单独核实后同样「保留纪律、更换理由」,不塞进本 PR。


Generated by Claude Code

…4802)

The `databaseHooks` JSDoc justified "use this seam, not an ObjectQL
middleware" with a mechanism claim that no longer holds: better-auth's
adapter "goes through `dataEngine` directly, bypassing the
`ql.registerMiddleware` chain".

Re-verified against main: ObjectQLPlugin registers ONE instance under both
`objectql` and `data`; AuthPlugin hands that same instance to
createObjectQLAdapterFactory; the adapter writes with a plain
`dataEngine.insert(objectName, …)` (no bypass option); and
`ObjectQL.insert()` wraps its body in `executeWithMiddleware()`, filtered
only by object name. The chain and the lifecycle hooks do fire.

The rule is unchanged — user-lifecycle invariants belong in
`user.create.after` — but the reason is now ADR-0093 D2 (one owner on the
one seam every creation path flows through). The narrower surviving fact is
written down in place of the false one: adapter writes carry
`context.isSystem: true`, so authorization middlewares early-return by
design. The stale sentence is refuted in place rather than deleted because
it was copied into cloud's agent-facing docs (cloud#1012 / cloud#1022).

Comments only; no runtime behaviour change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018iARDqtrhQgz6fVHDeDkbQ
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 3, 2026 4:57pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling size/s labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-auth.

9 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/deployment/cli.mdx (via @objectstack/plugin-auth)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/plugin-auth)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/plugin-auth)
  • content/docs/permissions/authentication.mdx (via @objectstack/plugin-auth)
  • content/docs/permissions/sso.mdx (via @objectstack/plugin-auth)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-auth)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-auth)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-auth)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-auth)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

2 participants