Filed by the #14150 measurement seat (read-only card, session session_01AUF1NoViznQK32gqpK8wS8, scratchpad key issue-14150-knife1). Out of scope for that card — recording only, unassigned, for the finding box to grade. Measured on origin/main at e854a531a.
What was measured
@better-auth/scim@1.7.2 — the version this repo pins (packages/plugins/plugin-auth/package.json:29, ^1.7.2) and resolves (pnpm-lock.yaml:9632; installed node_modules/@better-auth/scim/package.json reports 1.7.2) — contains no ban code path at all. The substring ban occurs zero times anywhere in the published package:
grep -ro "ban" node_modules/@better-auth/scim/dist | wc -l → 0
What active: false does instead, dist/index.mjs:4196-4231 (reconcileUser):
- calls the OPTIONAL host callback
options.identity.reconcileUser(state, ...) — typed at dist/index.d.mts:267 as "Reconciles global enabled or disabled state inside the SCIM transaction."; if (!active) await input.auth.internalAdapter.deleteUserSessions(userId).
plugin-auth passes noidentity option. The only two members handed to scim() are connections: [] and authentication.verifyBearerToken — packages/plugins/plugin-auth/src/auth-manager.ts:3256-3270. And reconcileUser appears nowhere in this repo's sources:
git grep -n "reconcileUser" -- 'packages/**/src/**' → 0 hits
⇒ On main today, an IdP deactivating a user over SCIM revokes that user's sessions and changes nothing else. sys_user.banned is never written, so the account is not disabled, and the vendor's BANNED_USER sign-in refusal (better-auth/dist/plugins/admin/admin.mjs:33-49) never applies to it.
When it changed — measured on published tarballs, not recalled
@better-auth/scim | occurrences of ban in dist/ | deactivation path |
|---|
1.6.30 | 15 | resolveSCIMActiveDeactivation writes banned: true |
1.7.0-rc.1 (this repo's pin until 2026-08-27) | 17 | same, plus a refusal when the admin plugin is absent |
1.7.0 | 0 | host callback + deleteUserSessions |
1.7.1 | 0 | same |
1.7.2 (installed) | 0 | same |
1.7.0-rc.1dist/index.mjs:1985-1986, verbatim:
if (!ctx.context.hasPlugin("admin")) {
if (deactivating) throw new SCIMAPIError("BAD_REQUEST", {
detail: "Setting `active: false` requires the admin plugin, which provides the enforced disabled-user state"
});
and 1.6.30dist/index.mjs:751-755, verbatim:
"Applies SCIM active semantics to a pending user update. active maps to the admin plugin's banned field, the only enforced disabled-user state in Better Auth, so honoring deactivation requires the admin plugin. ... A deactivation without the admin plugin is rejected, never silently dropped."
So the coupling this repo records was true when it was written, and was removed upstream in 1.7.0 (published 2026-08-18, per the npm registry time map). This repo inherited the removal on 2026-08-27 with 366f89576 (the stable-1.7.1 migration, epic #11632) and has carried the old belief since.
What that leaves stale in the tree
Why this is filed and not fixed
#14150 is a read-only measurement card. The remedy is a design choice with at least two shapes — implement the vendor's identity.reconcileUser host hook using ObjectStack's own ban write (admin-ban-endpoints.ts already owns that write plus the last-admin guard), or accept session-revocation-only and restate the coupling everywhere it is asserted — and the choice interacts with #13816's landed refusal and with #14150's fork. Unassigned and ungraded; triage decides.
Boundary. Measured statically from published vendor sources and this repo's tree. NOT measured: a live IdP-driven active: false against a running SCIM-enabled deployment, and whether SSO-authenticated SCIM users are protected in practice by the IdP refusing them afterwards — a mitigation this repo does not itself enforce.
Refs: #14150 (the measurement that surfaced it) · #13816 · #11632 · #5892 · #13980 · ADR-0049.
Generated by Claude Code
Filed by the #14150 measurement seat (read-only card, session
session_01AUF1NoViznQK32gqpK8wS8, scratchpad keyissue-14150-knife1). Out of scope for that card — recording only, unassigned, for the finding box to grade. Measured onorigin/mainate854a531a.What was measured
@better-auth/scim@1.7.2— the version this repo pins (packages/plugins/plugin-auth/package.json:29,^1.7.2) and resolves (pnpm-lock.yaml:9632; installednode_modules/@better-auth/scim/package.jsonreports1.7.2) — contains no ban code path at all. The substringbanoccurs zero times anywhere in the published package:What
active: falsedoes instead,dist/index.mjs:4196-4231(reconcileUser):options.identity.reconcileUser(state, ...)— typed atdist/index.d.mts:267as "Reconciles global enabled or disabled state inside the SCIM transaction.";if (!active) await input.auth.internalAdapter.deleteUserSessions(userId).plugin-authpasses noidentityoption. The only two members handed toscim()areconnections: []andauthentication.verifyBearerToken—packages/plugins/plugin-auth/src/auth-manager.ts:3256-3270. AndreconcileUserappears nowhere in this repo's sources:⇒ On
maintoday, an IdP deactivating a user over SCIM revokes that user's sessions and changes nothing else.sys_user.bannedis never written, so the account is not disabled, and the vendor'sBANNED_USERsign-in refusal (better-auth/dist/plugins/admin/admin.mjs:33-49) never applies to it.When it changed — measured on published tarballs, not recalled
@better-auth/scimbanindist/1.6.30resolveSCIMActiveDeactivationwritesbanned: true1.7.0-rc.1(this repo's pin until 2026-08-27)1.7.0deleteUserSessions1.7.11.7.2(installed)1.7.0-rc.1dist/index.mjs:1985-1986, verbatim:and
1.6.30dist/index.mjs:751-755, verbatim:So the coupling this repo records was true when it was written, and was removed upstream in
1.7.0(published 2026-08-18, per the npm registrytimemap). This repo inherited the removal on 2026-08-27 with366f89576(the stable-1.7.1 migration, epic #11632) and has carried the old belief since.What that leaves stale in the tree
packages/plugins/plugin-auth/src/auth-manager.ts:3234— "active:false→ ban + session revoke (needs the admin plugin, forced on above)". The same block still says "on the installed 1.7.1" at:3236-3237while the installed version is1.7.2(that half belongs to [finding] 130 anchored better-auth 1.7.1 measurements are unverified against the installed 1.7.2 — pay down per surface, never as a batch #13980's population, not to this card).packages/plugins/plugin-auth/src/auth-manager.ts:2489,:2513,:5149— effective SCIM still forces theadminplugin on, on exactly this justification.packages/plugins/plugin-auth/src/auth-manager.ts:353-367— the [finding] SCIM forces the better-authadminplugin on (pluginConfig.admin ?? scimEffective) and a deployment has no declared way to decline it — split from #13439 by the 2026-08-31 ruling #13816 construction-time refusal tells an operator that "SCIM's active:false deprovisioning path runs through the better-auth admin plugin (ban/unban — ADR-0071)". At1.7.2that sentence is not true of the vendor.packages/plugins/plugin-auth/src/last-admin-guard.test.ts:552— the break-glass suite'sdeprovision()helper simulates the vendor write (update: { banned: true }) instead of driving@better-auth/scim. So the ADR-0024 D5.2 guard from plugin-auth: break-glass 守卫 —— SCIM/ban 不得停用最后一个管理员(ADR-0024 D5.2,cloud#621 转入) #5892, whose job is to stop SCIM deactivating the last administrator, keeps a green suite while its SCIM trigger is unreachable: nothing on the SCIM path writesbannedany more.content/docs/references/system/auth-config.mdx:105and:215(generated) repeat the coupling to authors.Why this is filed and not fixed
#14150 is a read-only measurement card. The remedy is a design choice with at least two shapes — implement the vendor's
identity.reconcileUserhost hook using ObjectStack's own ban write (admin-ban-endpoints.tsalready owns that write plus the last-admin guard), or accept session-revocation-only and restate the coupling everywhere it is asserted — and the choice interacts with #13816's landed refusal and with #14150's fork. Unassigned and ungraded; triage decides.Boundary. Measured statically from published vendor sources and this repo's tree. NOT measured: a live IdP-driven
active: falseagainst a running SCIM-enabled deployment, and whether SSO-authenticated SCIM users are protected in practice by the IdP refusing them afterwards — a mitigation this repo does not itself enforce.Refs: #14150 (the measurement that surfaced it) · #13816 · #11632 · #5892 · #13980 · ADR-0049.
Generated by Claude Code