|
| 1 | +--- |
| 2 | +'@objectstack/plugin-auth': patch |
| 3 | +--- |
| 4 | + |
| 5 | +fix(plugin-auth)!: a cloud-managed environment (`OS_CLOUD_MANAGED=1`) refuses local self-registration, invitations, user creation and import, and sets passwords only on local accounts — self-hosted deployments are unchanged |
| 6 | + |
| 7 | +Clause-②: yes (narrowing) — one widening and one narrowing. The widening: the package entry now exports `cloudManagedRefusal`, `CLOUD_MANAGED_REFUSAL_CODE`, `CLOUD_MANAGED_REFUSAL_STATUS`, `CLOUD_MANAGED_REFUSAL_MESSAGES` and `CloudManagedDoor`, so a host that mounts its own twin of one of these doors (the cloud's per-environment `set-initial-password`, objectstack-ai/cloud#3169) refuses with the same status, code and message. The narrowing: under the managed mode only, doors that admitted a request now refuse it: every audience posture but `invite_only`, email sign-up, member invitations, admin create-user and import-users, an admin password set on an identity-provider account, and a first password for a caller without platform-admin standing. A process that does not set `OS_CLOUD_MANAGED=1` answers every one of these doors exactly as before. |
| 8 | +Metadata-protocol: none |
| 9 | + |
| 10 | +<!-- adr-0087: not-required (no-migration-prescription) No metadata moves: no spec key, authorable spelling, object definition, settings key or stored shape is added, removed, renamed or re-shaped, and no exported declaration is removed or narrowed (the only export change is five added names), so there is nothing for `objectstack migrate meta` to rewrite. What changes is the answer plugin-auth's runtime doors give on a process whose environment sets `OS_CLOUD_MANAGED=1`, which the cloud sets on its hosted containers; the remedy is to invite people from the organisation in the cloud console, never a rewrite of a package source or a stored row. `sys_user`, `sys_invitation` and `sys_account` rows are not touched. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); and nothing exported is removed or narrowed (not runtime-interface-only, type-surface-only or runtime-export-retired). --> |
| 11 | + |
| 12 | +**BREAKING** for a cloud-managed environment only (a process started with `OS_CLOUD_MANAGED=1`). A hosted environment's people are invited by the organisation that hosts it, in the cloud, and sign in through the cloud's identity provider; authorization (positions, permission sets) stays in the environment. The local doors that would let someone in another way now refuse, and the password doors split by population rather than switching off, so a local break-glass administrator stays reachable when the identity provider is not. Every refusal is `409` with code `RESOURCE_CONFLICT` (in the ADR-0112 envelope on the raw routes, in better-auth's own `{ code, message }` body on `invite-member`) and a message that says what was not done and where it is done instead. |
| 13 | + |
| 14 | +- **Audience posture.** FROM: any posture booted and patched. TO: under the managed mode every posture but `invite_only` (the default) is refused at construction and on every merged config patch, with a message naming the posture and the fix. |
| 15 | +- **Email sign-up.** FROM: open unless `disableSignUp` or SSO-only was set. TO: forced off under the managed mode, and `getPublicConfig()` advertises `disableSignUp: true`. The bootstrap owner's first sign-up still enters, as it does under SSO-only. |
| 16 | +- **Invitations.** FROM: `organization/invite-member` created an invitation, and with `resend: true` extended a pending one and sent its e-mail again. TO: `409` on both, refused in the route's `before` hook, ahead of better-auth's body validation, session and permission checks and handler, so no invitation row is written or extended and no e-mail leaves. |
| 17 | +- **Admin create-user and import-users.** FROM: created or imported users. TO: `409` after the administrator check (a caller without it still gets `403`), and no user is written. |
| 18 | +- **Admin set-user-password.** FROM: set a password on any user. TO: refuses a target whose `source` is `idp_provisioned` with `409`; a local (`env_native`) target is still answered. |
| 19 | +- **Set-initial-password.** FROM: any signed-in user without a credential could set one. TO: only a caller with platform-admin standing may; any other signed-in caller gets `409`, and an anonymous caller still gets `401`. |
| 20 | +- **New exports.** `cloudManagedRefusal(door)` and the constants behind it, for a host twin of one of these doors. |
| 21 | +- **Unchanged:** every door on a process without `OS_CLOUD_MANAGED=1`, including one running SSO-only; existing users, invitations and credentials. |
| 22 | +- **The fix for an operator:** nothing on a self-hosted deployment. On a hosted environment, invite people from the organisation in the cloud console; their account arrives on their first sign-in. Keep a local password only for the break-glass administrator. |
0 commit comments