# Un photographe : une galerie de livraison protégée par un lien expirant

> Un jeton à durée de vie limitée, stocké en métadonnée, remplace un mot de passe partagé par e-mail pour livrer des photos de séance à un client.

- Auteur : WordPress Développement
- Publié le : 2024-06-09
- Mis à jour le : 2024-06-09
- Catégorie : Extensions
- URL : https://www.wpmoderne.fr/extensions/photographe-galerie-livraison-lien-expirant/

## L’essentiel

- Un jeton aléatoire remplace un mot de passe transmis en clair
- La date d'expiration se vérifie à chaque accès, pas seulement à la création
- Le lien reste valable sans nécessiter de compte utilisateur côté client

32 caractères générés aléatoirement, stockés dans une métadonnée d'article aux côtés d'une date d'expiration : c'est tout ce qu'il faut pour remplacer le mot de passe partagé par e-mail qu'un photographe indépendant envoyait jusque-là à chaque client pour accéder à la galerie de livraison de sa séance photo, une pratique fragile dès que l'e-mail était transféré ou conservé indéfiniment dans une boîte de réception non sécurisée.

Ce tutoriel construit, étape par étape, un système de lien expirant appliqué à une galerie hébergée comme type de contenu personnalisé, sans compte utilisateur à créer côté client et sans dépendance à une extension tierce de gestion de téléchargements.

## Étape 1 : générer le jeton à la création de la galerie

À la publication d'une galerie, un jeton aléatoire et une date d'expiration sont générés et stockés comme métadonnées de l'article, via la fonction native `wp_generate_password()` détournée de son usage habituel pour produire une chaîne aléatoire sans caractères spéciaux ambigus dans une URL.

```
add_action( 'save_post_galerie_photo', function ( int $post_id, WP_Post $post ) {
    if ( wp_is_post_revision( $post_id ) ) {
        return;
    }

    if ( ! get_post_meta( $post_id, 'jeton_acces', true ) ) {
        $jeton = wp_generate_password( 32, false, false );
        update_post_meta( $post_id, 'jeton_acces', $jeton );

        $expiration = strtotime( '+30 days' );
        update_post_meta( $post_id, 'jeton_expiration', $expiration );
    }
}, 10, 2 );
```

Le second et le troisième argument de `wp_generate_password()`, mis à `false`, retirent respectivement les caractères spéciaux et les caractères supplémentaires ambigus, pour produire un jeton directement utilisable dans une URL sans encodage particulier.

## Étape 2 : construire le lien de livraison

> L'essentiel à retenir : Un jeton aléatoire remplace un mot de passe transmis en clair ; La date d'expiration se vérifie à chaque accès, pas seulement à la création ; Le lien reste valable sans nécessiter de compte utilisateur côté client

Le lien envoyé au client combine l'URL de la galerie et le jeton en paramètre de requête, jamais en partie du chemin d'URL lui-même, ce qui simplifie sa vérification sans dépendre de règles de réécriture personnalisées :

```
function galerie_lien_livraison( int $post_id ): string {
    $jeton = get_post_meta( $post_id, 'jeton_acces', true );
    return add_query_arg( 'acces', $jeton, get_permalink( $post_id ) );
}
```

## Étape 3 : vérifier le jeton et son expiration à chaque accès

La vérification se fait sur le hook `template_redirect`, avant que le contenu de la galerie ne soit affiché, avec un contrôle systématique de la date d'expiration en plus de la simple correspondance du jeton :

```
add_action( 'template_redirect', function () {
    if ( ! is_singular( 'galerie_photo' ) ) {
        return;
    }

    $post_id = get_queried_object_id();
    $jeton_attendu = get_post_meta( $post_id, 'jeton_acces', true );
    $expiration    = (int) get_post_meta( $post_id, 'jeton_expiration', true );
    $jeton_fourni  = isset( $_GET['acces'] ) ? sanitize_text_field( wp_unslash( $_GET['acces'] ) ) : '';

    if ( ! hash_equals( $jeton_attendu, $jeton_fourni ) ) {
        wp_die( 'Lien invalide.', 403 );
    }

    if ( time() > $expiration ) {
        wp_die( 'Ce lien de livraison a expiré. Contactez le photographe pour un nouveau lien.', 410 );
    }
} );
```

L'usage de `hash_equals()` plutôt qu'une simple comparaison avec `===` évite une attaque par mesure de temps sur la comparaison du jeton, une précaution peu coûteuse à ajouter même si le risque réel reste faible sur ce type de contenu.

## Étape 4 : renouveler un lien expiré

Un écran d'administration simple, accessible depuis la liste des galeries, permet au photographe de régénérer un nouveau jeton et une nouvelle date d'expiration en un clic, sans avoir à modifier manuellement les métadonnées :

- Un lien d'action dans la colonne personnalisée de la liste des galeries, avec un nonce de sécurité classique.
- Régénération du jeton via la même logique que l'étape 1, appelée manuellement plutôt qu'à la publication.
- Envoi automatique d'un nouvel e-mail au client avec le nouveau lien, via `wp_mail()`.

> Le choix qui a le plus rassuré le photographe sur ce projet n'était pas la sécurité du jeton lui-même, mais la possibilité de révoquer un accès en un clic simplement en régénérant un nouveau jeton, ce qui invalide immédiatement tous les liens précédemment envoyés.

## En résumé

Un jeton aléatoire à durée de vie limitée, vérifié à chaque accès, offre une protection largement suffisante pour une galerie de livraison client, sans la lourdeur d'un système de comptes utilisateurs. Ce tutoriel couvre l'accès à la galerie ; le stockage des fichiers eux-mêmes, sur un espace de stockage local ou distant, reste un sujet distinct non traité ici.
