Uh oh!
There was an error while loading. Please reload this page.
feat(spec,plugin-auth): declare plugins.scim/sso/ssoDomainVerification, explicit config wins over env - #14066
Conversation
…n, explicit config wins over env Part of #13439 — WIP before artifact regeneration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mciyv38maJ6HYVMiaM26T1
…soDomainVerification; regen spec docs + authorable surface Part of #13439. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mciyv38maJ6HYVMiaM26T1
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mciyv38maJ6HYVMiaM26T1
…th-config-declared-scim-sso
📓 Docs Drift CheckThis PR changes 2 package(s): 4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 132 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1a75e6943b95d2f7edbd190ce76860614b71eb4c && git checkout 1a75e6943b95d2f7edbd190ce76860614b71eb4c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8b04c75d7d4a866789e1dbe13ce5a897d22797e4 67ab1187fef2404506d8c2fb164a03e543be816d && git checkout -B drift-repro 8b04c75d7d4a866789e1dbe13ce5a897d22797e4 && git merge --no-ff 67ab1187fef2404506d8c2fb164a03e543be816d
node scripts/docs-audit/affected-docs.mjs --json 8b04c75d7d4a866789e1dbe13ce5a897d22797e4
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#13439
Clause-②: yes — path limb
packages/spec/src/**(auth-config.zod.ts) + content limb (three new declared keys widen the accept set; documented precedence change). Dispatched at CONTRACT_REVIEW_TIER;needs:contract-reviewhung on this PR and the card at creation.Implements the maintainer ruling of 2026-08-31 (card comment 5479233268), quoted there verbatim; decision A of the card, admin amplification split out.
What changed
1. Declaration (packages/spec/src/system/auth-config.zod.ts).
AuthPluginConfigSchemanow declaresscim,ssoandssoDomainVerificationas tri-statez.boolean().optional(), following thedynamicClientRegistrationtemplate, with docstrings spelling out the unset semantics. Previously the keys were read through anas anycast while no schema declared them — a key an author could write, that typechecked only because of the cast, that no publish-time check would ever reject or confirm.2. Precedence flip (packages/plugins/plugin-auth/src/auth-manager.ts). For these three keys an EXPLICIT config value now wins over the env var; the env var decides only where the config leaves the key unset (absent env means off). The flipped read points, re-derived on current main rather than taken from the card (line numbers at merge time):
The
as anycasts on these three keys are gone — the declaration is what makes them typecheck. The stale docstring onisSsoWired(which documented the old env-wins order) is updated, and a comment block above the chains documents the new precedence and why it deliberately differs from the env-wins order the OIDC / DCR / 2FA / HIBP keys keep.Named consequence — the ADR-0071 admin coupling. The coupling SHAPE is untouched:
admin: pluginConfig.admin ?? scimEffectivestill forces the better-auth admin plugin on when SCIM is effective andadminis unset (ADR-0071's recorded behavior: SCIM active-false to ban runs through the admin plugin). What flows through it is the flipped scim resolution — in both the plugin list and the/auth/configfeatures block's inline recomputation — so an explicitplugins.scim: falsenow also declines the admin plugin that SCIM would have dragged in. I measured no conflict between the ruled flip and ADR-0071: the ADR pins the coupling (effective SCIM forces admin), not the resolution order of scim itself, and the coupling holds identically after the flip. The coupling itself is out of scope here per the ruling — #13816 tracks it and remains open; nothing in this PR pins its behavior.Known risk, named (per the ruling): a deployment that writes BOTH an explicit value and the env var and depends on the env winning will flip. The only known explicit writer is the cloud control plane, which requires the new order — its plan-derived
plugins.scimbecomes authoritative, cloud#1265's refuse-to-build workaround can retire, and cloud#1451 unblocks.Scope discipline: behavior changes only where a host explicitly sets one of the three keys. Deployments that leave them unset see zero change — the operator per-environment env override is preserved for that case, pinned by tests. The dogfood suites that set OS_SCIM_ENABLED with no explicit config key sit entirely in that unchanged quadrant (their own comments note bootStack exposes no auth-plugin override).
Tests
check:generated— all 15 artifacts up to date (docs references + authorable-surface regenerated for the three keys).check:type-check-debtre-measure ("28 ledger entries re-measured, none above its recorded number" — plugin-auth's TEST_DEBT entry did not move; a scoped probe reproduced its 94-total / 43-in-auth-manager.test.ts split exactly),check:dual-build-cjs-loadsand speccheck:skill-examplesafter a full workspace build,check:adr-0087-registration("no declared-breaking changeset"),check:nul-bytes. CI-context gates (shard-attestation, test-completeness, docs drift comment) are CI-owned runs.Changeset: minor for @objectstack/spec and @objectstack/plugin-auth, behavior change and risk stated, ADR-0087 disposition marker included (not-required, no migration prescription — nothing removed or renamed).
Generated by Claude Code
Generated by Claude Code