Skip to content

fix: race condition OAuth2 et rejet 400 caractères spéciaux [RUN-3788] - #16

Open
rockynox wants to merge 1 commit into
mainfrom
fix/RUN-3788-token-race-condition-and-special-chars
Open

fix: race condition OAuth2 et rejet 400 caractères spéciaux [RUN-3788]#16
rockynox wants to merge 1 commit into
mainfrom
fix/RUN-3788-token-race-condition-and-special-chars

Conversation

@rockynox

Copy link
Copy Markdown
Member

Contexte

Deux bugs de production identifiés sur les commandes #1456, #1353 et #1449 (ticket RUN-3788).

Bug 1 — Boucle 401 / race condition token OAuth2

process_payment() appelait helloasso_refresh_token_asso() systématiquement à chaque paiement, même si le token courant était encore valide (30 min de durée de vie). Sous charge concurrente, deux requêtes lisaient le même refresh token en base, déclenchaient deux rotations en cascade, et le token utilisé par la première requête se retrouvait révoqué → 401 en boucle.

Fix : le refresh n'est déclenché que si le token est absent ou si helloasso_token_expires_in_asso indique qu'il est expiré. Les paiements simultanés partagent le même access token valide (l'API autorise jusqu'à 20 en simultané).

Bug 2 — Rejet 400 / noms avec points ou caractères spéciaux

La regex de validate_fields() était cassée :

// Avant — '!' est un caractère littéral, pas une négation
preg_match('/![a-zA-ZéèêëáàâäúùûüçÇ\'-]/', $firstName)

// Après — '[^...]' est la vraie négation de classe en PCRE
preg_match('/[^a-zA-ZéèêëáàâäúùûüçÇ\' -]/', $firstName)

Les points (.), @, _ passaient la validation locale et étaient rejetés par l'API avec ArgumentInvalid.

Corrections annexes

  • Les erreurs wp_error, les réponses non-200 et les réponses sans redirectUrl retournent maintenant un result: failure propre à WooCommerce, au lieu de continuer l'exécution et crasher sur ->redirectUrl.

Fichiers modifiés

  • inc/Gateway/WC_HelloAsso_Gateway.php

… spéciaux

- process_payment() ne refresh le token que si absent ou expiré, évitant
  la rotation concurrente du refresh token sous charge (boucle 401)
- les erreurs API (wp_error, non-200, réponse sans redirectUrl) retournent
  désormais un échec WooCommerce propre au lieu de continuer l'exécution
- corrige la regex validate_fields() : '![...]' → '[^...]' pour bloquer
  correctement les points, @, _ et autres caractères rejetés par l'API
@lpmcsn

lpmcsn commented Aug 19, 2026

Copy link
Copy Markdown

La PR couvre plusieurs problèmes que je viens de rencontrer en production (plugin 1.1.2, WooCommerce 11.0.1, WP 7.0.4, PHP 8.3.6). Deux retours après l'avoir testée.

La correction de la regex introduit un refus de prénoms français courants

Le passage de ! à [^...] est ok ici. La regex d'origine ne matchait effectivement que la chaîne littérale « ! suivi d'une lettre ». Mais une fois la négation active, le jeu de caractères autorisé devient déterminant, et la liste actuelle est incomplète : il manque î, ï, ô, œ ainsi que *outes les majuscules accentuées. L'absence du flag /u aggrave le problème, la classe étant alors évaluée octet par octet sur de l'UTF-8.

Constaté sur une instance réelle. J'ai déployé votre branche seule (inc/Gateway/WC_HelloAsso_Gateway.php uniquement, afbac9d) sur ma production, et passé une commande avec le prénom Benoît. Elle est refusée au checkout :

Le prénom ne doit pas contenir de caractères spéciaux ni de caractères n'appartenant pas à l'alphabet latin

Les autres cas ci-dessous sont mesurés en exécutant directement les deux motifs :

Saisie Attendu Obtenu
Émile passe bloqué
Benoît passe bloqué
Loïc passe bloqué
Anaïs passe bloqué
Jérôme passe bloqué
Éric passe bloqué
Agnès passe passe
Chloé passe passe
Jean-Pierre passe passe
N'Guyen passe passe
Ivan#Petrov bloqué bloqué
Лукаш bloqué bloqué

Le filtrage des caractères indésirables fonctionne bien. Par ex, Ivan#Petrov et les alphabets non latins sont correctement rejetés. Mais en l'état, la PR refuse au checkout un donateur prénommé Benoît, Loïc ou Jérôme ce qui... peut peut être poser problème sur une plateforme française 😁

Une classe plus large qui a réglé le cas :

preg_match('/[^a-zA-ZÀ-ÿ\' -]/u', $firstName)

Vérifiée sur le même jeu : les prénoms français passent, Ivan#Petrov et les alphabets non latins restent bloqués.

Nous l'avons également déployée sur la même instance, à la place de la vôtre : Benoît aboutit alors normalement à la page de paiement HelloAsso (checkout-intents en 200), tandis que Ivan# reste refusé au checkout.

La liste de noms interdits est sensible à la casse

Indépendamment de cette PR, validate_fields() ne rattrape pas les variantes majuscules :

in_array('TEST', array(..., 'test'))  // false
in_array('Test', array(..., 'test'))  // false

C'est ce qui m'a amenés ici à la base : une commande de test avec le prénom TEST a franchi la validation et l'API a répondu 400 {"errors":[{"code":"ArgumentInvalid","message":"Le champ prénom est invalide"}]}. Retour de process_payment(), capturé sur le filtre woocommerce_payment_successful_result :

{"result":"success","redirect":null,"order_id":6057}

Côté client, le script de checkout échoue sur cette redirection nulle et affiche i18n_checkout_error, qui invite le donateur à « vérifier la présence d'une éventuelle transaction sur son moyen de paiement » — alors qu'aucun paiement n'a jamais été initié.

Votre PR supprime ce faux message mais la validation locale continue de laisser passer le cas qu'elle est précisément censée intercepter.

$forbiddenNames = array('firstname', 'lastname', /* ... */, 'test');

if (in_array(mb_strtolower(trim($firstName)), $forbiddenNames, true)) {

Remonter le message de l'API

Dernier point, mineur : dans le bloc !isset($response_data->redirectUrl), le corps de la réponse contient déjà un message exploitable par l'utilisateur. Le remonter évite de laisser le donateur devant un « Réponse inattendue » sans piste de correction.

$message = 'Réponse inattendue de HelloAsso. Veuillez réessayer.';
if (isset($response_data->errors[0]->message)) {
    $message = $response_data->errors[0]->message;
}
wc_add_notice($message, 'error');
return array('result' => 'failure', 'messages' => $message);

Ces trois correctifs sont prêts sur une branche basée sur la vôtre : https://github.com/lpmcsn/woocommerce-plugin/tree/fix/complement-pr16

Je peux les proposer en PR séparée une fois celle-ci mergée, ou les pousser ici si vous préférez tout regrouper — dites-nous ce qui vous arrange.

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