Repository navigation
Après la suppression du compte, l'app reste connectée et appelle GET /me en boucle (Android) #475
Copy link
Copy link
Closed
Labels
area:mobileapps/mobileapps/mobileprio:P0BloquantBloquantstatus:readySpec complète, prêt à coderSpec complète, prêt à codertype:bugDéfautDéfaut
Milestone
Description
Contexte
Epic : #6
Maquette : E-09 · validée le 2026-09-24 (D-023, D-025, D-028), reprise de #387
Ecrans › E-09 · Compte-et-Monetisation · E-09 · D-025, D-027
Bug trouvé par Paul en recette le 2026-10-15 : build preview EAS 858b93ac (main 1a4845d), Redmi Note 9 (Android), API de staging à jour avec la PR #461.
Après « Supprimer mon compte » (DELETE /v1/me répond 204 à 09:19:01Z) :
- l'app reste connectée côté session : « Créer un compte ou se connecter » et « Programmer » ne font plus rien, alors que PostHog reçoit
auth_promptedà chaque appui (intentionplan, puisprofile) ; - le téléphone envoie
GET /v1/meen boucle, environ 8 requêtes par seconde (499 refus 401 par minute sur l'API de staging, user-agent okhttp), sans s'arrêter ; - aucune erreur JavaScript n'arrive à Sentry (seule une erreur haptique, sans rapport : voir le ticket haptique lié).
Contournement constaté : forcer l'arrêt de l'app et effacer ses données.
Code en cause probable :
apps/mobile/src/profile/me.ts:deleteAccountappelleapi.deleteMe()puissession.signOut();apps/mobile/src/auth/firebase.ts:signOutappelleGoogleSignin.signOut()puissignOut(auth);apps/mobile/src/auth/rights.ts:signOutOnDeletedAccountrappellesignOut()à chaque 401 « Account deleted » ;apps/mobile/src/auth/flow.ts:requireAccount, siisSignedIn(), appellegetFirstName()→GET /me, et en cas d'erreur lanceaction()sans ouvrir la feuille ;useMe:enabled: uid !== null.
Hypothèse : la déconnexion Firebase ne change pas l'état de session (onUserChanged n'est jamais appelé avec null). Le store reste signedIn, chaque écran relance GET /me, le 401 relance signOut(), qui n'aboutit pas, et la boucle tourne.
Comportement attendu
- Après un
DELETE /meréussi, la session passe à l'état anonyme localement, tout de suite, même si la déconnexion Firebase ou Google échoue ; la déconnexion Firebase suit, sans conditionner l'état local. - Un 401 « Account deleted » coupe les requêtes authentifiées : la query
meest désactivée et ne se relance pas, et la déconnexion n'est déclenchée qu'une fois. - Une fois anonyme, « Créer un compte ou se connecter » et « Programmer » rouvrent la feuille de connexion (E-10).
Critères d'acceptation
- Après
DELETE /me, la session passe anonyme même si la déconnexion Firebase échoue (état forcé localement, puis déconnexion Firebase) - Un 401 « Account deleted » désactive la query
me(pas de relance) et ne déclenche la déconnexion qu'une seule fois - Aucune boucle de requêtes mesurable : au plus un
GET /meaprès la suppression (test) - La feuille de connexion se rouvre depuis « Créer un compte ou se connecter » et « Programmer », et une reconnexion Google crée un compte neuf (Aligner la suppression de compte sur D-025 et permettre la réinscription #128)
- Test unitaire qui reproduit la boucle (déconnexion Firebase sans effet sur la session, 401 répétés) et prouve qu'elle ne tourne plus
- Recette sur émulateur Android : suppression, retour à l'état anonyme, aucune boucle dans les journaux réseau, reconnexion ; consignée dans la PR
Notes techniques
- Mesure côté API à envisager : limiter le débit des 401 répétés d'un même jeton. Hors du périmètre de ce ticket ; si CODE la juge utile, il la signale sur l'Inbox 📥 Inbox — Triage #4 pour un ticket
area:apiséparé. - Garder en tête iOS : le correctif porte sur l'état de session, pas sur une branche Android.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:mobileapps/mobileapps/mobileprio:P0BloquantBloquantstatus:readySpec complète, prêt à coderSpec complète, prêt à codertype:bugDéfautDéfaut
Activity
PaulThiberville commentedon Oct 15, 2026
🔨 Démarrage — plan :
GET /v1/mene vient pas de la déconnexion Firebase. Elle tourne déjà en 200 avant la suppression (53 requêtes entre 09:18:04 et 09:18:58Z, onglet Profil ouvert), puis en 401 juste après (09:19:02Z), s'arrête quand l'onglet Profil n'est plus affiché (09:19:14 → 09:19:56Z) et reprend au retour. LeGET /v1/routes/searchen 200 à 09:19:02.547Z, juste après leDELETE, part sans jeton : la session était bien anonyme. En cause :useMe()rend une fonctionrefetchneuve à chaque rendu, etProfileScreenla passe àuseFocusEffect(useCallback(() => refetch(), [refetch])): chaque rendu relance l'effet, donc la requête, donc un rendu. Etquery.refetch()ignoreenabled: uid !== null: anonyme, la requête part sans jeton (401 « Missing bearer token »).refetchstable et sans effet sans compte.session.endSession()force l'état anonyme localement avant la déconnexion Firebase, sans dépendre de son succès (deleteAccount, 401 « Account deleted ») ; une seule déconnexion par compte supprimé ;requireAccounttraite un 401 « Account deleted » comme anonyme et ouvre la feuille ; test qui compte lesGET /mesur une API simulée.PaulThiberville commentedon Oct 15, 2026
🔨 Démarrage — plan :
Reprise d'un travail interrompu par un redémarrage de session (branche
issue/475-deleted-account-signout, trois commits locaux relus et gardés). Trois causes, un commit chacune :GET /me:useMerendait unrefetchneuf à chaque rendu, et l'onglet Profil le passe àuseFocusEffect: chaque rendu relançait l'effet, donc une requête, donc un rendu. Une fois anonyme,refetchignoraitenabledet partait sans jeton, refusé en 401, en boucle. Correctif :refetchstable, et sans effet sans compte. Un test sur une API simulée compte les requêtes ; sur le code actuel, il échoue (« The focus effect of the Profil tab runs in a loop »).session.endSession()passe l'app anonyme tout de suite, sans attendre Firebase, puis demande la déconnexion Firebase, une seule fois. Un échec Firebase part dans Sentry. On l'appelle aprèsDELETE /meet sur un 401 « Account deleted ».requireAccounttraite un compte supprimé comme une absence de compte et ouvre la feuille ; une connexion réussie pose l'utilisateur dans la session sans attendre Firebase (Aligner la suppression de compte sur D-025 et permettre la réinscription #128).auth_promptedsans feuille. Sur le code actuel, le test échoue.Ensuite : lint, typecheck et tests verts commit par commit, puis recette Android et iOS avec un faux Firebase (patch local jamais commité) sur une API locale, base de recette à part. Une PR
Fixes #475, niveau 2.PaulThiberville commentedon Oct 15, 2026
Recette sur appareil, déclarée par Paul en session le 2026-10-15 vers 13:40 (Paris), consignée par MANAGER. Build preview EAS
98b537be(main4b766c5), Redmi Note 9 (Android), API de staging. Paul : « tout est bon ».Status du Project : Terminés.