fix(plugin-auth): run better-auth adapter writes as system context (#3164)#3177
Merged
Merged
Conversation
…3164) The better-auth ObjectQL adapter wrapped the engine so its READS carried `isSystem` (to bypass the control-plane org-scope read hook) but its WRITES passed through with no context. The static-`readonly` UPDATE strip (#2948) runs on any non-system update, and since the adapter carries no caller context `!ctx?.isSystem` was true — so the strip SILENTLY DROPPED better-auth's own writes to readonly `sys_user` columns: `email` (change-email), `banned` / `ban_reason` / `ban_expires` (admin ban). Those operations returned success but never persisted. Rename `withSystemReadContext` → `withSystemContext` (deprecated alias kept one release) and inject `isSystem` on insert/update/delete as well as reads. Correct because these are the identity authority's own writes: user-context writes to `managedBy: 'better-auth'` tables are already rejected upstream by the ADR-0092 identity write guard, so this path only ever carries better-auth's internal writes. Found while implementing #3043 (the INSERT-side readonly strip) — this is its UPDATE-side dual. Also corrects content/docs/data-modeling/fields.mdx, which still said "insert may still seed it" (stale after #3043 / PR #3162): a readonly column is now server-enforced on INSERT too, and seeding one at create requires a system context. Verified: plugin-auth suite 460 passed (adapter writes now assert isSystem); dogfood auth/identity/permission regression green (sign-in, org/member reads, permission seeding, two-doors provenance). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P5fRY4ctobCeFpBba1oGrz
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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:
|
os-zhuang
marked this pull request as ready for review
July 18, 2026 06:37
This was referenced Jul 18, 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.
背景
关闭 #3164(在 #3043 探索中发现)。better-auth 的 ObjectQL adapter(
plugin-auth/src/objectql-adapter.ts)对读注入了isSystem(绕开 control-plane 的 org-scope 读 hook),但写是无上下文透传。#2948 的静态 readonly UPDATE 剥离对任何非 system 的 update 生效;adapter 不带 caller context →!ctx?.isSystem为true→ 剥离命中,静默丢弃 better-auth 自己对 readonlysys_user列的写入:email(change-email)、banned/ban_reason/ban_expires(admin ban)。表现为「操作返回成功但数据没变」。复现(机制级,已实证)
用真实引擎(
ObjectQLPlugin,#2948 剥离生效)按 adapter 的确切调用形态engine.update(model, {id,...patch})(无 context)更新 readonly 列email/banned:驱动实际收到的 update payload 只剩{ id, updated_at }——email/banned都被剥离。修复
withSystemReadContext→ 重命名为withSystemContext(保留一个发布周期的 deprecated 别名),对insert/update/delete也注入isSystem(与读对称)。这是正确的:这些本就是身份权威自身的写入——对managedBy: 'better-auth'表的 user-context 写入已被上游 ADR-0092 的 identity write guard 拒绝,故此路径只承载 better-auth 的内部写入。同时修正
content/docs/data-modeling/fields.mdx:readonly行原写「insert may still seed it」,在 #3043(PR #3162,已合并)后已过时——readonly 现在在 INSERT 面也被服务端强制(经数据 API),创建时播种 readonly 列须 system 上下文。关系
readonlyon INSERT at the data-write ingress (#3043) #3162(INSERT 面)—— 本修复是其 UPDATE 侧对偶:安全/设计:静态 readonly 的 INSERT 豁免让审批/状态字段可在创建时被直接播种(比 #3003 少一步) #3043 因「内部/adapter 写入不带 isSystem」把 INSERT 剥离改到了外部入口;引擎级的 UPDATE 剥离无此腾挪,故让 adapter 显式声明其写入为 system。测试 / 验证
plugin-auth全套 460 passed(adapter 写入现断言携带isSystem;新增「writes run as system」用例)。two-doors-permission/single-tenant-identity-create/showcase-scope-depth/showcase-permission-seeding共 17 passed(sign-in、org/member 读、权限播种、provenance 403 均正常)。pnpm build71/71。🤖 Generated with Claude Code
Generated by Claude Code