All files / src/services/auth logoutReason.ts

88.88% Statements 8/9
100% Branches 2/2
100% Functions 2/2
88.88% Lines 8/9

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43                                    108x         1x 1x               19x 19x 19x 2x   19x          
/**
 * Logout-reason hand-off (Story 12.8 finding F5, 2026-06-04).
 *
 * When the gateway returns 403 ACCOUNT_DEACTIVATED, the apiClient interceptor forces a
 * sign-out and routes to `/login?reason=account_deactivated`. For a FEDERATED session,
 * however, Amplify `signOut()` performs a full-page redirect to the Cognito hosted-UI
 * `/logout` endpoint — clobbering that in-app navigation (and its query param). This tiny
 * sessionStorage hand-off carries the reason across the Cognito logout round-trip
 * (sessionStorage is per-tab and survives cross-origin navigation away and back):
 *
 *   apiClient (403) ── setLogoutReason ──▶ Cognito /logout ──▶ /logout (LogoutPage)
 *       │                                     consumeLogoutReason → /login?reason=…
 *       └─▶ in-app /login (native sessions / no redirect) — LoginForm consumeLogoutReason
 *
 * Both LoginForm and LogoutPage consume (whichever lands first wins); the flag is only
 * ever set by the deactivated-account path, so a consumed-but-unused flag is harmless.
 */
 
const KEY = 'batbern.logout-reason';
 
export type LogoutReason = 'account_deactivated';
 
export function setLogoutReason(reason: LogoutReason): void {
  try {
    sessionStorage.setItem(KEY, reason);
  } catch {
    // Storage unavailable (private mode / quota) — non-fatal, the in-app
    // `/login?reason=…` navigation still covers the non-redirect path.
  }
}
 
export function consumeLogoutReason(): LogoutReason | null {
  try {
    const value = sessionStorage.getItem(KEY);
    if (value !== null) {
      sessionStorage.removeItem(KEY);
    }
    return value as LogoutReason | null;
  } catch {
    return null;
  }
}