# Nonce ou mot de passe d’application : tester la parité des deux voies d’authentification

> Deux méthodes d'authentification cohabitent sur l'API REST de WordPress. Un test automatique garantit qu'elles produisent bien le même résultat.

- Auteur : Clément Hadrot
- Publié le : 2024-02-19
- Mis à jour le : 2024-02-19
- Catégorie : Tests
- URL : https://wpmoderne.dev.wordpress-developpement.fr/tests/tester-parite-nonce-application-password/

## L’essentiel

- Deux chemins d'authentification, un seul comportement attendu
- Un test paramétré qui rejoue chaque requête sous les deux formes
- Détecter la régression silencieuse sur l'une des deux voies

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

> L'essentiel à retenir : Deux chemins d'authentification, un seul comportement attendu ; Un test paramétré qui rejoue chaque requête sous les deux formes ; Détecter la régression silencieuse sur l'une des deux voies

```
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 `200` avec 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é.
