docs(plugin-auth): 纠正 databaseHooks 的中间件绕过断言 (#4802) - #4942
Merged
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 9 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
xuyushun441-sys
marked this pull request as ready for review
August 3, 2026 16:58
xuyushun441-sys
enabled auto-merge
August 3, 2026 16:58
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.
Fixes #4802
先核实:议题的前提今天成立
议题要求「实施前对着当时的 HEAD 重跑逐跳核对」。已在
origin/main@6bc93dc5c重跑,议题的断言成立,注释是错的:packages/objectql/src/plugin.ts:243,245registerService('objectql', this.ql)与registerService('data', this.ql)注册的是同一个this.ql(init里if (!this.ql)只 new 一次)。全仓grep "registerService('data'"只有这一处,不存在第二个data提供者packages/plugins/plugin-auth/src/auth-plugin.ts:316const dataEngine = ctx.getService< IDataEngine >('data'),原样传进createObjectQLAdapterFactorypackages/plugins/plugin-auth/src/objectql-adapter.ts:331,522await dataEngine.insert(objectName, …)——没有任何 bypass / skip-middleware 选项可传packages/objectql/src/engine.ts:4351,4365insert()整个函数体包在executeWithMiddleware(opCtx, …)里;executeWithMiddleware(1199)唯一的过滤条件是!m.object || m.object === '*' || m.object === ctx.object——registerMiddlewarepush 进的正是它 filter 的那个数组结论:
ql.registerMiddleware(fn, { object: 'sys_user' })对 better-auth 的写入会触发。生命周期钩子同理(triggerHooks在executeWithMiddleware的 executor 内部)——同包内auth-plugin.ts的 SCIM 身份来源印戳就建立在这个事实上。一个议题没提、但更该写进注释的限定
适配器不是裸 engine:
objectql-adapter.ts:253的withSystemContext()给每次调用注入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.ts—AuthManagerOptions.databaseHooks的 JSDoc(议题指名的那处)packages/plugins/plugin-auth/src/auth-manager.ts—composeDatabaseHooks旁的接线注释(同一断言的第二处抄写)packages/plugins/plugin-auth/src/auth-plugin.ts—AuthPluginOptions.databaseHooks的 JSDoc(第三处抄写).changeset/auth-database-hooks-middleware-claim-corrected.md(空 frontmatter,注释改动不发版)仅注释,无运行时行为改变。
验证
首次 typecheck 报了一片
Cannot find module '@objectstack/spec/system'+ TS7006——是 worktree 里上游包未构建、缺.d.ts所致(AGENTS.md 记过这个陷阱)。pnpm --filter @objectstack/plugin-auth^... build之后全绿。未加新测试:本包不依赖
@objectstack/objectql(objectql-adapter.test.ts自己注明了这一点并用同形 fake 代替),所以「中间件会触发」这个跨包事实无法在本包内钉住。已覆盖的那半边——适配器只调dataEngine.insert(...)、第三参数恰好是{ context: { isSystem: true } }、没有别的选项——现有测试用toHaveBeenCalledWith精确断言,注释已指向它。顺带发现(已另开单,未在本 PR 修)
#4940 — 同一包内
admin-user-endpoints.ts与admin-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