feat(auth): render terminal modal on 410 Gone (ACCOUNT_DELETED) and force logout #411

Merged
YousifShkara merged 1 commit from feat/MAPPS-348-terminal-modal-on-account-deleted into main 2026-07-02 11:14:22 +02:00
Owner

Companion to mokosh-server's MAPPS-348 change (feat/MAPPS-348-410-gone-account-deleted): now that every auth extractor returns 410 Gone with code: ACCOUNT_DELETED once a bunyip account has been deleted (via the PMS-591 webhook that stamps users.deleted_at), the SPA can distinguish that terminal state from a generic 401 (session expired, please refresh) and render the "your account has been deleted" flow instead of degrading to the shared "server unreachable" banner + demo rows that the /tickets screenshot showed on staging.

  • Add a sticky ACCOUNT_DELETED GlobalSignal<bool> in hooks::fetch. Flipped to true (once, one-way) by note_account_deleted() the moment ANY fetch (get_authed, post_authed, etc. and the _typed variants) sees a 410 + error.code == "ACCOUNT_DELETED". Idempotent: only writes to the signal on the transition, so a burst of concurrent 410s does not wake readers repeatedly.
  • Hook both fetch chokepoints (status_error for the String-error helpers and handle_response for the typed ApiError helpers). Every existing call site inherits the detection without needing changes; the terminal state is a global fact, not a per-request one.
  • New AccountDeletedOverlay component. Renders nothing while the signal is false (zero DOM on a healthy session). On the transition it covers the entire viewport with a non-dismissible modal reading "Your account has been deleted. Signing you out in N seconds...", clears the local OIDC token holder on mount (belt + braces so a subsequent request cannot re-arm the tombstoned bearer), counts down 5 seconds, and hard-navigates to bunyip's /v1/auth/logout?url=<msp origin root> - the same URL pattern the existing UserMenu uses for a user-initiated logout so bunyip clears the shared .a8n.systems cookies and 302s back to the SPA origin root signed out. A "Sign out now" button jumps to the same target immediately.
  • Mount the overlay at the AppLayout root, next to ToastRoot, so every route inherits it.

Rollout order: server-first. If this SPA lands before the mokosh-server 410 companion, no regression - the terminal signal never flips because no fetch ever sees an ACCOUNT_DELETED body, so the overlay stays dark and the SPA behaves exactly as it does today.

#MAPPS-348

Companion to mokosh-server's MAPPS-348 change (feat/MAPPS-348-410-gone-account-deleted): now that every auth extractor returns 410 Gone with `code: ACCOUNT_DELETED` once a bunyip account has been deleted (via the PMS-591 webhook that stamps `users.deleted_at`), the SPA can distinguish that terminal state from a generic 401 (session expired, please refresh) and render the "your account has been deleted" flow instead of degrading to the shared "server unreachable" banner + demo rows that the /tickets screenshot showed on staging. - Add a sticky `ACCOUNT_DELETED` `GlobalSignal<bool>` in `hooks::fetch`. Flipped to true (once, one-way) by `note_account_deleted()` the moment ANY fetch (`get_authed`, `post_authed`, etc. and the `_typed` variants) sees a 410 + `error.code == "ACCOUNT_DELETED"`. Idempotent: only writes to the signal on the transition, so a burst of concurrent 410s does not wake readers repeatedly. - Hook both fetch chokepoints (`status_error` for the `String`-error helpers and `handle_response` for the typed `ApiError` helpers). Every existing call site inherits the detection without needing changes; the terminal state is a global fact, not a per-request one. - New `AccountDeletedOverlay` component. Renders nothing while the signal is false (zero DOM on a healthy session). On the transition it covers the entire viewport with a non-dismissible modal reading "Your account has been deleted. Signing you out in N seconds...", clears the local OIDC token holder on mount (belt + braces so a subsequent request cannot re-arm the tombstoned bearer), counts down 5 seconds, and hard-navigates to bunyip's `/v1/auth/logout?url=<msp origin root>` - the same URL pattern the existing `UserMenu` uses for a user-initiated logout so bunyip clears the shared `.a8n.systems` cookies and 302s back to the SPA origin root signed out. A "Sign out now" button jumps to the same target immediately. - Mount the overlay at the `AppLayout` root, next to `ToastRoot`, so every route inherits it. Rollout order: server-first. If this SPA lands before the mokosh-server 410 companion, no regression - the terminal signal never flips because no fetch ever sees an ACCOUNT_DELETED body, so the overlay stays dark and the SPA behaves exactly as it does today. #MAPPS-348
feat(auth): render terminal modal on 410 Gone (ACCOUNT_DELETED) and force logout
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 2m14s
Create release / Create release from merged PR (pull_request) Has been skipped
21287dad69
Companion to mokosh-server's MAPPS-348 change (feat/MAPPS-348-410-gone-account-deleted): now that every auth extractor returns 410 Gone with `code: ACCOUNT_DELETED` once a bunyip account has been deleted (via the PMS-591 webhook that stamps `users.deleted_at`), the SPA can distinguish that terminal state from a generic 401 (session expired, please refresh) and render the "your account has been deleted" flow instead of degrading to the shared "server unreachable" banner + demo rows that the /tickets screenshot showed on staging.

- Add a sticky `ACCOUNT_DELETED` `GlobalSignal<bool>` in `hooks::fetch`. Flipped to true (once, one-way) by `note_account_deleted()` the moment ANY fetch (`get_authed`, `post_authed`, etc. and the `_typed` variants) sees a 410 + `error.code == "ACCOUNT_DELETED"`. Idempotent: only writes to the signal on the transition, so a burst of concurrent 410s does not wake readers repeatedly.
- Hook both fetch chokepoints (`status_error` for the `String`-error helpers and `handle_response` for the typed `ApiError` helpers). Every existing call site inherits the detection without needing changes; the terminal state is a global fact, not a per-request one.
- New `AccountDeletedOverlay` component. Renders nothing while the signal is false (zero DOM on a healthy session). On the transition it covers the entire viewport with a non-dismissible modal reading "Your account has been deleted. Signing you out in N seconds...", clears the local OIDC token holder on mount (belt + braces so a subsequent request cannot re-arm the tombstoned bearer), counts down 5 seconds, and hard-navigates to bunyip's `/v1/auth/logout?url=<msp origin root>` - the same URL pattern the existing `UserMenu` uses for a user-initiated logout so bunyip clears the shared `.a8n.systems` cookies and 302s back to the SPA origin root signed out. A "Sign out now" button jumps to the same target immediately.
- Mount the overlay at the `AppLayout` root, next to `ToastRoot`, so every route inherits it.

Rollout order: server-first. If this SPA lands before the mokosh-server 410 companion, no regression - the terminal signal never flips because no fetch ever sees an ACCOUNT_DELETED body, so the overlay stays dark and the SPA behaves exactly as it does today.

#MAPPS-348
YousifShkara deleted branch feat/MAPPS-348-terminal-modal-on-account-deleted 2026-07-02 11:14:22 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-apps!411
No description provided.