# Authentifier un serveur MCP relié à un CRM associatif : jetons et rotation

> Un serveur MCP qui donne accès aux fiches adhérents d'une association ne peut pas se contenter d'une clé statique. Voici le schéma de jetons et de rotation qui tient la route.

- Auteur : Clément Hadrot
- Publié le : 2025-11-25
- Mis à jour le : 2025-11-25
- Catégorie : IA &amp; MCP
- URL : https://wpmoderne.dev.wordpress-developpement.fr/ia-mcp/authentifier-serveur-mcp-crm-associatif-jetons-rotation/

## L’essentiel

- Un jeton par agent, jamais un jeton partagé
- Rotation automatique toutes les 24 heures
- Périmètre d'accès distinct selon la mission de l'agent

`Authorization: Bearer 8f2c9e...` — c'est cette seule ligne d'en-tête qui protège, ou pas, la base des adhérents d'une association que j'accompagne depuis quelques mois. Leur CRM associatif tourne sur une instance CiviCRM couplée à WordPress, et ils voulaient qu'un agent conversationnel puisse consulter les fiches de contact pour préparer les relances de cotisation. Le serveur MCP qui expose ces données ne pouvait pas se permettre une authentification approximative.

Le problème initial était simple à formuler : comment autoriser un agent IA à lire (et parfois écrire) dans un CRM qui contient des données personnelles, sans donner les clés du camion à un processus qui peut halluciner une requête ou être détourné par une injection de prompt. La réponse tient en deux mécanismes complémentaires : des jetons à portée réduite, et une rotation qui limite la casse en cas de fuite.

## Le problème : une clé unique, un risque unique

Dans la première version du serveur, une seule clé API statique, stockée en variable d'environnement, donnait accès à l'ensemble des points de terminaison MCP. Tout agent qui l'utilisait pouvait lire les adhérents, modifier les dons enregistrés et déclencher l'envoi d'e-mails de relance. Un seul secret compromis — copié dans un journal de débogage, par exemple — ouvrait tout le système.

## Le snippet : jetons courts et scopes explicites

> L'essentiel à retenir : Un jeton par agent, jamais un jeton partagé ; Rotation automatique toutes les 24 heures ; Périmètre d'accès distinct selon la mission de l'agent

La solution retenue s'appuie sur des jetons JWT signés côté serveur, avec une durée de vie de 24 heures et un champ `scope` qui limite précisément ce que l'agent peut faire. Voici la fonction PHP qui génère un jeton pour un agent donné, à intégrer dans le serveur MCP construit avec le plugin MCP Adapter :

```
function wpm_generer_jeton_mcp( int $agent_id, array $scopes ): string {
    $payload = array(
        'sub'   => $agent_id,
        'scope' => implode( ' ', $scopes ), // ex. 'contacts:read dons:read'
        'iat'   => time(),
        'exp'   => time() + ( 24 * HOUR_IN_SECONDS ),
    );

    return JWT::encode( $payload, wpm_cle_secrete_mcp(), 'HS256' );
}

add_filter( 'mcp_adapter_authenticate_request', function ( $request ) {
    $jeton = wpm_extraire_jeton_bearer( $request );
    $decode = wpm_valider_jeton_mcp( $jeton );

    if ( is_wp_error( $decode ) ) {
        return new WP_Error( 'mcp_auth_invalide', 'Jeton invalide ou expiré', array( 'status' => 401 ) );
    }

    return $decode; // contient les scopes autorisés pour cette requête
} );
```

Chaque outil MCP exposé (lister les contacts, mettre à jour une fiche, déclencher une relance) vérifie ensuite que le scope requis est bien présent dans le jeton décodé. Un agent chargé uniquement de « préparer un brouillon de relance » reçoit un jeton avec le scope `contacts:read` seul, jamais `contacts:write`.

## La rotation : limiter la fenêtre d'exposition

La durée de vie de 24 heures n'est pas arbitraire : elle correspond au cycle de traitement des relances côté association, qui tourne une fois par jour via une tâche planifiée. Passé ce délai, le jeton expire de lui-même et l'agent doit en redemander un, ce qui passe par une ré-authentification côté back-office avec un compte de service dédié.

- Rotation automatique toutes les 24 heures, sans intervention manuelle
- Révocation immédiate possible via une liste noire stockée en option WordPress
- Journalisation de chaque émission de jeton avec l'identifiant de l'agent et les scopes accordés

## Les variantes selon le contexte

Pour une association plus petite, avec un seul agent utilisé en interne, une rotation hebdomadaire suffit généralement et allège la charge d'authentification. À l'inverse, pour un CRM qui gère des données sensibles (santé, minorité, situation de vulnérabilité), il est préférable de descendre à une durée de vie de quelques heures et d'ajouter une vérification d'adresse IP source en complément du jeton.

Une variante intéressante consiste à déléguer entièrement la rotation à un fournisseur d'identité externe (type Keycloak ou Auth0) plutôt que de la gérer en PHP maison : le serveur MCP ne fait alors que valider des jetons OAuth émis ailleurs, ce qui simplifie l'audit mais ajoute une dépendance externe qu'il faut surveiller.

> Un jeton MCP ne devrait jamais survivre plus longtemps que le cycle métier qu'il sert. Si la tâche de l'agent dure une heure, le jeton ne doit pas durer une semaine.

## En résumé

L'authentification d'un serveur MCP relié à des données associatives sensibles repose sur deux piliers : des scopes qui limitent chaque jeton à ce dont l'agent a réellement besoin, et une rotation qui réduit la fenêtre de risque en cas de compromission. Ce n'est pas plus compliqué à mettre en place qu'une authentification classique par clé API, mais cela demande de penser le système dès la conception plutôt que de le rajouter après coup.
