Skip to content

fix(plugin-auth): run better-auth adapter writes as system context (#3164)#3177

Merged
os-zhuang merged 1 commit into
mainfrom
claude/static-readonly-insert-exemption-xbjl5j
Jul 18, 2026
Merged

fix(plugin-auth): run better-auth adapter writes as system context (#3164)#3177
os-zhuang merged 1 commit into
mainfrom
claude/static-readonly-insert-exemption-xbjl5j

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

背景

关闭 #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?.isSystemtrue → 剥离命中,静默丢弃 better-auth 自己对 readonly sys_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 都被剥离。

dogfood HTTP 层不能直接触发用户可见症状:/auth/change-email 在 dev harness 返回 CHANGE_EMAIL_DISABLED,/auth/admin/ban-user 未挂载(404)。即在这些 better-auth 功能启用的部署里症状才显现;机制已在引擎层确证。

修复

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 上下文。

关系

测试 / 验证

  • plugin-auth 全套 460 passed(adapter 写入现断言携带 isSystem;新增「writes run as system」用例)。
  • dogfood 回归:two-doors-permission / single-tenant-identity-create / showcase-scope-depth / showcase-permission-seeding17 passed(sign-in、org/member 读、权限播种、provenance 403 均正常)。
  • 全量 pnpm build 71/71。

🤖 Generated with Claude Code


Generated by Claude Code

…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
@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Building Building Preview, Comment Jul 18, 2026 6:26am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 18, 2026
@github-actions

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/production-readiness.mdx (via @objectstack/plugin-auth)
  • content/docs/getting-started/cli.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/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants