Skip to content

fix(frontend): keep the user signed in when signing out fails - #32

Merged
knittingcode merged 2 commits into
mainfrom
fix/fcrm-auto3-logout-failure
Oct 25, 2026
Merged

knittingcode merged 2 commits into
mainfrom
fix/fcrm-auto3-logout-failure

Conversation

@knittingcode

Copy link
Copy Markdown
Owner

Summary

When signing out fails, the SPA now keeps the user signed in, stays on the current page and shows an alert (role="alert"): "Signing out did not complete. You may still be signed in, so try again." Before, every failed sign-out still switched to signed out and went to the login page without a message.

Motivation

ADR-019: logout ends the session on the server. If the logout request does not succeed, the session and its HttpOnly cookie can still be valid. On main, AuthProvider.logout reset the state to anonymous in a finally, and AppShell.signOut navigated to /login in a finally. The user was told nothing; a reload, or the next person at the same browser, was signed in again through GET /api/v1/auth/session.

Cases that reach this on main:

  • No answer: connection lost, timeout.
  • An edge 502/504.
  • A 500 with the database unavailable. Observed on a disposable production-like stack: POST /api/v1/auth/logout → 500 internal-error with the database stopped.
  • A CSRF token refused again after the client re-read the session.

The UI must not claim an outcome it does not have. Found in the read-only frontend audit (AUTO3-028, FE-1).

Implementation

  • frontend/src/auth/AuthProvider.tsx:
    • logout changes the state to anonymous only after a successful answer, or after a 401.
    • Any other failure is rethrown and the state stays signed in.
    • Why a 401 counts as signed out: it means the session had already ended. The backend answers a logout without a valid session with 204 ("logout without a session is a no-op", AuthenticationTest::testLogoutEndsTheSessionOnTheServer; openapi.yaml: "204 also without a session"), so a 401 is not expected from it. The client handles it anyway, as the end of the session.
  • frontend/src/pages/AppShell.tsx: signOut navigates to /login only when logout resolved. Otherwise it re-enables the button and shows the alert. The previously unhandled rejection of void signOut() is gone.
  • frontend/src/index.css: .shell-alert spacing; the alert reuses the existing form-error style.
  • Wording: "may still be signed in", because after a lost answer the server may in fact have ended the session. Signing out again is safe either way: logout without a session is 204.

Testing performed

The dev web image ran with this worktree's sources (read-only mounts), via scratchpad/check-fe.sh with the worktree path.

  • Red first, on main's code with the new tests: src/App.test.tsx gave 16 tests | 4 failed. The failures were when the connection fails, when the API answers 500, when the edge answers 502 and when the CSRF token is refused again after reading the session, plus Vitest caught 5 unhandled errors (the rejected signOut).
    • The 401 test passed on main as well: main treated every failure, including a 401, as signed out. That test guards the new code path.
  • With the fix:
    • npx tsc --build: TSC-OK
    • npx eslint --max-warnings=0 .: ESLINT-OK
    • npx vitest run: Test Files 4 passed (4), Tests 41 passed (41), no unhandled errors
    • npx vite build: built
  • Merged with feat(frontend): member administration page (FND-010C, stacked on #19) #20 (git merge-tree against origin/feat/fcrm-fnd-010c-membership-ui f5f31b8: clean, no conflicts). The merged frontend gives TSC-OK, ESLINT-OK, Test Files 6 passed (6), Tests 93 passed (93).
  • Not run: Playwright. It needs a full stack under the shared heavy lock. The failure path is covered by the App tests with a faked API; the success path stays covered by e2e/auth.spec.ts ("logout ends the session").

Security considerations

  • The user is no longer misled into believing a session has ended when it may not have. That matters on shared computers.
  • No change to what is sent: the CSRF token, the single retry after csrf-invalid and same-origin credentials are unchanged.
  • The CSRF token stays in memory while the session may still be valid. It is cleared only once the session has ended, as before for the success path.

Known limitations

A sign-out whose request failed (no answer, a server or edge error, a CSRF
token refused again after reading the session) still switched the SPA to
signed out and went to the login page without a word, while the session
and its cookie could still be valid on the server: a reload, or the next
person at the same browser, was signed in again.

AuthProvider.logout now changes the state only when the session has ended:
after a successful answer, or a 401 (the session had already ended).
Otherwise it rejects and the user stays signed in; AppShell then stays on
the page and shows an alert that signing out did not complete and the
user may still be signed in.

Tests: App tests for a network failure, a 500, a 502 from the edge and a
CSRF token refused twice (each failed before this change), and a 401 that
signs out.
@knittingcode
knittingcode merged commit e03d151 into main Oct 25, 2026
2 checks passed
@knittingcode
knittingcode deleted the fix/fcrm-auto3-logout-failure branch October 25, 2026 10:27
knittingcode added a commit that referenced this pull request Oct 25, 2026
Only the documented negative-control count conflicted: 53 with #26's three controls.
knittingcode added a commit that referenced this pull request Oct 25, 2026
Only the documented negative-control count and the test table conflicted: 64 with #26's three controls; the unit-test row keeps this branch's membership entries.
knittingcode added a commit that referenced this pull request Oct 25, 2026
knittingcode added a commit that referenced this pull request Oct 25, 2026
… session it may still have

Two browser cases for the #32 behaviour on the members page: a logout that
never reaches the server shows the alert, keeps the page and the session
(GET /me 200), and a second sign-out ends it; a logout processed by the
server whose answer is lost shows the same alert, and the next change leads
to the login with "Your session has ended" and changes nothing.
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.

1 participant