Skip to content

fix(auth): dédupliquer les sousmissions de connexion/inscription concurrentes (PG::UniqueViolation, #769) - #274

Merged
hboisgibault merged 2 commits into
masterfrom
fix/auth-login-dedup
Aug 27, 2026
Merged

fix(auth): dédupliquer les sousmissions de connexion/inscription concurrentes (PG::UniqueViolation, #769)#274
hboisgibault merged 2 commits into
masterfrom
fix/auth-login-dedup

Conversation

@hboisgibault

Copy link
Copy Markdown
Collaborator

Problème

Des PG::UniqueViolation sur index_users_on_uid_and_application_id (HTTP 500) surviennent à la création/connexion d'utilisateurs (ticket #769). La cause est côté interface : le POST /oauth/token qui échange les identifiants et crée l'utilisateur côté serveur n'a aucune garde anti-renvoi simultané.

Toutes les sources de doublon (double-clic sur Sign up, double onCode OAuth via react-oauth-popup, double exécution de l'initializer SSO au montage + sur l'écouteur logora:authentication:refresh, réémission du formulaire) convergent vers useAuthActions.loginUser → authTokenHandler.fetchToken, qui poste plusieurs requêtes concurrentes avec le même (application_id, uid) pour un même compte (uid stable du provider pour OAuth/social ; crypto.randomUUID() par soumission pour le formulaire e-mail).

Côté backend, User.get_user utilise find_or_create_by (non atomique) : deux insertions concurrentes échouent sur la contrainte unique → HTTP 500.

Correctif

refreshToken possède déjà une garde in-flight (refreshInFlight) et un test de déduplication ; fetchToken n'en avait aucune. On applique la même garde in-flight à fetchToken dans src/components/auth/use_auth/authTokenHandler.js :

  • tant qu'un /auth/token est en vol, les appels concurrents renvoient la même promise → un seul POST part du navigateur ;
  • verrou libéré en .finally() → un retry manuel après erreur fonctionne toujours.

Tests

Tests ajoutés dans authTokenHandler.test.jsx :

  • déduplication des appels concurrents (1 seul appel HTTP) ;
  • nouvel appel autorisé après complétion de la requête ;
  • verrou libéré après échec.

98 tests auth passent (npx vitest run src/components/auth/).

Note

Le correctif front supprime les doublons issus d'un double-clic / double callback. Pour rendre la création totalement idempotente en cas de course résiduelle (deux onglets, retry réseau), on peut ensuite rendre User.get_user race-safe côté LogoraAPI (retry sur RecordNotUnique ou insert-on-conflict SQL) — correctif complémentaire proposé.


Cette PR a été créée par un agent IA (OpenHands) pour le compte de @hboisgibault.

…urrentes

Deux appels /oauth/token concurrents (double-clic sur Sign up, double
callback OAuth, double déclenchement de l'initializer SSO, ...) portent le
même (application_id, uid) pour un même compte. Le backend exécute alors
User.get_user (*find_or_create_by* non atomique) deux fois, et l'une des
requêtes échoue sur la contrainte index_users_on_uid_and_application_id
(PG::UniqueViolation -> HTTP 500, #769).

fetchToken n'avait aucune garde anti-déclenchement, contrairement à
refreshToken qui patiente déjà via refreshInFlight. On applique la même
garde in-flight à fetchToken : pendant qu'un /oauth/token est en vol, les
nouveaux appels renvoient la même requête, un seul POST part pour le
serveur.

Co-authored-by: openhands <openhands@all-hands.dev>
- Suppression du commentaire introduit dans authTokenHandler.js.
- Suppression des commentaires ajoutés dans les tests de fetchToken.
- Ajout d'un AGENTS.md de base mentionnant qu'aucun commentaire ne doit être ajouté.

Co-authored-by: openhands <openhands@all-hands.dev>
@hboisgibault
hboisgibault merged commit d036e50 into master Aug 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants