fix(auth): déconnexion forcée quand une requête 401 en cours de session (#108) #116
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/client-force-logout-on-401"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Contexte
Closes #108.La session expire déjà (JWT
expiresIn: JWT_EXPIRES_IN, défaut"7d"), mais le client web n'y réagissait pas : seul leGET /auth/medu montage deAuthProviderattrapait un 401 ; un 401 de n'importe quel autre endpoint ne faisait que remonter une erreur locale de la feature, l'UI connectée restant affichée jusqu'au prochain rechargement complet. Sur l'app native (rarement redémarrée, token persisté dans@capacitor/preferences), la session paraissait ne jamais finir.Changements
apps/web/src/api/client.ts: sur un401 NOT_AUTHENTICATEDpour toute requête autre que le probeGET /auth/me,ApiClient._requestpurge le token store et appelle un handleronSessionExpired(enregistré viasetSessionExpiredHandler). L'ApiErrorcontinue d'être levée comme avant — le traitement local des features est inchangé.apps/web/src/features/auth/AuthContext.tsx:AuthProviderenregistre ce handler au montage —setUser(null)+navigate("/login", { replace: true }), gardé par unuserRefpour être un no-op si déjà déconnecté.ApiClientrestant agnostique du framework (pas deuseNavigate), c'est bien lui qui délègue la navigation/état, comme suggéré dans l'issue.Pas de changement API : le back renvoie déjà 401 sur JWT expiré (
require-auth.ts), couvert par sa suite Mocha.Tests
apps/web/cypress/e2e/session-expiry.cy.ts(nouveau) — un 401 en cours de session redirige vers/loginet vide l'état (un retour sur une route gardée re-redirige) ; le 401 du/auth/meinitial d'un visiteur non connecté ne déclenche pas ce chemin (la landing reste affichée).pnpm lint+pnpm --filter web buildOK ;session-expiry.cy.ts2/2,auth.feature6/6, régressionlayout/account/recipes/onboarding/preferencessans casse.🤖 Generated with Claude Code
La session expire déjà (JWT 7 j), mais le client web n'y réagissait pas : seul le `GET /auth/me` du montage de `AuthProvider` attrapait un 401 ; un 401 de n'importe quel autre endpoint ne faisait que remonter une erreur locale, l'UI connectée restant affichée jusqu'au prochain rechargement complet. Sur l'app native (rarement redémarrée, token persisté) la session paraissait ne jamais finir. - `client.ts` : sur un `401 NOT_AUTHENTICATED` pour toute requête autre que le probe `GET /auth/me`, `ApiClient._request` purge le token store et appelle un handler `onSessionExpired` (enregistré via `setSessionExpiredHandler`). L'`ApiError` continue d'être levée comme avant. - `AuthContext.tsx` : `AuthProvider` enregistre ce handler au montage — `setUser(null)` + `navigate(/login, { replace: true })`, gardé par un `userRef` pour être un no-op si déjà déconnecté. `ApiClient` restant agnostique du framework, c'est bien lui qui délègue la navigation/état. Pas de changement API : le back renvoie déjà 401 sur JWT expiré (`require-auth.ts`), couvert par sa suite Mocha. Tests : `apps/web/cypress/e2e/session-expiry.cy.ts` — un 401 en cours de session redirige vers `/login` et vide l'état (un retour sur une route gardée re-redirige) ; le 401 du `/auth/me` initial d'un visiteur non connecté ne déclenche pas ce chemin (la landing reste affichée). Suite e2e existante (auth / layout / account / recipes / onboarding / preferences) revérifiée sans régression. Closes #108 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>