fix(auth) don't treat a rejected login as an expired session #38

Merged
araemer merged 1 commit from fix-login-401-session-expired into stage 2026-09-26 06:24:25 +00:00
Owner

Problem

A failed password login showed "Deine Sitzung ist abgelaufen. Bitte melde dich erneut an." above the correct "Login fehlgeschlagen!" message, and fired a pointless POST /auth/refresh.

apiClient treated every 401 as an expired session: refresh → clear session → SESSION_EXPIRED_EVENT → redirect to /login?reason=session_expired. For /auth/login a 401 just means wrong credentials.

Fix

  • apiClient.post takes an optional ApiRequestConfig. With isCredentialExchange: true, a 401 skips the refresh/expiry flow and is thrown as a plain ApiError, which the login page already handles.
  • Set on POST /auth/login and POST /passkey/authenticate/complete. All other requests behave as before.

Verification

  • npm run build, npm run lint (2 existing warnings in an untouched file), npm run test (187 passed)
  • In the browser (Playwright MCP): a wrong password gives a 401 and shows only "Login fehlgeschlagen…", with no refresh call and the URL staying /login. A correct login still lands on /recipe/list.

🤖 Generated with Claude Code

## Problem A failed password login showed **"Deine Sitzung ist abgelaufen. Bitte melde dich erneut an."** above the correct "Login fehlgeschlagen!" message, and fired a pointless `POST /auth/refresh`. `apiClient` treated every 401 as an expired session: refresh → clear session → `SESSION_EXPIRED_EVENT` → redirect to `/login?reason=session_expired`. For `/auth/login` a 401 just means wrong credentials. ## Fix - `apiClient.post` takes an optional `ApiRequestConfig`. With `isCredentialExchange: true`, a 401 skips the refresh/expiry flow and is thrown as a plain `ApiError`, which the login page already handles. - Set on `POST /auth/login` and `POST /passkey/authenticate/complete`. All other requests behave as before. ## Verification - `npm run build`, `npm run lint` (2 existing warnings in an untouched file), `npm run test` (187 passed) - In the browser (Playwright MCP): a wrong password gives a 401 and shows only "Login fehlgeschlagen…", with no refresh call and the URL staying `/login`. A correct login still lands on `/recipe/list`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(auth) don't treat a rejected login as an expired session
All checks were successful
/ test (pull_request) Successful in 46s
aff49ac5b3
A 401 from POST /auth/login (or passkey authenticate/complete) means wrong
credentials, but apiClient handled it like any other 401: it tried a token
refresh, cleared the session and fired SESSION_EXPIRED_EVENT, so the login
page showed "Deine Sitzung ist abgelaufen" above "Login fehlgeschlagen".

apiClient.post now accepts an optional ApiRequestConfig; with
isCredentialExchange set, a 401 skips the refresh/expiry flow and is thrown
as a plain ApiError for the caller to handle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
araemer merged commit a27a8ec17c into stage 2026-09-26 06:24:25 +00: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
araemer/recipe-app!38
No description provided.