Skip to content

fix(#403): handle account-not-provisioned auth errors and gate branded header#428

Merged
danielbowne merged 3 commits into
mainfrom
fix/403-unprovisioned-user-msg
Jun 23, 2026
Merged

fix(#403): handle account-not-provisioned auth errors and gate branded header#428
danielbowne merged 3 commits into
mainfrom
fix/403-unprovisioned-user-msg

Conversation

@danielbowne

Copy link
Copy Markdown
Collaborator

Summary

Closes the FE half of #403. Rehomed from #418 by @tarratsco (Onix Tarrats Calderon) onto an internal branch so CI can run with secrets; original authorship is preserved across all three commits. Rebased onto current main (Title.tsx auto-merged cleanly with the #425 datacall-allowlist change; both survived).

Pre-existing bug: an IdP-authenticated identity with no ZTMF account (or a soft-deleted one) landed on LoginPage with "Your session has expired" and an infinite sign-in loop. The IdP session was valid so the ALB never re-prompted; the SPA 401'd on /users/current and rendered the wrong copy.

This adds a NO_ACCOUNT sign-in reason driven by a 403 + ACCOUNT_NOT_PROVISIONED signal from the paired BE PR, renders a terminal "contact your administrator" state with no retry CTA, and preserves the existing EXPIRED handling for genuine 401s. Also gates the branded header to hide whenever LoginPage is the body, not just on the /signin URL.

Backend dependency

Pairs with CMS-Enterprise/ztmf#360. The NO_ACCOUNT terminal state only renders once the BE emits the new code field.

Deploy order: FE first, BE second. Forward-compatible: with old BE deployed, every new path stays dormant and behavior matches today.

Changes

  • utils/authCodes.ts (new) — mirrors BE auth-package constants + FE SignInReasons
  • utils/authInterceptor.ts — branches on response.data.code (NO_ACCOUNT, FORBIDDEN_ORIGIN, controller-403 unchanged)
  • router/authLoader.ts — discriminated return for the two auth-failure shapes
  • views/LoginPage/LoginPage.tsx — terminal NoAccountTerminal, BE message verbatim
  • utils/apiErrors.ts — surface code for callers
  • views/Title/Title.tsx — header gate + shared AuthLoaderData type

Verification

  • typecheck, lint, full suite green on rebased branch (183 passing; new LoginPage + authInterceptor suites)
  • Forward-compat confirmed: old BE → code undefined → 401 stays EXPIRED, 403 falls through to existing controller-403 toast
  • Title.tsx merge verified: fix(#421): render datacall selector only where needed #425 allowlist (datacall sub-bar) and the new header gate both intact

Non-blocking follow-ups (from review, optional)

  1. Interceptor's empty-body NO_ACCOUNT fallback uses the permission copy, while the loader path lets LoginPage own the fallback — divergent copy only if BE sends an empty error body (paired BE always sends one). Could pass reason only and let LoginPage own the fallback.
  2. AuthCodes.UNAUTHORIZED is declared for BE-mirror docs but not branched on (any 401 is treated as EXPIRED). Harmless.

Credit: @tarratsco. Supersedes #418.

Onix Tarrats Calderon added 3 commits June 23, 2026 17:14
…authenticated users now hit a terminal "contact your administrator" state at /signin instead of looping through "Your session has expired".

@a-dipietro a-dipietro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

g2g

@danielbowne danielbowne merged commit eed8f98 into main Jun 23, 2026
3 checks passed
@danielbowne danielbowne deleted the fix/403-unprovisioned-user-msg branch June 23, 2026 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants