|
| 1 | +--- |
| 2 | +"@objectstack/spec": minor |
| 3 | +"@objectstack/plugin-auth": minor |
| 4 | +--- |
| 5 | + |
| 6 | +feat(spec,plugin-auth): declare `plugins.scim` / `plugins.sso` / `plugins.ssoDomainVerification`, and let an explicit config value win over the env var (#13439) |
| 7 | + |
| 8 | +**Behavior change** (maintainer ruling 2026-08-31 on #13439). |
| 9 | + |
| 10 | +Two halves of one contract gap: |
| 11 | + |
| 12 | +- **Declaration.** `AuthPluginConfigSchema` now declares `scim`, `sso` and |
| 13 | + `ssoDomainVerification` as tri-state `z.boolean().optional()`, following the |
| 14 | + `dynamicClientRegistration` template. Previously the keys were read by |
| 15 | + `plugin-auth` through an `as any` cast while no schema declared them — a key |
| 16 | + an author could write, that typechecked only via the cast, that no |
| 17 | + publish-time validation would ever reject or confirm. |
| 18 | +- **Precedence flip.** For these three keys an EXPLICIT config value now wins |
| 19 | + over the corresponding env var (`OS_SCIM_ENABLED` / `OS_SSO_ENABLED` / |
| 20 | + `OS_SSO_DOMAIN_VERIFICATION`); the env var decides only where the config |
| 21 | + leaves the key UNSET (absent env ⇒ off). Previously the env var always won, |
| 22 | + so `plugins: { scim: false }` had no effect at all whenever |
| 23 | + `OS_SCIM_ENABLED` was set — a line that read as a security control, passed |
| 24 | + review and typecheck, and did nothing. The operator per-environment override |
| 25 | + is preserved for every deployment that leaves the keys unset. The other |
| 26 | + env-paired keys (`oidcProvider`, `dynamicClientRegistration`, `twoFactor`, |
| 27 | + `passwordRejectBreached`) deliberately keep their documented env-wins order. |
| 28 | + |
| 29 | +The ADR-0071 forced-admin coupling is unchanged in shape |
| 30 | +(`admin: pluginConfig.admin ?? scimEffective`): effective SCIM still forces |
| 31 | +the better-auth `admin` plugin on when `admin` is unset — but the flipped |
| 32 | +resolution flows through it, so an explicit `plugins.scim: false` now also |
| 33 | +declines the admin plugin it would have dragged in. The admin coupling itself |
| 34 | +is out of this change's scope (#13816 tracks it). |
| 35 | + |
| 36 | +**Known risk, named:** a deployment that writes BOTH an explicit value and the |
| 37 | +env var and depends on the env winning will flip. The only known explicit |
| 38 | +writer is the cloud control plane, which requires the new order (its |
| 39 | +plan-derived `plugins.scim` must be authoritative; cloud#1265's refuse-to-build |
| 40 | +workaround can retire once this lands). |
| 41 | + |
| 42 | +<!-- adr-0087: not-required (no-migration-prescription) Additive declaration of three previously-undeclared optional keys plus a documented precedence flip: no key is removed, renamed or re-shaped, no tombstone exists, and nothing mechanical for `objectstack migrate meta` to rewrite. The affected quadrant (explicit value AND env var set, relying on env winning) has a measured population of one writer — the cloud control plane — which requires the new order. --> |
0 commit comments