[Auth] Déconnexion forcée quand la session expire (réaction client sur 401) #108

Closed
opened 2026-09-08 00:48:59 +02:00 by kyuno · 0 comments
Owner

Résumé

Une session qui a expiré n'a aucun effet visible tant qu'on ne recharge pas complètement l'app : le client ne réagit à aucun 401 en cours d'usage. Sur l'app native (rarement redémarrée à froid, token persisté), la session paraît ne jamais finir.

Contexte (trouvé en auditant le code, pas supposé)

  • Le JWT expire déjà : apps/api/src/lib/jwt.ts signe avec { expiresIn: env.JWT_EXPIRES_IN } ; apps/api/src/config/env.ts → JWT_EXPIRES_IN défaut "7d" ; jwt.verify (même fichier) rejette un token expiré ; require-auth.ts mappe l'échec en 401 NOT_AUTHENTICATED. Cookie maxAge = 7 j (auth.routes.ts). (À vérifier : la valeur JWT_EXPIRES_IN réellement déployée.)
  • Pas de refresh token, pas de session store. Le levier tokenVersion existe dans le payload et est re-vérifié à chaque requête (require-auth.ts) mais rien ne l'incrémente jamais (aucun endpoint « logout everywhere » / « change password »).
  • Côté web : apps/web/src/api/client.ts ApiClient._request() — sur !response.ok, throw new ApiError(status, …), aucun traitement spécifique du 401 : pas d'intercepteur, pas d'auto-logout, pas de purge du token. Token écrit seulement dans login()/signup(), purgé seulement dans logout()/deleteAccount(). Natif : capacitor-token-store.ts (@capacitor/preferences, clé auth_token, persiste au redémarrage).
  • À l'expiration : seul le apiClient.me() au montage de AuthProvider (features/auth/AuthContext.tsx) l'attrape → setUser(null) → RequireAuth redirige. Un 401 de n'importe quel autre endroit ne fait que remonter une erreur locale de la feature ; l'UI connectée reste affichée.

Contenu attendu

Le client doit réagir à une session invalide/expirée : un handler 401 central dans ApiClient._request() qui, sur 401 NOT_AUTHENTICATED, purge le token store, réinitialise l'état d'auth et redirige vers /login. Conserver la durée de vie 7 j.

ApiClient étant agnostique du framework (pas de useNavigate), motif : exposer un callback onUnauthorized que AuthProvider enregistre au montage.

Optionnel (hors périmètre strict) : brancher enfin tokenVersion pour un vrai « déconnecter partout ».

Couverture : Cypress avec un cy.intercept renvoyant 401 en cours de session → assert redirection /login + état auth vidé.

Dépendances

Aucune.

## Résumé Une session qui a expiré n'a aucun effet visible tant qu'on ne recharge pas complètement l'app : le client ne réagit à aucun `401` en cours d'usage. Sur l'app native (rarement redémarrée à froid, token persisté), la session paraît ne jamais finir. ## Contexte (trouvé en auditant le code, pas supposé) - Le JWT **expire déjà** : `apps/api/src/lib/jwt.ts` signe avec `{ expiresIn: env.JWT_EXPIRES_IN }` ; `apps/api/src/config/env.ts` → `JWT_EXPIRES_IN` défaut `"7d"` ; `jwt.verify` (même fichier) rejette un token expiré ; `require-auth.ts` mappe l'échec en `401 NOT_AUTHENTICATED`. Cookie `maxAge` = 7 j (`auth.routes.ts`). *(À vérifier : la valeur `JWT_EXPIRES_IN` réellement déployée.)* - Pas de refresh token, pas de session store. Le levier `tokenVersion` existe dans le payload et est re-vérifié à chaque requête (`require-auth.ts`) mais **rien ne l'incrémente jamais** (aucun endpoint « logout everywhere » / « change password »). - Côté web : `apps/web/src/api/client.ts` `ApiClient._request()` — sur `!response.ok`, `throw new ApiError(status, …)`, **aucun traitement spécifique du 401** : pas d'intercepteur, pas d'auto-logout, pas de purge du token. Token écrit seulement dans `login()`/`signup()`, purgé seulement dans `logout()`/`deleteAccount()`. Natif : `capacitor-token-store.ts` (`@capacitor/preferences`, clé `auth_token`, persiste au redémarrage). - À l'expiration : seul le `apiClient.me()` au montage de `AuthProvider` (`features/auth/AuthContext.tsx`) l'attrape → `setUser(null)` → `RequireAuth` redirige. Un `401` de n'importe quel autre endroit ne fait que remonter une erreur locale de la feature ; l'UI connectée reste affichée. ## Contenu attendu Le client doit réagir à une session invalide/expirée : **un handler `401` central** dans `ApiClient._request()` qui, sur `401 NOT_AUTHENTICATED`, purge le token store, réinitialise l'état d'auth et redirige vers `/login`. Conserver la durée de vie 7 j. `ApiClient` étant agnostique du framework (pas de `useNavigate`), motif : exposer un callback `onUnauthorized` que `AuthProvider` enregistre au montage. Optionnel (hors périmètre strict) : brancher enfin `tokenVersion` pour un vrai « déconnecter partout ». Couverture : Cypress avec un `cy.intercept` renvoyant `401` en cours de session → assert redirection `/login` + état auth vidé. ## Dépendances Aucune.
kyuno added the
Medium
bug
planned
labels 2026-09-08 00:48:59 +02:00
kyuno closed this issue 2026-09-08 04:14:51 +02:00
Sign in to join this conversation.
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: kyuno/batchCooking#108
No description provided.