Sur un projet où une extension mobile consommait l’API REST via des mots de passe d’application, pendant que l’interface web du même site utilisait le nonce classique X-WP-Nonce, une mise à jour a un jour cassé silencieusement l’une des deux voies. Un endpoint personnalisé vérifiait la capacité de l’utilisateur via current_user_can(), ce qui fonctionne correctement dans les deux cas, mais une condition ajoutée à la va-vite testait en plus is_user_logged_in() d’une façon qui échouait spécifiquement pour les requêtes authentifiées par mot de passe d’application dans un contexte particulier.
Le bug n’a été détecté que trois semaines plus tard, quand l’application mobile a commencé à recevoir des 401 sur un seul endpoint. Depuis, chaque endpoint personnalisé de nos projets est couvert par un test qui rejoue la même requête sous les deux formes d’authentification et vérifie que le résultat est strictement identique.
Pourquoi les deux voies peuvent diverger silencieusement
Le nonce repose sur une session de navigateur authentifiée par cookie, avec une durée de vie courte et liée à l’utilisateur courant. Le mot de passe d’application, introduit en WordPress 5.6, authentifie une requête indépendamment de tout cookie, via l’en-tête Authorization: Basic. WordPress route ensuite les deux vers le même utilisateur une fois authentifié, mais rien n’empêche du code personnalisé de tester des conditions qui ne sont vraies que dans l’un des deux contextes, par exemple en s’appuyant par erreur sur $_COOKIE ou sur une variable de session.
Écrire un test paramétré avec un fournisseur de données

class Test_Parite_Authentification extends WP_UnitTestCase {
protected $user_id;
public function set_up() {
parent::set_up();
$this->user_id = self::factory()->user->create( [ 'role' => 'editor' ] );
}
public function data_methodes_authentification() {
return [
'nonce' => [ 'nonce' ],
'application_password' => [ 'application_password' ],
];
}
/**
* @dataProvider data_methodes_authentification
*/
public function test_endpoint_reservation_repond_pareil( $methode ) {
$request = new WP_REST_Request( 'GET', '/mon-plugin/v1/reservations' );
if ( 'nonce' === $methode ) {
wp_set_current_user( $this->user_id );
$request->set_header( 'X-WP-Nonce', wp_create_nonce( 'wp_rest' ) );
} else {
wp_set_current_user( 0 );
$mot_de_passe = WP_Application_Passwords::create_new_application_password(
$this->user_id, [ 'name' => 'test-parite' ]
);
$request->set_header(
'Authorization',
'Basic ' . base64_encode( get_userdata( $this->user_id )->user_login . ':' . $mot_de_passe[0] )
);
}
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$this->assertNotEmpty( $response->get_data() );
}
}
Vérifier aussi les cas d’échec, pas seulement le succès
La parité ne concerne pas que le chemin heureux. Il faut aussi vérifier qu’un utilisateur sans la capacité requise reçoit bien un refus dans les deux cas, avec le même code d’erreur, faute de quoi une des deux voies pourrait laisser filtrer une information ou un accès que l’autre bloque correctement.
- Requête sans aucune authentification : les deux voies doivent renvoyer
401. - Requête authentifiée mais sans la capacité requise : les deux voies doivent renvoyer
403. - Requête authentifiée avec la capacité requise : les deux voies doivent renvoyer
200avec des données identiques.
Un point d’attention sur les mots de passe d’application en environnement de test
Par défaut, WordPress exige HTTPS pour autoriser les mots de passe d’application, ce que le filtre wp_is_application_passwords_available permet de contourner explicitement en environnement de test local. Il faut l’activer volontairement dans le bootstrap de la suite, sans quoi tous les tests de cette voie échoueraient pour une raison sans rapport avec le code testé :
add_filter( 'wp_is_application_passwords_available', '__return_true' );
En résumé
Deux portes qui doivent mener à la même pièce méritent un test qui vérifie explicitement qu’elles y mènent bien toutes les deux, à chaque déploiement. Ce test paramétré coûte peu à écrire une fois le fournisseur de données en place, et il transforme une hypothèse implicite — « ça doit marcher pareil » — en garantie vérifiée automatiquement, plutôt que découverte trois semaines plus tard par un utilisateur mobile frustré.