fix(auth): close the sso/scim parity hole; provision sys_scim_provider.provider_key (#3653) - #3688
Merged
Merged
Conversation
…r.provider_key (#3653) The better-auth parity gate (#3647) shipped with a known hole: `@better-auth/sso` and `@better-auth/scim` accept no `schema` option, so `getAuthTables()` cannot see them and they were excluded with a comment. A comment is not a gate — filed as #3653 rather than left buried, and closed here. Both plugins DO expose their own `.schema`, and the adapter bridges them by mechanically camelCase→snake_casing every field of a bridged model (`objectql-adapter.ts`). That mechanical rule — not any hand-written mapping — is what decides the column they write, so the gate now reads the former and applies the latter. What it found on first run: @better-auth/sso clean. All 7 ssoProvider fields already provisioned. @better-auth/scim sys_scim_provider was missing `provider_key`. `providerKey` is declared `required: true, unique: true` upstream and written on every provider insert (a derived `<organization>:<provider_id>` key, same shape as `sys_team_member.membership_key`). Without the column, creating a SCIM provider fails the moment SCIM is switched on — #3624 again, behind a flag. The four SCIM **group** models (`scimGroup`, `scimGroupMember`, `scimGroupRole`, `scimGroupRoleGrant`) have no platform object at all. That is a feature-sized gap, not a missing column, so it is NOT silently skipped: `KNOWN_UNMAPPED_MODELS` pins them as an exact set and the set itself is asserted. A new unmapped model fails the build, and a model that gains an object also fails — telling whoever provisioned it to move it into the column check. #3653 now tracks that work. Also raised there, deliberately NOT changed here: `sys_scim_provider` carries a UNIQUE index on `provider_id` alone, while upstream's boundary is `<organization>:<provider_id>` — so the same provider id in two organizations is legal upstream and rejected here. Relaxing a live uniqueness constraint is its own decision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UYLC8TfjzHGwatNxZKdX7H
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 10 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 27, 2026 14:39
21 tasks
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.
关掉 #3647 那个 parity gate 自己留下的洞。
背景
gate 上线时明确排除了
@better-auth/sso和@better-auth/scim,理由是它们不接受schemaoption、getAuthTables()看不见。当时我把这条写成了代码注释 —— 但注释不是 gate,所以按 Prime Directive #10 开了 #3653 记账,现在补上。怎么补的
这两个 plugin 确实暴露了自己的
.schema;而 adapter 对 bridged model 的处理是机械地 camelCase → snake_case(objectql-adapter.ts)。真正决定它们写哪一列的是后面这条机械规则,不是任何手写映射。所以 gate 现在:读前者 → 套后者 → 与平台对象比对。第一次跑出来的结果
@better-auth/ssossoProvider的 7 个字段全部已 provision@better-auth/scimsys_scim_provider缺provider_keyproviderKey在 upstream 声明为required: true, unique: true,每次 provider insert 都写(派生的<organization>:<provider_id>键,和sys_team_member.membership_key同一形状)。缺这个列,SCIM 一打开、创建 provider 就失败 —— 又是 #3624,只是躲在OS_SCIM_ENABLED后面。已补上列 + 镜像 upstream 的 UNIQUE 索引。存量环境走 additive schema sync,老行保持 null(可空 UNIQUE 允许重复 NULL)。
没有假装覆盖的部分
@better-auth/scim的四个 group model(scimGroup/scimGroupMember/scimGroupRole/scimGroupRoleGrant)完全没有平台对象。这是功能级缺口(SCIM group 供给),不是少个列,所以我没有把它悄悄跳过:#3653 现在追踪这块功能工作,正文已更新为完整的列清单。
一处刻意没动的地方
sys_scim_provider上有{ fields: ['provider_id'], unique: true },而 upstream 的唯一性边界是<organization>:<provider_id>—— 同一个 provider_id 在两个组织下,upstream 认为合法,这个索引会拒绝。比库假设的更严。我没有在本 PR 放宽它 —— 动一个已生效的唯一约束是独立决定,也可能当初就是有意为之。已在代码注释和 #3653 里都记了。
测试
@objectstack/plugin-auth@objectstack/platform-objectspnpm check:i18nRefs #3653, #3624, #3647, ADR-0071。
Generated by Claude Code