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

vercelBot commented Jul 18, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specBuildingBuildingPreview, CommentJul 18, 2026 6:26am

Request Review

@github-actionsgithub-actionsBot 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude