[Majeur] Token d'un utilisateur supprimé ou cookie illisible : 500 au lieu de 401 #109

Closed
opened 2026-10-28 10:32:04 +00:00 by asventi · 0 comments
Owner

Gravité : Majeur
Rapports QA d'origine : ADMIN-11, AUTH-12, SECURITY_UX-15

/me lève une NullReferenceException pour un user supprimé ; un cookie AccessToken/RefreshToken corrompu fait planter toutes les requêtes, y compris /auth/login.

Détails des rapports

[ADMIN-11] Après suppression d'un user, ses requêtes (token d'accès encore valide) répondent 500 NullReferenceException au lieu de 401 — Gravité : Majeur
  • Où : GET /api/me (et tout endpoint utilisant tk.User / [Roles]) pour un user supprimé via DELETE /api/users/{id}
  • Étapes : 1. User qa avec HandleUsers supprime son propre compte (DELETE /users/<self> -> 200) 2. Avec le même cookie, GET /api/me
  • Obtenu : 500 System.NullReferenceException avec stack trace. (Le cookie AccessToken reste accepté tant que le JWT n'a pas expiré, mais TokenContext.User est null.)
  • Côté UI (Playwright, scripts/admin/t6_live.py) : un user connecté dans le navigateur puis supprimé par dede n'est PAS éjecté en temps réel : la page reste affichée, la navigation SPA (Calendar) continue de s'afficher, GET /api/me répond 500 (x2 dans les requêtes en échec), et ce n'est qu'au rechargement complet que l'on est renvoyé vers /login. Aucune déconnexion SignalR/aucun message. Screenshots : screens/admin_live_05_deleted_user_page.png, admin_live_06_deleted_after_nav.png, admin_live_07_deleted_after_reload.png.
  • Cause probable : backend/API/Middlewares/TokenContext.cs (user introuvable -> _next sans User) + endpoints [Authorize] qui déréférencent tk.User (ex. Users/Me/Get), Middlewares/Roles.cs:21.
[AUTH-12] Un cookie token illisible ou un token valide d'un user inexistant provoque des 500 au lieu de 401 — Gravité : Mineur
  • Où : GET /auth/refresh avec RefreshToken=abc -> 500 SecurityTokenMalformedException (et a.b.c -> 500 ArgumentException), alors que la doc de l'endpoint promet de purger les cookies sur toute erreur ; GET /me avec AccessToken = le refresh token (JWT valide, même clé/issuer) -> 500 NullReferenceException ; GET /me avec un access token signé valide dont l'id user n'existe pas (cas d'un user supprimé pendant ses 10 min de token) -> 500 NullReferenceException.
  • Attendu : 401 propre (+ nettoyage des cookies).
  • Cause probable : RefreshTokensCommand.cs:20 (ReadFirstClaim sans try/catch), TokenContextMiddleware.cs:31-36 (CanRead faux -> Remove mais on continue avec ReadFirstClaim) et TokenContextMiddleware.cs:40-44 (user absent -> tk.User reste null, [Authorize] laisse passer car JWT valide).
[SECURITY_UX-15] Cookie `AccessToken`/`RefreshToken` illisible -> 500 (y compris sur `/auth/login`) — Gravité : Mineur
  • Où : GET /api/auth/refresh avec RefreshToken=garbage ; GET /api/users, POST /api/auth/login, et tout endpoint sans [Authorize] avec AccessToken=garbage
  • Étapes : 1. curl -i -H 'Cookie: RefreshToken=aaa.bbb.ccc' http://localhost:5173/api/auth/refresh 2. curl -X POST -H 'Content-Type: application/json' -H 'Cookie: AccessToken=garbage' -d '{"login":"x","password":"y"}' http://localhost:5173/api/auth/login
  • Attendu : 401 (et cookies purgés)
  • Obtenu : 500 (ArgumentException IDX12729 avec stack trace). Un navigateur dont le cookie AccessToken est corrompu ne peut plus se connecter tant que le cookie n'est pas supprimé à la main (login -> 500). Sur les endpoints [Authorize], le garbage donne bien 401 (alg:none et JWT forgé -> 401, OK).
  • Cause probable : backend/API/Middlewares/TokenContext.cs:27 (ReadFirstClaim appelé même quand CanRead est faux, il manque un return/await _next), backend/API/Auth/Refresh/RefreshTokensCommand.cs:20 (ReadFirstClaim non protégé)

Trouvé par une campagne de tests QA automatisés (agents Claude, Playwright) sur l'environnement de dev. Les captures citées sont locales et non jointes.

**Gravité :** Majeur **Rapports QA d'origine :** ADMIN-11, AUTH-12, SECURITY_UX-15 `/me` lève une NullReferenceException pour un user supprimé ; un cookie AccessToken/RefreshToken corrompu fait planter toutes les requêtes, y compris `/auth/login`. ## Détails des rapports <details><summary>[ADMIN-11] Après suppression d'un user, ses requêtes (token d'accès encore valide) répondent 500 NullReferenceException au lieu de 401 — Gravité : Majeur</summary> - Où : `GET /api/me` (et tout endpoint utilisant `tk.User` / `[Roles]`) pour un user supprimé via `DELETE /api/users/{id}` - Étapes : 1. User qa avec HandleUsers supprime son propre compte (`DELETE /users/<self>` -> 200) 2. Avec le même cookie, `GET /api/me` - Obtenu : 500 `System.NullReferenceException` avec stack trace. (Le cookie AccessToken reste accepté tant que le JWT n'a pas expiré, mais `TokenContext.User` est null.) - Côté UI (Playwright, `scripts/admin/t6_live.py`) : un user connecté dans le navigateur puis supprimé par dede n'est PAS éjecté en temps réel : la page reste affichée, la navigation SPA (Calendar) continue de s'afficher, `GET /api/me` répond 500 (x2 dans les requêtes en échec), et ce n'est qu'au rechargement complet que l'on est renvoyé vers /login. Aucune déconnexion SignalR/aucun message. Screenshots : `screens/admin_live_05_deleted_user_page.png`, `admin_live_06_deleted_after_nav.png`, `admin_live_07_deleted_after_reload.png`. - Cause probable : `backend/API/Middlewares/TokenContext.cs` (user introuvable -> `_next` sans User) + endpoints `[Authorize]` qui déréférencent `tk.User` (ex. `Users/Me/Get`), `Middlewares/Roles.cs:21`. </details> <details><summary>[AUTH-12] Un cookie token illisible ou un token valide d'un user inexistant provoque des 500 au lieu de 401 — Gravité : Mineur</summary> - Où : `GET /auth/refresh` avec `RefreshToken=abc` -> 500 `SecurityTokenMalformedException` (et `a.b.c` -> 500 `ArgumentException`), alors que la doc de l'endpoint promet de purger les cookies sur toute erreur ; `GET /me` avec `AccessToken` = le refresh token (JWT valide, même clé/issuer) -> 500 NullReferenceException ; `GET /me` avec un access token signé valide dont l'id user n'existe pas (cas d'un user supprimé pendant ses 10 min de token) -> 500 NullReferenceException. - Attendu : 401 propre (+ nettoyage des cookies). - Cause probable : `RefreshTokensCommand.cs:20` (`ReadFirstClaim` sans try/catch), `TokenContextMiddleware.cs:31-36` (`CanRead` faux -> `Remove` mais on continue avec `ReadFirstClaim`) et `TokenContextMiddleware.cs:40-44` (user absent -> `tk.User` reste null, `[Authorize]` laisse passer car JWT valide). </details> <details><summary>[SECURITY_UX-15] Cookie `AccessToken`/`RefreshToken` illisible -> 500 (y compris sur `/auth/login`) — Gravité : Mineur</summary> - Où : `GET /api/auth/refresh` avec `RefreshToken=garbage` ; `GET /api/users`, `POST /api/auth/login`, et tout endpoint sans `[Authorize]` avec `AccessToken=garbage` - Étapes : 1. `curl -i -H 'Cookie: RefreshToken=aaa.bbb.ccc' http://localhost:5173/api/auth/refresh` 2. `curl -X POST -H 'Content-Type: application/json' -H 'Cookie: AccessToken=garbage' -d '{"login":"x","password":"y"}' http://localhost:5173/api/auth/login` - Attendu : 401 (et cookies purgés) - Obtenu : 500 (`ArgumentException IDX12729` avec stack trace). Un navigateur dont le cookie AccessToken est corrompu ne peut plus se connecter tant que le cookie n'est pas supprimé à la main (login -> 500). Sur les endpoints `[Authorize]`, le garbage donne bien 401 (alg:none et JWT forgé -> 401, OK). - Cause probable : `backend/API/Middlewares/TokenContext.cs:27` (`ReadFirstClaim` appelé même quand `CanRead` est faux, il manque un `return`/`await _next`), `backend/API/Auth/Refresh/RefreshTokensCommand.cs:20` (`ReadFirstClaim` non protégé) </details> --- _Trouvé par une campagne de tests QA automatisés (agents Claude, Playwright) sur l'environnement de dev. Les captures citées sont locales et non jointes._
asventi self-assigned this 2026-10-29 16:50:27 +00:00
asventi stopped working 2026-10-29 17:12:01 +00:00
12 minutes 23 seconds
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Total time spent: 12 minutes 23 seconds
asventi
12 minutes 23 seconds
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
Transcendence/backend#109
No description provided.