Skip to content

[finding] SCIM active:false no longer disables the account — the vendor ban coupling was removed upstream in @better-auth/scim 1.7.0 and nothing in this repo replaced it #14360

Description

@claude

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):

  1. 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.";
  2. if (!active) await input.auth.internalAdapter.deleteUserSessions(userId).

plugin-auth passes no identity option. The only two members handed to scim() are connections: [] and authentication.verifyBearerTokenpackages/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.1 dist/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.30 dist/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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions