Nicolas Dabene
Retour au blog
11 December 2025 Nicolas Dabene 5 min

Comment sécuriser un serveur MCP?

Sécuriser votre Serveur MCP : Permissions, Validation et Protection

API IA développement sécurité Tutoriel Intelligence Artificielle automatisation
Comment sécuriser un serveur MCP?

Sécuriser votre Serveur MCP : Permissions, Validation et Protection

Sécuriser votre Serveur MCP : Permissions, Validation et Protection

Votre serveur MCP expose maintenant plusieurs outils que les IA peuvent découvrir et utiliser. Génial ! Mais une question cruciale se pose : qui peut utiliser quoi ? Dans cet article, nous allons transformer votre serveur en une forteresse sécurisée, sans sacrifier sa simplicité d’utilisation. Parce qu’un serveur puissant doit aussi être un serveur protégé.

Introduction

En 15 ans de développement d’API, j’ai appris une règle d’or : la sécurité ne se rajoute pas après coup, elle se conçoit dès le départ. Un serveur MCP qui donne accès à vos fichiers, vos données, vos ressources sensibles nécessite plusieurs couches de protection. Mais rassurez-vous : sécuriser ne veut pas dire complexifier.

Aujourd’hui, nous allons implémenter quatre piliers de sécurité essentiels : la validation des entrées (pour éviter les données malveillantes), l’authentification (qui êtes-vous ?), l’autorisation (qu’avez-vous le droit de faire ?) et la limitation des ressources (pour éviter les abus). À la fin de cet article, votre serveur sera production-ready.

Les Quatre Piliers de la Sécurité MCP

Avant de coder, comprenons notre stratégie de défense en profondeur :

1. Validation des Entrées

Le principe : Ne jamais faire confiance aux données entrantes. Toujours valider, nettoyer, vérifier.

Pourquoi ? Un paramètre mal validé peut permettre un accès à des fichiers sensibles (../../etc/passwd), une injection de code, ou un crash du serveur.

2. Authentification

Le principe : Identifier qui utilise votre serveur. Chaque requête doit être associée à une identité vérifiée.

Pourquoi ? Sans authentification, n’importe qui peut utiliser vos outils. C’est comme laisser votre maison sans serrure.

3. Autorisation

Le principe : Vérifier les permissions. Même authentifié, tout le monde ne peut pas tout faire.

Pourquoi ? Votre stagiaire n’a pas besoin d’accéder aux fichiers RH. Les permissions granulaires protègent vos données sensibles.

4. Limitation des Ressources

Le principe : Imposer des quotas, des limites de taille, des timeouts.

Pourquoi ? Éviter qu’un utilisateur malveillant (ou une erreur) ne sature votre serveur avec 10 000 requêtes par seconde.

Validation Robuste des Entrées

Commençons par le plus important : valider toutes les entrées. Créez src/security/validator.ts :

<span class="c1">// src/security/validator.ts</span>
<span class="k">import</span> <span class="nx">path</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">path</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">InputSchema</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../mcp/protocol</span><span class="dl">'</span><span class="p">;</span>

<span class="cm">/**
 * Erreur de validation
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">ValidationError</span> <span class="kd">extends</span> <span class="nb">Error</span> <span class="p">{</span>
  <span class="kd">constructor</span><span class="p">(</span>
    <span class="nx">message</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
    <span class="k">public</span> <span class="nx">field</span><span class="p">?:</span> <span class="kr">string</span><span class="p">,</span>
    <span class="k">public</span> <span class="nx">expected</span><span class="p">?:</span> <span class="kr">string</span>
  <span class="p">)</span> <span class="p">{</span>
    <span class="k">super</span><span class="p">(</span><span class="nx">message</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">ValidationError</span><span class="dl">'</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="cm">/**
 * Validateur de paramètres basé sur JSON Schema
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">ParameterValidator</span> <span class="p">{</span>
  
  <span class="cm">/**
   * Valider les paramètres selon un schéma
   */</span>
  <span class="k">static</span> <span class="nx">validate</span><span class="p">(</span><span class="nx">params</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">schema</span><span class="p">:</span> <span class="nx">InputSchema</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="c1">// Vérifier que params est un objet</span>
    <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">params</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span> <span class="o">||</span> <span class="nx">params</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span><span class="dl">'</span><span class="s1">Les paramètres doivent être un objet</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// Vérifier les champs requis</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">requiredField</span> <span class="k">of</span> <span class="nx">schema</span><span class="p">.</span><span class="nx">required</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="nx">requiredField</span> <span class="k">in</span> <span class="nx">params</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
          <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">requiredField</span><span class="p">}</span><span class="s2">' est requis`</span><span class="p">,</span>
          <span class="nx">requiredField</span>
        <span class="p">);</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// Valider chaque propriété</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="p">[</span><span class="nx">fieldName</span><span class="p">,</span> <span class="nx">fieldValue</span><span class="p">]</span> <span class="k">of</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">entries</span><span class="p">(</span><span class="nx">params</span><span class="p">))</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">fieldSchema</span> <span class="o">=</span> <span class="nx">schema</span><span class="p">.</span><span class="nx">properties</span><span class="p">[</span><span class="nx">fieldName</span><span class="p">];</span>
      
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">fieldSchema</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
          <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2">' n'est pas autorisé`</span><span class="p">,</span>
          <span class="nx">fieldName</span>
        <span class="p">);</span>
      <span class="p">}</span>

      <span class="k">this</span><span class="p">.</span><span class="nx">validateField</span><span class="p">(</span><span class="nx">fieldName</span><span class="p">,</span> <span class="nx">fieldValue</span><span class="p">,</span> <span class="nx">fieldSchema</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Valider un champ spécifique
   */</span>
  <span class="k">private</span> <span class="k">static</span> <span class="nx">validateField</span><span class="p">(</span>
    <span class="nx">fieldName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
    <span class="nx">value</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span>
    <span class="nx">schema</span><span class="p">:</span> <span class="kr">any</span>
  <span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="c1">// Validation de type</span>
    <span class="kd">const</span> <span class="nx">actualType</span> <span class="o">=</span> <span class="k">typeof</span> <span class="nx">value</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">expectedType</span> <span class="o">=</span> <span class="nx">schema</span><span class="p">.</span><span class="kd">type</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="nx">expectedType</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span> <span class="o">&&</span> <span class="nx">actualType</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
        <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2">' doit être une chaîne de caractères`</span><span class="p">,</span>
        <span class="nx">fieldName</span><span class="p">,</span>
        <span class="nx">expectedType</span>
      <span class="p">);</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="nx">expectedType</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span> <span class="o">&&</span> <span class="nx">actualType</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
        <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2">' doit être un nombre`</span><span class="p">,</span>
        <span class="nx">fieldName</span><span class="p">,</span>
        <span class="nx">expectedType</span>
      <span class="p">);</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="nx">expectedType</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">boolean</span><span class="dl">'</span> <span class="o">&&</span> <span class="nx">actualType</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">boolean</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
        <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2">' doit être un booléen`</span><span class="p">,</span>
        <span class="nx">fieldName</span><span class="p">,</span>
        <span class="nx">expectedType</span>
      <span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// Validation d'énumération</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">schema</span><span class="p">.</span><span class="kr">enum</span> <span class="o">&&</span> <span class="o">!</span><span class="nx">schema</span><span class="p">.</span><span class="kr">enum</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="nx">value</span><span class="p">))</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
        <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2">' doit être l'une des valeurs : </span><span class="p">${</span><span class="nx">schema</span><span class="p">.</span><span class="kr">enum</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span><span class="p">)}</span><span class="s2">`</span><span class="p">,</span>
        <span class="nx">fieldName</span>
      <span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// Validation de longueur pour les strings</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">expectedType</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">schema</span><span class="p">.</span><span class="nx">minLength</span> <span class="o">&&</span> <span class="nx">value</span><span class="p">.</span><span class="nx">length</span> <span class="o"><</span> <span class="nx">schema</span><span class="p">.</span><span class="nx">minLength</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
          <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2">' doit contenir au moins </span><span class="p">${</span><span class="nx">schema</span><span class="p">.</span><span class="nx">minLength</span><span class="p">}</span><span class="s2"> caractères`</span><span class="p">,</span>
          <span class="nx">fieldName</span>
        <span class="p">);</span>
      <span class="p">}</span>

      <span class="k">if</span> <span class="p">(</span><span class="nx">schema</span><span class="p">.</span><span class="nx">maxLength</span> <span class="o">&&</span> <span class="nx">value</span><span class="p">.</span><span class="nx">length</span> <span class="o">></span> <span class="nx">schema</span><span class="p">.</span><span class="nx">maxLength</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
          <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2">' ne peut pas dépasser </span><span class="p">${</span><span class="nx">schema</span><span class="p">.</span><span class="nx">maxLength</span><span class="p">}</span><span class="s2"> caractères`</span><span class="p">,</span>
          <span class="nx">fieldName</span>
        <span class="p">);</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// Validation de plage pour les nombres</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">expectedType</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">schema</span><span class="p">.</span><span class="nx">minimum</span> <span class="o">!==</span> <span class="kc">undefined</span> <span class="o">&&</span> <span class="nx">value</span> <span class="o"><</span> <span class="nx">schema</span><span class="p">.</span><span class="nx">minimum</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
          <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2">' doit être supérieur ou égal à </span><span class="p">${</span><span class="nx">schema</span><span class="p">.</span><span class="nx">minimum</span><span class="p">}</span><span class="s2">`</span><span class="p">,</span>
          <span class="nx">fieldName</span>
        <span class="p">);</span>
      <span class="p">}</span>

      <span class="k">if</span> <span class="p">(</span><span class="nx">schema</span><span class="p">.</span><span class="nx">maximum</span> <span class="o">!==</span> <span class="kc">undefined</span> <span class="o">&&</span> <span class="nx">value</span> <span class="o">></span> <span class="nx">schema</span><span class="p">.</span><span class="nx">maximum</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
          <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2">' ne peut pas dépasser </span><span class="p">${</span><span class="nx">schema</span><span class="p">.</span><span class="nx">maximum</span><span class="p">}</span><span class="s2">`</span><span class="p">,</span>
          <span class="nx">fieldName</span>
        <span class="p">);</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// Validation de pattern pour les strings</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">expectedType</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span> <span class="o">&&</span> <span class="nx">schema</span><span class="p">.</span><span class="nx">pattern</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">regex</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="nx">schema</span><span class="p">.</span><span class="nx">pattern</span><span class="p">);</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">regex</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">value</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
          <span class="s2">`Le champ '</span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2">' ne correspond pas au format attendu`</span><span class="p">,</span>
          <span class="nx">fieldName</span>
        <span class="p">);</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="cm">/**
 * Validateur de chemins de fichiers
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">PathValidator</span> <span class="p">{</span>
  <span class="k">private</span> <span class="nx">allowedDirectories</span><span class="p">:</span> <span class="kr">string</span><span class="p">[];</span>
  <span class="k">private</span> <span class="nx">blockedPaths</span><span class="p">:</span> <span class="kr">string</span><span class="p">[];</span>

  <span class="kd">constructor</span><span class="p">(</span><span class="nx">allowedDirectories</span><span class="p">:</span> <span class="kr">string</span><span class="p">[],</span> <span class="nx">blockedPaths</span><span class="p">:</span> <span class="kr">string</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[])</span> <span class="p">{</span>
    <span class="c1">// Résoudre tous les chemins en absolus</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">allowedDirectories</span> <span class="o">=</span> <span class="nx">allowedDirectories</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">dir</span> <span class="o">=></span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">dir</span><span class="p">));</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">blockedPaths</span> <span class="o">=</span> <span class="nx">blockedPaths</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">p</span> <span class="o">=></span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">p</span><span class="p">));</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Valider qu'un chemin est sûr
   */</span>
  <span class="nx">validatePath</span><span class="p">(</span><span class="nx">filePath</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="kr">string</span> <span class="p">{</span>
    <span class="c1">// Résoudre le chemin absolu</span>
    <span class="kd">const</span> <span class="nx">absolutePath</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">filePath</span><span class="p">);</span>

    <span class="c1">// Vérifier les path traversal (../)</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">absolutePath</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="dl">'</span><span class="s1">..</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
        <span class="dl">'</span><span class="s1">Les chemins avec ".." ne sont pas autorisés (path traversal)</span><span class="dl">'</span>
      <span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// Vérifier que le chemin est dans un répertoire autorisé</span>
    <span class="kd">const</span> <span class="nx">isInAllowedDir</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">allowedDirectories</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="nx">dir</span> <span class="o">=></span>
      <span class="nx">absolutePath</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="nx">dir</span><span class="p">)</span>
    <span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isInAllowedDir</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
        <span class="s2">`Accès refusé : le chemin doit être dans l'un des répertoires autorisés`</span>
      <span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// Vérifier que le chemin n'est pas bloqué</span>
    <span class="kd">const</span> <span class="nx">isBlocked</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">blockedPaths</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="nx">blocked</span> <span class="o">=></span>
      <span class="nx">absolutePath</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="nx">blocked</span><span class="p">)</span>
    <span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="nx">isBlocked</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
        <span class="s2">`Accès refusé : ce chemin est explicitement bloqué`</span>
      <span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="nx">absolutePath</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Ajouter un répertoire autorisé
   */</span>
  <span class="nx">addAllowedDirectory</span><span class="p">(</span><span class="nx">directory</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">allowedDirectories</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">directory</span><span class="p">));</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Bloquer un chemin spécifique
   */</span>
  <span class="nx">blockPath</span><span class="p">(</span><span class="nx">pathToBlock</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">blockedPaths</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">pathToBlock</span><span class="p">));</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="cm">/**
 * Validateur de taille de fichier
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">SizeValidator</span> <span class="p">{</span>
  <span class="cm">/**
   * Valider qu'une taille est acceptable
   */</span>
  <span class="k">static</span> <span class="nx">validateSize</span><span class="p">(</span>
    <span class="nx">size</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span>
    <span class="nx">maxSize</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span>
    <span class="nx">fieldName</span><span class="p">:</span> <span class="kr">string</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">fichier</span><span class="dl">'</span>
  <span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">size</span> <span class="o">></span> <span class="nx">maxSize</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ValidationError</span><span class="p">(</span>
        <span class="s2">`Le </span><span class="p">${</span><span class="nx">fieldName</span><span class="p">}</span><span class="s2"> est trop volumineux (max </span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">formatSize</span><span class="p">(</span><span class="nx">maxSize</span><span class="p">)}</span><span class="s2">)`</span>
      <span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Formater une taille en octets en format lisible
   */</span>
  <span class="k">static</span> <span class="nx">formatSize</span><span class="p">(</span><span class="nx">bytes</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">units</span> <span class="o">=</span> <span class="p">[</span><span class="dl">'</span><span class="s1">octets</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Ko</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Mo</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Go</span><span class="dl">'</span><span class="p">];</span>
    <span class="kd">let</span> <span class="nx">size</span> <span class="o">=</span> <span class="nx">bytes</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">unitIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">while</span> <span class="p">(</span><span class="nx">size</span> <span class="o">>=</span> <span class="mi">1024</span> <span class="o">&&</span> <span class="nx">unitIndex</span> <span class="o"><</span> <span class="nx">units</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">size</span> <span class="o">/=</span> <span class="mi">1024</span><span class="p">;</span>
      <span class="nx">unitIndex</span><span class="o">++</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="s2">`</span><span class="p">${</span><span class="nx">size</span><span class="p">.</span><span class="nx">toFixed</span><span class="p">(</span><span class="mi">2</span><span class="p">)}</span><span class="s2"> </span><span class="p">${</span><span class="nx">units</span><span class="p">[</span><span class="nx">unitIndex</span><span class="p">]}</span><span class="s2">`</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

Ce validateur complet vérifie :

Types de données : string, number, boolean

Valeurs requises : les champs obligatoires sont présents

Énumérations : les valeurs sont dans la liste autorisée

Longueurs : min/max pour les chaînes

Plages : min/max pour les nombres

Patterns : expressions régulières pour les formats

Chemins : protection contre path traversal et accès non autorisés

Tailles : limites de fichiers

Système d’Authentification JWT

Créons maintenant un système d’authentification basé sur JSON Web Tokens. Créez src/security/auth.ts :

<span class="c1">// src/security/auth.ts</span>
<span class="k">import</span> <span class="nx">crypto</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">crypto</span><span class="dl">'</span><span class="p">;</span>

<span class="cm">/**
 * Interface utilisateur
 */</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">User</span> <span class="p">{</span>
  <span class="nl">id</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">username</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">role</span><span class="p">:</span> <span class="dl">'</span><span class="s1">admin</span><span class="dl">'</span> <span class="o">|</span> <span class="dl">'</span><span class="s1">user</span><span class="dl">'</span> <span class="o">|</span> <span class="dl">'</span><span class="s1">readonly</span><span class="dl">'</span><span class="p">;</span>
  <span class="nl">permissions</span><span class="p">:</span> <span class="kr">string</span><span class="p">[];</span>
<span class="p">}</span>

<span class="cm">/**
 * Token JWT simplifié (pour la démo - utilisez une vraie lib JWT en prod)
 */</span>
<span class="kr">interface</span> <span class="nx">Token</span> <span class="p">{</span>
  <span class="nl">userId</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">username</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">role</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">permissions</span><span class="p">:</span> <span class="kr">string</span><span class="p">[];</span>
  <span class="nl">expiresAt</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
<span class="p">}</span>

<span class="cm">/**
 * Gestionnaire d'authentification
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">AuthManager</span> <span class="p">{</span>
  <span class="k">private</span> <span class="nx">users</span><span class="p">:</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="nx">User</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Map</span><span class="p">();</span>
  <span class="k">private</span> <span class="nx">tokens</span><span class="p">:</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="nx">Token</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Map</span><span class="p">();</span>
  <span class="k">private</span> <span class="k">readonly</span> <span class="nx">SECRET_KEY</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="k">private</span> <span class="k">readonly</span> <span class="nx">TOKEN_DURATION</span> <span class="o">=</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">;</span> <span class="c1">// 24 heures</span>

  <span class="kd">constructor</span><span class="p">(</span><span class="nx">secretKey</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">SECRET_KEY</span> <span class="o">=</span> <span class="nx">secretKey</span><span class="p">;</span>
    
    <span class="c1">// Créer quelques utilisateurs de test</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">createUser</span><span class="p">({</span>
      <span class="na">id</span><span class="p">:</span> <span class="dl">'</span><span class="s1">1</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">username</span><span class="p">:</span> <span class="dl">'</span><span class="s1">admin</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">role</span><span class="p">:</span> <span class="dl">'</span><span class="s1">admin</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">permissions</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">*</span><span class="dl">'</span><span class="p">]</span> <span class="c1">// Toutes les permissions</span>
    <span class="p">});</span>

    <span class="k">this</span><span class="p">.</span><span class="nx">createUser</span><span class="p">({</span>
      <span class="na">id</span><span class="p">:</span> <span class="dl">'</span><span class="s1">2</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">username</span><span class="p">:</span> <span class="dl">'</span><span class="s1">user</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">role</span><span class="p">:</span> <span class="dl">'</span><span class="s1">user</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">permissions</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">readFile</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">listFiles</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">searchFiles</span><span class="dl">'</span><span class="p">]</span>
    <span class="p">});</span>

    <span class="k">this</span><span class="p">.</span><span class="nx">createUser</span><span class="p">({</span>
      <span class="na">id</span><span class="p">:</span> <span class="dl">'</span><span class="s1">3</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">username</span><span class="p">:</span> <span class="dl">'</span><span class="s1">readonly</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">role</span><span class="p">:</span> <span class="dl">'</span><span class="s1">readonly</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">permissions</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">readFile</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">listFiles</span><span class="dl">'</span><span class="p">]</span>
    <span class="p">});</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Créer un utilisateur
   */</span>
  <span class="nx">createUser</span><span class="p">(</span><span class="nx">user</span><span class="p">:</span> <span class="nx">User</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">users</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">username</span><span class="p">,</span> <span class="nx">user</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Authentifier un utilisateur et générer un token
   */</span>
  <span class="nx">authenticate</span><span class="p">(</span><span class="nx">username</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">password</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="kr">string</span> <span class="o">|</span> <span class="kc">null</span> <span class="p">{</span>
    <span class="c1">// En production, vérifier le mot de passe hashé !</span>
    <span class="c1">// Ici c'est simplifié pour la démo</span>
    <span class="kd">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">users</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">username</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">user</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Générer un token</span>
    <span class="kd">const</span> <span class="nx">tokenId</span> <span class="o">=</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">randomBytes</span><span class="p">(</span><span class="mi">32</span><span class="p">).</span><span class="nx">toString</span><span class="p">(</span><span class="dl">'</span><span class="s1">hex</span><span class="dl">'</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">token</span><span class="p">:</span> <span class="nx">Token</span> <span class="o">=</span> <span class="p">{</span>
      <span class="na">userId</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span>
      <span class="na">username</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">username</span><span class="p">,</span>
      <span class="na">role</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">role</span><span class="p">,</span>
      <span class="na">permissions</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">permissions</span><span class="p">,</span>
      <span class="na">expiresAt</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">TOKEN_DURATION</span>
    <span class="p">};</span>

    <span class="k">this</span><span class="p">.</span><span class="nx">tokens</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">tokenId</span><span class="p">,</span> <span class="nx">token</span><span class="p">);</span>
    <span class="k">return</span> <span class="nx">tokenId</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Valider un token
   */</span>
  <span class="nx">validateToken</span><span class="p">(</span><span class="nx">tokenId</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">Token</span> <span class="o">|</span> <span class="kc">null</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">tokens</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">tokenId</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">token</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Vérifier l'expiration</span>
    <span class="k">if</span> <span class="p">(</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">></span> <span class="nx">token</span><span class="p">.</span><span class="nx">expiresAt</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">tokens</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">tokenId</span><span class="p">);</span>
      <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="nx">token</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Révoquer un token
   */</span>
  <span class="nx">revokeToken</span><span class="p">(</span><span class="nx">tokenId</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">tokens</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">tokenId</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Obtenir un utilisateur
   */</span>
  <span class="nx">getUser</span><span class="p">(</span><span class="nx">username</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">User</span> <span class="o">|</span> <span class="kc">undefined</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">users</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">username</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Nettoyer les tokens expirés
   */</span>
  <span class="nx">cleanExpiredTokens</span><span class="p">():</span> <span class="k">void</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">now</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="p">[</span><span class="nx">tokenId</span><span class="p">,</span> <span class="nx">token</span><span class="p">]</span> <span class="k">of</span> <span class="k">this</span><span class="p">.</span><span class="nx">tokens</span><span class="p">.</span><span class="nx">entries</span><span class="p">())</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">now</span> <span class="o">></span> <span class="nx">token</span><span class="p">.</span><span class="nx">expiresAt</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">tokens</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">tokenId</span><span class="p">);</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="cm">/**
 * Middleware d'authentification pour Express
 */</span>
<span class="k">export</span> <span class="kd">function</span> <span class="nx">authMiddleware</span><span class="p">(</span><span class="nx">authManager</span><span class="p">:</span> <span class="nx">AuthManager</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">next</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
    <span class="c1">// Récupérer le token de l'en-tête Authorization</span>
    <span class="kd">const</span> <span class="nx">authHeader</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">headers</span><span class="p">.</span><span class="nx">authorization</span><span class="p">;</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">authHeader</span> <span class="o">||</span> <span class="o">!</span><span class="nx">authHeader</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="dl">'</span><span class="s1">Bearer </span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">401</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Token d</span><span class="se">\'</span><span class="s1">authentification manquant</span><span class="dl">'</span>
      <span class="p">});</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">tokenId</span> <span class="o">=</span> <span class="nx">authHeader</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="mi">7</span><span class="p">);</span> <span class="c1">// Enlever "Bearer "</span>
    <span class="kd">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="nx">authManager</span><span class="p">.</span><span class="nx">validateToken</span><span class="p">(</span><span class="nx">tokenId</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">token</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">401</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Token invalide ou expiré</span><span class="dl">'</span>
      <span class="p">});</span>
    <span class="p">}</span>

    <span class="c1">// Ajouter les infos utilisateur à la requête</span>
    <span class="nx">req</span><span class="p">.</span><span class="nx">user</span> <span class="o">=</span> <span class="nx">token</span><span class="p">;</span>
    <span class="nx">next</span><span class="p">();</span>
  <span class="p">};</span>
<span class="p">}</span>

Système de Permissions Granulaires

Maintenant, créons un système qui vérifie si un utilisateur peut exécuter un outil spécifique. Créez src/security/permissions.ts :

<span class="c1">// src/security/permissions.ts</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">User</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./auth</span><span class="dl">'</span><span class="p">;</span>

<span class="cm">/**
 * Erreur de permission
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">PermissionError</span> <span class="kd">extends</span> <span class="nb">Error</span> <span class="p">{</span>
  <span class="kd">constructor</span><span class="p">(</span><span class="nx">message</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">super</span><span class="p">(</span><span class="nx">message</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">PermissionError</span><span class="dl">'</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="cm">/**
 * Gestionnaire de permissions
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">PermissionManager</span> <span class="p">{</span>
  <span class="cm">/**
   * Vérifier si un utilisateur a la permission d'utiliser un outil
   */</span>
  <span class="k">static</span> <span class="nx">hasPermission</span><span class="p">(</span>
    <span class="nx">user</span><span class="p">:</span> <span class="nx">User</span><span class="p">,</span>
    <span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
    <span class="nx">params</span><span class="p">?:</span> <span class="kr">any</span>
  <span class="p">):</span> <span class="nx">boolean</span> <span class="p">{</span>
    <span class="c1">// Les admins ont accès à tout</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">permissions</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="dl">'</span><span class="s1">*</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Vérifier la permission spécifique</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">user</span><span class="p">.</span><span class="nx">permissions</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="nx">toolName</span><span class="p">))</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Permissions contextuelles supplémentaires</span>
    <span class="c1">// Par exemple, vérifier les chemins autorisés pour readFile</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">toolName</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">readFile</span><span class="dl">'</span> <span class="o">&&</span> <span class="nx">params</span><span class="p">?.</span><span class="nx">chemin_du_fichier</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">canAccessPath</span><span class="p">(</span><span class="nx">user</span><span class="p">,</span> <span class="nx">params</span><span class="p">.</span><span class="nx">chemin_du_fichier</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Vérifier l'accès à un chemin spécifique
   */</span>
  <span class="k">private</span> <span class="k">static</span> <span class="nx">canAccessPath</span><span class="p">(</span><span class="nx">user</span><span class="p">:</span> <span class="nx">User</span><span class="p">,</span> <span class="nx">filePath</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">boolean</span> <span class="p">{</span>
    <span class="c1">// En readonly, uniquement lecture dans certains dossiers</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">role</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">readonly</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">allowedPaths</span> <span class="o">=</span> <span class="p">[</span><span class="dl">'</span><span class="s1">/public</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">/docs</span><span class="dl">'</span><span class="p">];</span>
      <span class="k">return</span> <span class="nx">allowedPaths</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="nx">allowed</span> <span class="o">=></span> 
        <span class="nx">filePath</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="nx">allowed</span><span class="p">)</span>
      <span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Obtenir les permissions d'un utilisateur
   */</span>
  <span class="k">static</span> <span class="nx">getPermissions</span><span class="p">(</span><span class="nx">user</span><span class="p">:</span> <span class="nx">User</span><span class="p">):</span> <span class="kr">string</span><span class="p">[]</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">user</span><span class="p">.</span><span class="nx">permissions</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Vérifier et lancer une erreur si pas de permission
   */</span>
  <span class="k">static</span> <span class="nx">requirePermission</span><span class="p">(</span>
    <span class="nx">user</span><span class="p">:</span> <span class="nx">User</span><span class="p">,</span>
    <span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
    <span class="nx">params</span><span class="p">?:</span> <span class="kr">any</span>
  <span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">hasPermission</span><span class="p">(</span><span class="nx">user</span><span class="p">,</span> <span class="nx">toolName</span><span class="p">,</span> <span class="nx">params</span><span class="p">))</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nx">PermissionError</span><span class="p">(</span>
        <span class="s2">`Permission refusée : vous n'avez pas accès à l'outil '</span><span class="p">${</span><span class="nx">toolName</span><span class="p">}</span><span class="s2">'`</span>
      <span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="cm">/**
 * Politique de permissions pour un outil
 */</span>
<span class="k">export</span> <span class="kr">interface</span> <span class="nx">ToolPolicy</span> <span class="p">{</span>
  <span class="nl">allowedRoles</span><span class="p">:</span> <span class="kr">string</span><span class="p">[];</span>
  <span class="nl">requiredPermissions</span><span class="p">:</span> <span class="kr">string</span><span class="p">[];</span>
  <span class="nl">rateLimit</span><span class="p">?:</span> <span class="p">{</span>
    <span class="na">maxRequests</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
    <span class="nl">windowMs</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
  <span class="p">};</span>
<span class="p">}</span>

<span class="cm">/**
 * Gestionnaire de politiques d'outils
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">PolicyManager</span> <span class="p">{</span>
  <span class="k">private</span> <span class="nx">policies</span><span class="p">:</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="nx">ToolPolicy</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Map</span><span class="p">();</span>

  <span class="cm">/**
   * Définir une politique pour un outil
   */</span>
  <span class="nx">setPolicy</span><span class="p">(</span><span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">policy</span><span class="p">:</span> <span class="nx">ToolPolicy</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">policies</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">toolName</span><span class="p">,</span> <span class="nx">policy</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Obtenir la politique d'un outil
   */</span>
  <span class="nx">getPolicy</span><span class="p">(</span><span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">ToolPolicy</span> <span class="o">|</span> <span class="kc">undefined</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">policies</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Vérifier qu'un utilisateur respecte la politique
   */</span>
  <span class="nx">checkPolicy</span><span class="p">(</span><span class="nx">user</span><span class="p">:</span> <span class="nx">User</span><span class="p">,</span> <span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">boolean</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">policy</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">policies</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">policy</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">true</span><span class="p">;</span> <span class="c1">// Pas de politique = autorisé par défaut</span>
    <span class="p">}</span>

    <span class="c1">// Vérifier le rôle</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">policy</span><span class="p">.</span><span class="nx">allowedRoles</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">role</span><span class="p">)</span> <span class="o">&&</span> 
        <span class="o">!</span><span class="nx">policy</span><span class="p">.</span><span class="nx">allowedRoles</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="dl">'</span><span class="s1">*</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Vérifier les permissions</span>
    <span class="kd">const</span> <span class="nx">hasAllPermissions</span> <span class="o">=</span> <span class="nx">policy</span><span class="p">.</span><span class="nx">requiredPermissions</span><span class="p">.</span><span class="nx">every</span><span class="p">(</span><span class="nx">perm</span> <span class="o">=></span>
      <span class="nx">user</span><span class="p">.</span><span class="nx">permissions</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="nx">perm</span><span class="p">)</span> <span class="o">||</span> <span class="nx">user</span><span class="p">.</span><span class="nx">permissions</span><span class="p">.</span><span class="nx">includes</span><span class="p">(</span><span class="dl">'</span><span class="s1">*</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">);</span>

    <span class="k">return</span> <span class="nx">hasAllPermissions</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

Rate Limiting et Quotas

Protégeons notre serveur contre les abus avec un système de rate limiting. Créez src/security/rateLimit.ts :

<span class="c1">// src/security/rateLimit.ts</span>

<span class="cm">/**
 * Enregistrement d'utilisation
 */</span>
<span class="kr">interface</span> <span class="nx">UsageRecord</span> <span class="p">{</span>
  <span class="nl">count</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
  <span class="nl">resetAt</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
<span class="p">}</span>

<span class="cm">/**
 * Gestionnaire de rate limiting
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">RateLimiter</span> <span class="p">{</span>
  <span class="k">private</span> <span class="nx">usage</span><span class="p">:</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="nx">UsageRecord</span><span class="o">></span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Map</span><span class="p">();</span>

  <span class="kd">constructor</span><span class="p">(</span>
    <span class="k">private</span> <span class="nx">maxRequests</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span>
    <span class="k">private</span> <span class="nx">windowMs</span><span class="p">:</span> <span class="kr">number</span>
  <span class="p">)</span> <span class="p">{}</span>

  <span class="cm">/**
   * Vérifier et incrémenter le compteur pour un utilisateur
   */</span>
  <span class="nx">checkLimit</span><span class="p">(</span><span class="nx">userId</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">boolean</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">now</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span>
    <span class="kd">const</span> <span class="nx">record</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">usage</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">userId</span><span class="p">);</span>

    <span class="c1">// Pas d'enregistrement ou fenêtre expirée</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">record</span> <span class="o">||</span> <span class="nx">now</span> <span class="o">></span> <span class="nx">record</span><span class="p">.</span><span class="nx">resetAt</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">usage</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">userId</span><span class="p">,</span> <span class="p">{</span>
        <span class="na">count</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
        <span class="na">resetAt</span><span class="p">:</span> <span class="nx">now</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">windowMs</span>
      <span class="p">});</span>
      <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Limite atteinte</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">record</span><span class="p">.</span><span class="nx">count</span> <span class="o">>=</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxRequests</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Incrémenter le compteur</span>
    <span class="nx">record</span><span class="p">.</span><span class="nx">count</span><span class="o">++</span><span class="p">;</span>
    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Obtenir les infos de limite pour un utilisateur
   */</span>
  <span class="nx">getLimitInfo</span><span class="p">(</span><span class="nx">userId</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="p">{</span>
    <span class="nl">current</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
    <span class="nl">max</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
    <span class="nl">resetsAt</span><span class="p">:</span> <span class="nb">Date</span><span class="p">;</span>
  <span class="p">}</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">record</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">usage</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">userId</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">record</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="p">{</span>
        <span class="na">current</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
        <span class="na">max</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxRequests</span><span class="p">,</span>
        <span class="na">resetsAt</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">windowMs</span><span class="p">)</span>
      <span class="p">};</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="p">{</span>
      <span class="na">current</span><span class="p">:</span> <span class="nx">record</span><span class="p">.</span><span class="nx">count</span><span class="p">,</span>
      <span class="na">max</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxRequests</span><span class="p">,</span>
      <span class="na">resetsAt</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="nx">record</span><span class="p">.</span><span class="nx">resetAt</span><span class="p">)</span>
    <span class="p">};</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Réinitialiser le compteur pour un utilisateur
   */</span>
  <span class="nx">reset</span><span class="p">(</span><span class="nx">userId</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">usage</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">userId</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Nettoyer les enregistrements expirés
   */</span>
  <span class="nx">cleanup</span><span class="p">():</span> <span class="k">void</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">now</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="p">[</span><span class="nx">userId</span><span class="p">,</span> <span class="nx">record</span><span class="p">]</span> <span class="k">of</span> <span class="k">this</span><span class="p">.</span><span class="nx">usage</span><span class="p">.</span><span class="nx">entries</span><span class="p">())</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">now</span> <span class="o">></span> <span class="nx">record</span><span class="p">.</span><span class="nx">resetAt</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">usage</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="nx">userId</span><span class="p">);</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="cm">/**
 * Middleware de rate limiting pour Express
 */</span>
<span class="k">export</span> <span class="kd">function</span> <span class="nx">rateLimitMiddleware</span><span class="p">(</span><span class="nx">rateLimiter</span><span class="p">:</span> <span class="nx">RateLimiter</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">next</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">userId</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">user</span><span class="p">?.</span><span class="nx">userId</span> <span class="o">||</span> <span class="nx">req</span><span class="p">.</span><span class="nx">ip</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">rateLimiter</span><span class="p">.</span><span class="nx">checkLimit</span><span class="p">(</span><span class="nx">userId</span><span class="p">))</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">info</span> <span class="o">=</span> <span class="nx">rateLimiter</span><span class="p">.</span><span class="nx">getLimitInfo</span><span class="p">(</span><span class="nx">userId</span><span class="p">);</span>
      <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">429</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Limite de requêtes atteinte</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">limit</span><span class="p">:</span> <span class="p">{</span>
          <span class="na">max</span><span class="p">:</span> <span class="nx">info</span><span class="p">.</span><span class="nx">max</span><span class="p">,</span>
          <span class="na">current</span><span class="p">:</span> <span class="nx">info</span><span class="p">.</span><span class="nx">current</span><span class="p">,</span>
          <span class="na">resetsAt</span><span class="p">:</span> <span class="nx">info</span><span class="p">.</span><span class="nx">resetsAt</span>
        <span class="p">}</span>
      <span class="p">});</span>
    <span class="p">}</span>

    <span class="nx">next</span><span class="p">();</span>
  <span class="p">};</span>
<span class="p">}</span>

<span class="cm">/**
 * Gestionnaire de quotas par outil
 */</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">QuotaManager</span> <span class="p">{</span>
  <span class="k">private</span> <span class="nx">quotas</span><span class="p">:</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="nb">Map</span><span class="o"><</span><span class="kr">string</span><span class="p">,</span> <span class="kr">number</span><span class="o">>></span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Map</span><span class="p">();</span>

  <span class="cm">/**
   * Définir un quota pour un utilisateur et un outil
   */</span>
  <span class="nx">setQuota</span><span class="p">(</span><span class="nx">userId</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">maxUsage</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">quotas</span><span class="p">.</span><span class="nx">has</span><span class="p">(</span><span class="nx">userId</span><span class="p">))</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">quotas</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">userId</span><span class="p">,</span> <span class="k">new</span> <span class="nb">Map</span><span class="p">());</span>
    <span class="p">}</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">quotas</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">userId</span><span class="p">)</span><span class="o">!</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">toolName</span><span class="p">,</span> <span class="nx">maxUsage</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Vérifier et décrémenter le quota
   */</span>
  <span class="nx">checkQuota</span><span class="p">(</span><span class="nx">userId</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">boolean</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">userQuotas</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">quotas</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">userId</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">userQuotas</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">true</span><span class="p">;</span> <span class="c1">// Pas de quota = illimité</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">remaining</span> <span class="o">=</span> <span class="nx">userQuotas</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">toolName</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="nx">remaining</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">true</span><span class="p">;</span> <span class="c1">// Pas de quota pour cet outil</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="nx">remaining</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">false</span><span class="p">;</span> <span class="c1">// Quota épuisé</span>
    <span class="p">}</span>

    <span class="nx">userQuotas</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">toolName</span><span class="p">,</span> <span class="nx">remaining</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Obtenir le quota restant
   */</span>
  <span class="nx">getRemainingQuota</span><span class="p">(</span><span class="nx">userId</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="kr">number</span> <span class="o">|</span> <span class="kc">null</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">userQuotas</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">quotas</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">userId</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">userQuotas</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">// Illimité</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="nx">userQuotas</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">toolName</span><span class="p">)</span> <span class="o">||</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Réinitialiser le quota d'un utilisateur
   */</span>
  <span class="nx">resetQuota</span><span class="p">(</span><span class="nx">userId</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">maxUsage</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">setQuota</span><span class="p">(</span><span class="nx">userId</span><span class="p">,</span> <span class="nx">toolName</span><span class="p">,</span> <span class="nx">maxUsage</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

Intégration dans le Serveur

Maintenant, intégrons toutes ces couches de sécurité dans notre serveur. Modifiez src/index.ts :

<span class="c1">// src/index.ts</span>
<span class="k">import</span> <span class="nx">express</span><span class="p">,</span> <span class="p">{</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">Response</span><span class="p">,</span> <span class="nx">NextFunction</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">express</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">MCP_PROTOCOL_VERSION</span><span class="p">,</span> <span class="nx">SERVER_INFO</span><span class="p">,</span> <span class="nx">DiscoveryResponse</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./mcp/protocol</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">toolRegistry</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./mcp/registry</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">AuthManager</span><span class="p">,</span> <span class="nx">authMiddleware</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./security/auth</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">PermissionManager</span><span class="p">,</span> <span class="nx">PermissionError</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./security/permissions</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">RateLimiter</span><span class="p">,</span> <span class="nx">rateLimitMiddleware</span><span class="p">,</span> <span class="nx">QuotaManager</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./security/rateLimit</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ParameterValidator</span><span class="p">,</span> <span class="nx">ValidationError</span><span class="p">,</span> <span class="nx">PathValidator</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./security/validator</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">PORT</span> <span class="o">=</span> <span class="mi">3000</span><span class="p">;</span>

<span class="c1">// ============================================</span>
<span class="c1">// INITIALISATION SÉCURITÉ</span>
<span class="c1">// ============================================</span>

<span class="kd">const</span> <span class="nx">authManager</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">AuthManager</span><span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">SECRET_KEY</span> <span class="o">||</span> <span class="dl">'</span><span class="s1">your-secret-key-change-me</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">rateLimiter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">RateLimiter</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">);</span> <span class="c1">// 100 req/minute</span>
<span class="kd">const</span> <span class="nx">quotaManager</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">QuotaManager</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">pathValidator</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PathValidator</span><span class="p">([</span>
  <span class="nx">process</span><span class="p">.</span><span class="nx">cwd</span><span class="p">(),</span> <span class="c1">// Répertoire courant</span>
  <span class="dl">'</span><span class="s1">/tmp</span><span class="dl">'</span><span class="p">,</span>        <span class="c1">// Dossier temporaire</span>
<span class="p">]);</span>

<span class="c1">// Quotas par défaut</span>
<span class="nx">quotaManager</span><span class="p">.</span><span class="nx">setQuota</span><span class="p">(</span><span class="dl">'</span><span class="s1">2</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">readFile</span><span class="dl">'</span><span class="p">,</span> <span class="mi">1000</span><span class="p">);</span> <span class="c1">// user : 1000 lectures/jour</span>
<span class="nx">quotaManager</span><span class="p">.</span><span class="nx">setQuota</span><span class="p">(</span><span class="dl">'</span><span class="s1">3</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">readFile</span><span class="dl">'</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>  <span class="c1">// readonly : 100 lectures/jour</span>

<span class="c1">// ============================================</span>
<span class="c1">// MIDDLEWARES</span>
<span class="c1">// ============================================</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">express</span><span class="p">.</span><span class="nx">json</span><span class="p">());</span>

<span class="c1">// Logging</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`[</span><span class="p">${</span><span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">()}</span><span class="s2">] </span><span class="p">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">method</span><span class="p">}</span><span class="s2"> </span><span class="p">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">path</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="nx">next</span><span class="p">();</span>
<span class="p">});</span>

<span class="c1">// ============================================</span>
<span class="c1">// ROUTES PUBLIQUES (sans auth)</span>
<span class="c1">// ============================================</span>

<span class="cm">/**
 * Page d'accueil
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
    <span class="na">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">MCP File Server - Secured</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">version</span><span class="p">:</span> <span class="nx">SERVER_INFO</span><span class="p">.</span><span class="nx">version</span><span class="p">,</span>
    <span class="na">protocol_version</span><span class="p">:</span> <span class="nx">MCP_PROTOCOL_VERSION</span><span class="p">,</span>
    <span class="na">status</span><span class="p">:</span> <span class="dl">'</span><span class="s1">operational</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">security</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">authentication</span><span class="p">:</span> <span class="dl">'</span><span class="s1">required</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">rateLimit</span><span class="p">:</span> <span class="dl">'</span><span class="s1">100 requests/minute</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">endpoints</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">auth</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/auth/login</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">discovery</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/mcp/tools</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">execute</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/mcp/execute</span><span class="dl">'</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">});</span>
<span class="p">});</span>

<span class="cm">/**
 * Endpoint d'authentification
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="dl">'</span><span class="s1">/auth/login</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">username</span><span class="p">,</span> <span class="nx">password</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">username</span> <span class="o">||</span> <span class="o">!</span><span class="nx">password</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">400</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
      <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
      <span class="na">error</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Username et password requis</span><span class="dl">'</span>
    <span class="p">});</span>
  <span class="p">}</span>

  <span class="kd">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="nx">authManager</span><span class="p">.</span><span class="nx">authenticate</span><span class="p">(</span><span class="nx">username</span><span class="p">,</span> <span class="nx">password</span><span class="p">);</span>

  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">token</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">401</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
      <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
      <span class="na">error</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Identifiants invalides</span><span class="dl">'</span>
    <span class="p">});</span>
  <span class="p">}</span>

  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
    <span class="na">success</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="na">token</span><span class="p">:</span> <span class="nx">token</span><span class="p">,</span>
    <span class="na">message</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Authentification réussie</span><span class="dl">'</span>
  <span class="p">});</span>
<span class="p">});</span>

<span class="cm">/**
 * Health check (public)
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/health</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
    <span class="na">status</span><span class="p">:</span> <span class="dl">'</span><span class="s1">healthy</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">uptime</span><span class="p">:</span> <span class="nx">process</span><span class="p">.</span><span class="nx">uptime</span><span class="p">(),</span>
    <span class="na">tools_count</span><span class="p">:</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">count</span><span class="p">(),</span>
    <span class="na">timestamp</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">()</span>
  <span class="p">});</span>
<span class="p">});</span>

<span class="c1">// ============================================</span>
<span class="c1">// ROUTES PROTÉGÉES (avec auth + rate limit)</span>
<span class="c1">// ============================================</span>

<span class="c1">// Appliquer l'authentification à toutes les routes /mcp/*</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp</span><span class="dl">'</span><span class="p">,</span> <span class="nx">authMiddleware</span><span class="p">(</span><span class="nx">authManager</span><span class="p">));</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp</span><span class="dl">'</span><span class="p">,</span> <span class="nx">rateLimitMiddleware</span><span class="p">(</span><span class="nx">rateLimiter</span><span class="p">));</span>

<span class="cm">/**
 * Endpoint de découverte (protégé)
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/tools</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">user</span><span class="p">;</span>
  
  <span class="c1">// Filtrer les outils selon les permissions</span>
  <span class="kd">const</span> <span class="nx">allTools</span> <span class="o">=</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">getAllDescriptions</span><span class="p">();</span>
  <span class="kd">const</span> <span class="nx">allowedTools</span> <span class="o">=</span> <span class="nx">allTools</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">tool</span> <span class="o">=></span>
    <span class="nx">PermissionManager</span><span class="p">.</span><span class="nx">hasPermission</span><span class="p">(</span>
      <span class="p">{</span> 
        <span class="na">id</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">userId</span><span class="p">,</span>
        <span class="na">username</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">username</span><span class="p">,</span>
        <span class="na">role</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">role</span><span class="p">,</span>
        <span class="na">permissions</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">permissions</span>
      <span class="p">},</span>
      <span class="nx">tool</span><span class="p">.</span><span class="nx">name</span>
    <span class="p">)</span>
  <span class="p">);</span>

  <span class="kd">const</span> <span class="na">response</span><span class="p">:</span> <span class="nx">DiscoveryResponse</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">protocol_version</span><span class="p">:</span> <span class="nx">MCP_PROTOCOL_VERSION</span><span class="p">,</span>
    <span class="na">server_info</span><span class="p">:</span> <span class="nx">SERVER_INFO</span><span class="p">,</span>
    <span class="na">tools</span><span class="p">:</span> <span class="nx">allowedTools</span>
  <span class="p">};</span>

  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`📋 Découverte - </span><span class="p">${</span><span class="nx">allowedTools</span><span class="p">.</span><span class="nx">length</span><span class="p">}</span><span class="s2">/</span><span class="p">${</span><span class="nx">allTools</span><span class="p">.</span><span class="nx">length</span><span class="p">}</span><span class="s2"> outils visibles pour </span><span class="p">${</span><span class="nx">user</span><span class="p">.</span><span class="nx">username</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span>
<span class="p">});</span>

<span class="cm">/**
 * Endpoint d'exécution sécurisé
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/execute</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="kd">const</span> <span class="p">{</span> <span class="nx">tool</span><span class="p">,</span> <span class="nx">params</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">user</span><span class="p">;</span>

  <span class="k">try</span> <span class="p">{</span>
    <span class="c1">// Validation basique</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">tool</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">400</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Le paramètre 'tool' est requis</span><span class="dl">"</span>
      <span class="p">});</span>
    <span class="p">}</span>

    <span class="c1">// Vérifier les permissions</span>
    <span class="nx">PermissionManager</span><span class="p">.</span><span class="nx">requirePermission</span><span class="p">(</span>
      <span class="p">{</span>
        <span class="na">id</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">userId</span><span class="p">,</span>
        <span class="na">username</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">username</span><span class="p">,</span>
        <span class="na">role</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">role</span><span class="p">,</span>
        <span class="na">permissions</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">permissions</span>
      <span class="p">},</span>
      <span class="nx">tool</span><span class="p">,</span>
      <span class="nx">params</span>
    <span class="p">);</span>

    <span class="c1">// Vérifier les quotas</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">quotaManager</span><span class="p">.</span><span class="nx">checkQuota</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">userId</span><span class="p">,</span> <span class="nx">tool</span><span class="p">))</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">403</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Quota épuisé pour cet outil</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">remaining</span><span class="p">:</span> <span class="nx">quotaManager</span><span class="p">.</span><span class="nx">getRemainingQuota</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">userId</span><span class="p">,</span> <span class="nx">tool</span><span class="p">)</span>
      <span class="p">});</span>
    <span class="p">}</span>

    <span class="c1">// Obtenir la description de l'outil pour validation</span>
    <span class="kd">const</span> <span class="nx">toolDescription</span> <span class="o">=</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">getDescription</span><span class="p">(</span><span class="nx">tool</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">toolDescription</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">404</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="s2">`Outil '</span><span class="p">${</span><span class="nx">tool</span><span class="p">}</span><span class="s2">' introuvable`</span>
      <span class="p">});</span>
    <span class="p">}</span>

    <span class="c1">// Valider les paramètres selon le schéma</span>
    <span class="nx">ParameterValidator</span><span class="p">.</span><span class="nx">validate</span><span class="p">(</span><span class="nx">params</span> <span class="o">||</span> <span class="p">{},</span> <span class="nx">toolDescription</span><span class="p">.</span><span class="nx">input_schema</span><span class="p">);</span>

    <span class="c1">// Validation spécifique des chemins pour les outils de fichiers</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">tool</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">readFile</span><span class="dl">'</span> <span class="o">||</span> <span class="nx">tool</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">listFiles</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">pathParam</span> <span class="o">=</span> <span class="nx">params</span><span class="p">.</span><span class="nx">chemin_du_fichier</span> <span class="o">||</span> <span class="nx">params</span><span class="p">.</span><span class="nx">chemin_du_dossier</span><span class="p">;</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">pathParam</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">params</span><span class="p">.</span><span class="nx">validated_path</span> <span class="o">=</span> <span class="nx">pathValidator</span><span class="p">.</span><span class="nx">validatePath</span><span class="p">(</span><span class="nx">pathParam</span><span class="p">);</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`⚙️  Exécution sécurisée : </span><span class="p">${</span><span class="nx">tool</span><span class="p">}</span><span class="s2"> par </span><span class="p">${</span><span class="nx">user</span><span class="p">.</span><span class="nx">username</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>

    <span class="c1">// Exécution via le registre</span>
    <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="nx">tool</span><span class="p">,</span> <span class="nx">params</span><span class="p">);</span>

    <span class="c1">// Log du résultat</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">success</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`✅ Succès : </span><span class="p">${</span><span class="nx">tool</span><span class="p">}</span><span class="s2"> par </span><span class="p">${</span><span class="nx">user</span><span class="p">.</span><span class="nx">username</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`❌ Échec : </span><span class="p">${</span><span class="nx">tool</span><span class="p">}</span><span class="s2"> par </span><span class="p">${</span><span class="nx">user</span><span class="p">.</span><span class="nx">username</span><span class="p">}</span><span class="s2"> - </span><span class="p">${</span><span class="nx">result</span><span class="p">.</span><span class="nx">error</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>

  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="na">error</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Gestion des erreurs de sécurité</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">error</span> <span class="k">instanceof</span> <span class="nx">ValidationError</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">400</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">,</span>
        <span class="na">field</span><span class="p">:</span> <span class="nx">error</span><span class="p">.</span><span class="nx">field</span>
      <span class="p">});</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="nx">error</span> <span class="k">instanceof</span> <span class="nx">PermissionError</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">403</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
        <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
        <span class="na">error</span><span class="p">:</span> <span class="nx">error</span><span class="p">.</span><span class="nx">message</span>
      <span class="p">});</span>
    <span class="p">}</span>

    <span class="c1">// Erreur générique</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Erreur serveur:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
    <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">500</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
      <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
      <span class="na">error</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Erreur interne du serveur</span><span class="dl">'</span>
    <span class="p">});</span>
  <span class="p">}</span>
<span class="p">});</span>

<span class="cm">/**
 * Endpoint pour voir ses permissions
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/me</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">user</span><span class="p">;</span>
  
  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
    <span class="na">userId</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">userId</span><span class="p">,</span>
    <span class="na">username</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">username</span><span class="p">,</span>
    <span class="na">role</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">role</span><span class="p">,</span>
    <span class="na">permissions</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">permissions</span><span class="p">,</span>
    <span class="na">rateLimit</span><span class="p">:</span> <span class="nx">rateLimiter</span><span class="p">.</span><span class="nx">getLimitInfo</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">userId</span><span class="p">)</span>
  <span class="p">});</span>
<span class="p">});</span>

<span class="cm">/**
 * Endpoint pour voir ses quotas
 */</span>
<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/quotas</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">user</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">tools</span> <span class="o">=</span> <span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">getAllDescriptions</span><span class="p">();</span>
  
  <span class="kd">const</span> <span class="nx">quotas</span> <span class="o">=</span> <span class="nx">tools</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">tool</span> <span class="o">=></span> <span class="p">({</span>
    <span class="na">tool</span><span class="p">:</span> <span class="nx">tool</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span>
    <span class="na">remaining</span><span class="p">:</span> <span class="nx">quotaManager</span><span class="p">.</span><span class="nx">getRemainingQuota</span><span class="p">(</span><span class="nx">user</span><span class="p">.</span><span class="nx">userId</span><span class="p">,</span> <span class="nx">tool</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span>
  <span class="p">}));</span>

  <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span>
    <span class="na">userId</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">userId</span><span class="p">,</span>
    <span class="na">quotas</span><span class="p">:</span> <span class="nx">quotas</span>
  <span class="p">});</span>
<span class="p">});</span>

<span class="c1">// ============================================</span>
<span class="c1">// GESTION DES ERREURS</span>
<span class="c1">// ============================================</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">((</span><span class="nx">err</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">req</span><span class="p">:</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span><span class="p">:</span> <span class="nx">Response</span><span class="p">,</span> <span class="nx">next</span><span class="p">:</span> <span class="nx">NextFunction</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Erreur non gérée:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">500</span><span class="p">).</span><span class="nx">json</span><span class="p">({</span>
    <span class="na">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
    <span class="na">error</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Erreur interne du serveur</span><span class="dl">'</span>
  <span class="p">});</span>
<span class="p">});</span>

<span class="c1">// ============================================</span>
<span class="c1">// TÂCHES DE MAINTENANCE</span>
<span class="c1">// ============================================</span>

<span class="c1">// Nettoyer les tokens expirés toutes les heures</span>
<span class="nx">setInterval</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
  <span class="nx">authManager</span><span class="p">.</span><span class="nx">cleanExpiredTokens</span><span class="p">();</span>
  <span class="nx">rateLimiter</span><span class="p">.</span><span class="nx">cleanup</span><span class="p">();</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">🧹 Nettoyage des tokens et rate limits expirés</span><span class="dl">'</span><span class="p">);</span>
<span class="p">},</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">);</span>

<span class="c1">// ============================================</span>
<span class="c1">// DÉMARRAGE DU SERVEUR</span>
<span class="c1">// ============================================</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="nx">PORT</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">🔒 MCP File Server - Secured Edition</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`📍 URL: http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`🔐 Auth: POST http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">/auth/login`</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`📋 Découverte: GET http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">/mcp/tools`</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`⚙️  Exécution: POST http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">/mcp/execute`</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`🔧 Outils: </span><span class="p">${</span><span class="nx">toolRegistry</span><span class="p">.</span><span class="nx">count</span><span class="p">()}</span><span class="s2">`</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">👤 Utilisateurs de test:</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">   - admin (toutes permissions)</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">   - user (lecture + liste + recherche)</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">   - readonly (lecture + liste seulement)</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>

Tester le Système Sécurisé

Relançons notre serveur et testons toutes les couches de sécurité :

npm run dev

Test 1 : Authentification

D’abord, essayons d’accéder sans authentification :

curl http://localhost:3000/mcp/tools

Résultat :

<span class="p">{</span><span class="w">
  </span><span class="nl">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
  </span><span class="nl">"error"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Token d'authentification manquant"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

Parfait ! Maintenant, authentifions-nous :

curl <span class="nt">-X</span> POST http://localhost:3000/auth/login <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{"username": "user", "password": "any"}'</span>

Réponse :

<span class="p">{</span><span class="w">
  </span><span class="nl">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
  </span><span class="nl">"token"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a1b2c3d4e5f6..."</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Authentification réussie"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

Sauvegardez ce token dans une variable :

<span class="nv">TOKEN</span><span class="o">=</span><span class="s2">"a1b2c3d4e5f6..."</span>

Test 2 : Découverte avec Permissions

Maintenant, découvrons les outils avec notre token :

curl http://localhost:3000/mcp/tools <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">"</span>

L’utilisateur “user” ne verra que les outils auxquels il a accès (readFile, listFiles, searchFiles).

Comparons avec un utilisateur readonly :

<span class="c"># S'authentifier en readonly</span>
curl <span class="nt">-X</span> POST http://localhost:3000/auth/login <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{"username": "readonly", "password": "any"}'</span> <span class="se">\</span>
  | jq <span class="nt">-r</span> <span class="s1">'.token'</span>

<span class="c"># Il verra moins d'outils (seulement readFile et listFiles)</span>

Test 3 : Validation des Paramètres

Testons avec des paramètres invalides :

curl <span class="nt">-X</span> POST http://localhost:3000/mcp/execute <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{
    "tool": "readFile",
    "params": {
      "encoding": "invalid_encoding"
    }
  }'</span>

Réponse :

<span class="p">{</span><span class="w">
  </span><span class="nl">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
  </span><span class="nl">"error"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Le champ 'chemin_du_fichier' est requis"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"field"</span><span class="p">:</span><span class="w"> </span><span class="s2">"chemin_du_fichier"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

Testons avec une énumération invalide :

curl <span class="nt">-X</span> POST http://localhost:3000/mcp/execute <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{
    "tool": "readFile",
    "params": {
      "chemin_du_fichier": "test.txt",
      "encoding": "invalid"
    }
  }'</span>

Réponse :

<span class="p">{</span><span class="w">
  </span><span class="nl">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
  </span><span class="nl">"error"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Le champ 'encoding' doit être l'une des valeurs : utf-8, ascii, base64"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"field"</span><span class="p">:</span><span class="w"> </span><span class="s2">"encoding"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

Test 4 : Protection Path Traversal

Essayons d’accéder à un fichier sensible :

curl <span class="nt">-X</span> POST http://localhost:3000/mcp/execute <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{
    "tool": "readFile",
    "params": {
      "chemin_du_fichier": "../../../etc/passwd"
    }
  }'</span>

Réponse :

<span class="p">{</span><span class="w">
  </span><span class="nl">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
  </span><span class="nl">"error"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Les chemins avec </span><span class="se">\"</span><span class="s2">..</span><span class="se">\"</span><span class="s2"> ne sont pas autorisés (path traversal)"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

Excellent ! Notre protection fonctionne.

Test 5 : Rate Limiting

Créons un script pour tester le rate limiting. Créez test-rate-limit.sh :

<span class="c">#!/bin/bash</span>

<span class="nv">TOKEN</span><span class="o">=</span><span class="s2">"votre-token-ici"</span>

<span class="nb">echo</span> <span class="s2">"Test de rate limiting - 100 requêtes rapides..."</span>

<span class="k">for </span>i <span class="k">in</span> <span class="o">{</span>1..105<span class="o">}</span><span class="p">;</span> <span class="k">do
  </span><span class="nv">response</span><span class="o">=</span><span class="si">$(</span>curl <span class="nt">-s</span> <span class="nt">-w</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">%{http_code}"</span> <span class="se">\</span>
    <span class="nt">-X</span> POST http://localhost:3000/mcp/execute <span class="se">\</span>
    <span class="nt">-H</span> <span class="s2">"Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">"</span> <span class="se">\</span>
    <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
    <span class="nt">-d</span> <span class="s1">'{"tool": "listFiles", "params": {"chemin_du_dossier": "."}}'</span><span class="si">)</span>
  
  <span class="nv">status_code</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$response</span><span class="s2">"</span> | <span class="nb">tail</span> <span class="nt">-n1</span><span class="si">)</span>
  
  <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$status_code</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"429"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="s2">"Requête </span><span class="nv">$i</span><span class="s2"> : Rate limit atteint ! ✓"</span>
    <span class="nb">echo</span> <span class="s2">"</span><span class="nv">$response</span><span class="s2">"</span> | <span class="nb">head</span> <span class="nt">-n-1</span> | jq <span class="nb">.</span>
    <span class="nb">break
  </span><span class="k">else
    </span><span class="nb">echo</span> <span class="s2">"Requête </span><span class="nv">$i</span><span class="s2"> : OK"</span>
  <span class="k">fi
done</span>

Après 100 requêtes, vous verrez :

<span class="p">{</span><span class="w">
  </span><span class="nl">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
  </span><span class="nl">"error"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Limite de requêtes atteinte"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"limit"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"max"</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w">
    </span><span class="nl">"current"</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w">
    </span><span class="nl">"resetsAt"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-12-10T15:30:00.000Z"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

Test 6 : Quotas

Testons les quotas. Créez un fichier de test puis exécutez plusieurs lectures :

<span class="c"># Voir ses quotas actuels</span>
curl http://localhost:3000/mcp/quotas <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">"</span>

Réponse :

<span class="p">{</span><span class="w">
  </span><span class="nl">"userId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"quotas"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"tool"</span><span class="p">:</span><span class="w"> </span><span class="s2">"readFile"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"remaining"</span><span class="p">:</span><span class="w"> </span><span class="mi">1000</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"tool"</span><span class="p">:</span><span class="w"> </span><span class="s2">"listFiles"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"remaining"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"tool"</span><span class="p">:</span><span class="w"> </span><span class="s2">"searchFiles"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"remaining"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

Créer un Client Sécurisé

Pour faciliter les tests, créons un client qui gère automatiquement l’authentification. Créez src/secure-client.ts :

<span class="c1">// src/secure-client.ts</span>

<span class="kd">const</span> <span class="nx">SERVER_URL</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">http://localhost:3000</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">class</span> <span class="nx">SecureClient</span> <span class="p">{</span>
  <span class="k">private</span> <span class="nx">token</span><span class="p">:</span> <span class="kr">string</span> <span class="o">|</span> <span class="kc">null</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>

  <span class="cm">/**
   * S'authentifier
   */</span>
  <span class="k">async</span> <span class="nx">login</span><span class="p">(</span><span class="nx">username</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">password</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nb">Promise</span><span class="o"><</span><span class="nx">boolean</span><span class="o">></span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">SERVER_URL</span><span class="p">}</span><span class="s2">/auth/login`</span><span class="p">,</span> <span class="p">{</span>
      <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
        <span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span>
      <span class="p">},</span>
      <span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span> <span class="nx">username</span><span class="p">,</span> <span class="nx">password</span> <span class="p">})</span>
    <span class="p">});</span>

    <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>

    <span class="k">if</span> <span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">success</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">token</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">token</span><span class="p">;</span>
      <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`✅ Authentifié en tant que </span><span class="p">${</span><span class="nx">username</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
      <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`❌ Échec d'authentification`</span><span class="p">);</span>
    <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Découvrir les outils disponibles
   */</span>
  <span class="k">async</span> <span class="nx">discoverTools</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">token</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Non authentifié</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">SERVER_URL</span><span class="p">}</span><span class="s2">/mcp/tools`</span><span class="p">,</span> <span class="p">{</span>
      <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
        <span class="dl">'</span><span class="s1">Authorization</span><span class="dl">'</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">token</span><span class="p">}</span><span class="s2">`</span>
      <span class="p">}</span>
    <span class="p">});</span>

    <span class="k">return</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Exécuter un outil
   */</span>
  <span class="k">async</span> <span class="nx">executeTool</span><span class="p">(</span><span class="nx">toolName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">params</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">token</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Non authentifié</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">SERVER_URL</span><span class="p">}</span><span class="s2">/mcp/execute`</span><span class="p">,</span> <span class="p">{</span>
      <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
        <span class="dl">'</span><span class="s1">Authorization</span><span class="dl">'</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">token</span><span class="p">}</span><span class="s2">`</span><span class="p">,</span>
        <span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span>
      <span class="p">},</span>
      <span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span>
        <span class="na">tool</span><span class="p">:</span> <span class="nx">toolName</span><span class="p">,</span>
        <span class="na">params</span><span class="p">:</span> <span class="nx">params</span>
      <span class="p">})</span>
    <span class="p">});</span>

    <span class="k">return</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Voir ses informations
   */</span>
  <span class="k">async</span> <span class="nx">getMe</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">token</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Non authentifié</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">SERVER_URL</span><span class="p">}</span><span class="s2">/mcp/me`</span><span class="p">,</span> <span class="p">{</span>
      <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
        <span class="dl">'</span><span class="s1">Authorization</span><span class="dl">'</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">token</span><span class="p">}</span><span class="s2">`</span>
      <span class="p">}</span>
    <span class="p">});</span>

    <span class="k">return</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="cm">/**
   * Voir ses quotas
   */</span>
  <span class="k">async</span> <span class="nx">getQuotas</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">token</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Non authentifié</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">SERVER_URL</span><span class="p">}</span><span class="s2">/mcp/quotas`</span><span class="p">,</span> <span class="p">{</span>
      <span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
        <span class="dl">'</span><span class="s1">Authorization</span><span class="dl">'</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">token</span><span class="p">}</span><span class="s2">`</span>
      <span class="p">}</span>
    <span class="p">});</span>

    <span class="k">return</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="cm">/**
 * Démonstration complète
 */</span>
<span class="k">async</span> <span class="kd">function</span> <span class="nx">demo</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">🔒 Démo Client Sécurisé MCP</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">═══════════════════════════════════════</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>

  <span class="kd">const</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">SecureClient</span><span class="p">();</span>

  <span class="c1">// Test 1 : Authentification</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">📝 Test 1 : Authentification</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>
  <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">login</span><span class="p">(</span><span class="dl">'</span><span class="s1">user</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">password</span><span class="dl">'</span><span class="p">);</span>

  <span class="c1">// Test 2 : Voir ses infos</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="s1">📝 Test 2 : Informations utilisateur</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">me</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">getMe</span><span class="p">();</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">me</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>

  <span class="c1">// Test 3 : Découverte des outils</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="s1">📝 Test 3 : Découverte des outils</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">discovery</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">discoverTools</span><span class="p">();</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`Outils disponibles : </span><span class="p">${</span><span class="nx">discovery</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="nx">length</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="nx">discovery</span><span class="p">.</span><span class="nx">tools</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">tool</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`  - </span><span class="p">${</span><span class="nx">tool</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="s2">: </span><span class="p">${</span><span class="nx">tool</span><span class="p">.</span><span class="nx">description</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="p">});</span>

  <span class="c1">// Test 4 : Exécution d'un outil</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="s1">📝 Test 4 : Exécution d</span><span class="se">\'</span><span class="s1">un outil</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">executeTool</span><span class="p">(</span><span class="dl">'</span><span class="s1">listFiles</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
    <span class="na">chemin_du_dossier</span><span class="p">:</span> <span class="dl">'</span><span class="s1">.</span><span class="dl">'</span>
  <span class="p">});</span>
  
  <span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">success</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">✅ Succès !</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`❌ Erreur : </span><span class="p">${</span><span class="nx">result</span><span class="p">.</span><span class="nx">error</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="c1">// Test 5 : Vérifier les quotas</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="s1">📝 Test 5 : Quotas restants</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">quotas</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">getQuotas</span><span class="p">();</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">quotas</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>

  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="s1">═══════════════════════════════════════</span><span class="dl">'</span><span class="p">);</span>
<span class="p">}</span>

<span class="nx">demo</span><span class="p">().</span><span class="k">catch</span><span class="p">(</span><span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">);</span>

Ajoutez le script dans package.json :

<span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"dev"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ts-node src/index.ts"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"secure-client"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ts-node src/secure-client.ts"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

Lancez-le :

npm run secure-client

Dashboard de Sécurité

Créons une interface web pour visualiser la sécurité. Créez src/public/security.html :

<span class="cp"></span>
<span class="nt"><html</span> <span class="na">lang=</span><span class="s">"fr"</span><span class="nt">></span>
<span class="nt"><head></span>
  <span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">></span>
  <span class="nt"><meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="nt">></span>
  <span class="nt"><title></span>MCP Security Dashboard<span class="nt"></title></span>
  <span class="nt"><style></span>
    <span class="o">*</span> <span class="p">{</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">padding</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">box-sizing</span><span class="p">:</span> <span class="n">border-box</span><span class="p">;</span> <span class="p">}</span>
    <span class="nt">body</span> <span class="p">{</span> 
      <span class="nl">font-family</span><span class="p">:</span> <span class="s2">'Segoe UI'</span><span class="p">,</span> <span class="n">system-ui</span><span class="p">,</span> <span class="nb">sans-serif</span><span class="p">;</span> 
      <span class="nl">background</span><span class="p">:</span> <span class="n">linear-gradient</span><span class="p">(</span><span class="m">135deg</span><span class="p">,</span> <span class="m">#1e3a8a</span> <span class="m">0%</span><span class="p">,</span> <span class="m">#ef4444</span> <span class="m">100%</span><span class="p">);</span>
      <span class="nl">padding</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.container</span> <span class="p">{</span> 
      <span class="nl">max-width</span><span class="p">:</span> <span class="m">1200px</span><span class="p">;</span> 
      <span class="nl">margin</span><span class="p">:</span> <span class="m">0</span> <span class="nb">auto</span><span class="p">;</span> 
      <span class="nl">background</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
      <span class="nl">border-radius</span><span class="p">:</span> <span class="m">12px</span><span class="p">;</span>
      <span class="nl">padding</span><span class="p">:</span> <span class="m">30px</span><span class="p">;</span>
      <span class="nl">box-shadow</span><span class="p">:</span> <span class="m">0</span> <span class="m">20px</span> <span class="m">60px</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0.3</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="nt">h1</span> <span class="p">{</span> 
      <span class="nl">color</span><span class="p">:</span> <span class="m">#1e3a8a</span><span class="p">;</span> 
      <span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
      <span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
      <span class="nl">align-items</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
      <span class="py">gap</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.subtitle</span> <span class="p">{</span>
      <span class="nl">color</span><span class="p">:</span> <span class="m">#64748b</span><span class="p">;</span>
      <span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">30px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.login-form</span> <span class="p">{</span>
      <span class="nl">background</span><span class="p">:</span> <span class="m">#f8fafc</span><span class="p">;</span>
      <span class="nl">padding</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
      <span class="nl">border-radius</span><span class="p">:</span> <span class="m">8px</span><span class="p">;</span>
      <span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.login-form</span> <span class="nt">input</span> <span class="p">{</span>
      <span class="nl">width</span><span class="p">:</span> <span class="m">200px</span><span class="p">;</span>
      <span class="nl">padding</span><span class="p">:</span> <span class="m">8px</span> <span class="m">12px</span><span class="p">;</span>
      <span class="nl">margin</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span>
      <span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="m">#cbd5e1</span><span class="p">;</span>
      <span class="nl">border-radius</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.login-form</span> <span class="nt">button</span> <span class="p">{</span>
      <span class="nl">padding</span><span class="p">:</span> <span class="m">8px</span> <span class="m">16px</span><span class="p">;</span>
      <span class="nl">background</span><span class="p">:</span> <span class="m">#1e3a8a</span><span class="p">;</span>
      <span class="nl">color</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
      <span class="nl">border</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
      <span class="nl">border-radius</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span>
      <span class="nl">cursor</span><span class="p">:</span> <span class="nb">pointer</span><span class="p">;</span>
      <span class="nl">margin</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.login-form</span> <span class="nt">button</span><span class="nd">:hover</span> <span class="p">{</span>
      <span class="nl">background</span><span class="p">:</span> <span class="m">#1e40af</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.user-info</span> <span class="p">{</span>
      <span class="nl">background</span><span class="p">:</span> <span class="m">#eff6ff</span><span class="p">;</span>
      <span class="nl">padding</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
      <span class="nl">border-radius</span><span class="p">:</span> <span class="m">8px</span><span class="p">;</span>
      <span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.security-grid</span> <span class="p">{</span>
      <span class="nl">display</span><span class="p">:</span> <span class="n">grid</span><span class="p">;</span>
      <span class="py">grid-template-columns</span><span class="p">:</span> <span class="nb">repeat</span><span class="p">(</span><span class="n">auto-fit</span><span class="p">,</span> <span class="n">minmax</span><span class="p">(</span><span class="m">300px</span><span class="p">,</span> <span class="m">1</span><span class="n">fr</span><span class="p">));</span>
      <span class="py">gap</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
      <span class="nl">margin-top</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.security-card</span> <span class="p">{</span>
      <span class="nl">background</span><span class="p">:</span> <span class="m">#f8fafc</span><span class="p">;</span>
      <span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="m">#e2e8f0</span><span class="p">;</span>
      <span class="nl">border-radius</span><span class="p">:</span> <span class="m">8px</span><span class="p">;</span>
      <span class="nl">padding</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.security-card</span> <span class="nt">h3</span> <span class="p">{</span>
      <span class="nl">color</span><span class="p">:</span> <span class="m">#1e3a8a</span><span class="p">;</span>
      <span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">15px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.permission-badge</span> <span class="p">{</span>
      <span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
      <span class="nl">padding</span><span class="p">:</span> <span class="m">4px</span> <span class="m">8px</span><span class="p">;</span>
      <span class="nl">background</span><span class="p">:</span> <span class="m">#3b82f6</span><span class="p">;</span>
      <span class="nl">color</span><span class="p">:</span> <span class="no">white</span><span class="p">;</span>
      <span class="nl">border-radius</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span>
      <span class="nl">font-size</span><span class="p">:</span> <span class="m">0.85em</span><span class="p">;</span>
      <span class="nl">margin</span><span class="p">:</span> <span class="m">2px</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.quota-bar</span> <span class="p">{</span>
      <span class="nl">height</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span>
      <span class="nl">background</span><span class="p">:</span> <span class="m">#e2e8f0</span><span class="p">;</span>
      <span class="nl">border-radius</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span>
      <span class="nl">overflow</span><span class="p">:</span> <span class="nb">hidden</span><span class="p">;</span>
      <span class="nl">margin</span><span class="p">:</span> <span class="m">10px</span> <span class="m">0</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.quota-fill</span> <span class="p">{</span>
      <span class="nl">height</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
      <span class="nl">background</span><span class="p">:</span> <span class="n">linear-gradient</span><span class="p">(</span><span class="m">90deg</span><span class="p">,</span> <span class="m">#3b82f6</span><span class="p">,</span> <span class="m">#8b5cf6</span><span class="p">);</span>
      <span class="nl">transition</span><span class="p">:</span> <span class="n">width</span> <span class="m">0.3s</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="nc">.hidden</span> <span class="p">{</span> <span class="nl">display</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span> <span class="p">}</span>
    <span class="nc">.error</span> <span class="p">{</span> <span class="nl">color</span><span class="p">:</span> <span class="m">#ef4444</span><span class="p">;</span> <span class="nl">padding</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span> <span class="nl">background</span><span class="p">:</span> <span class="m">#fee2e2</span><span class="p">;</span> <span class="nl">border-radius</span><span class="p">:</span> <span class="m">4px</span><span class="p">;</span> <span class="p">}</span>
  <span class="nt"></style></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
  <span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
    <span class="nt"><h1></span>🔒 MCP Security Dashboard<span class="nt"></h1></span>
    <span class="nt"><p</span> <span class="na">class=</span><span class="s">"subtitle"</span><span class="nt">></span>Gestion de la sécurité et des permissions<span class="nt"></p></span>
    
    <span class="c"><!-- Formulaire de connexion --></span>
    <span class="nt"><div</span> <span class="na">id=</span><span class="s">"loginForm"</span> <span class="na">class=</span><span class="s">"login-form"</span><span class="nt">></span>
      <span class="nt"><h3></span>Connexion<span class="nt"></h3></span>
      <span class="nt"><input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"username"</span> <span class="na">placeholder=</span><span class="s">"Username"</span> <span class="na">value=</span><span class="s">"user"</span><span class="nt">></span>
      <span class="nt"><input</span> <span class="na">type=</span><span class="s">"password"</span> <span class="na">id=</span><span class="s">"password"</span> <span class="na">placeholder=</span><span class="s">"Password"</span> <span class="na">value=</span><span class="s">"any"</span><span class="nt">></span>
      <span class="nt"><button</span> <span class="na">onclick=</span><span class="s">"login()"</span><span class="nt">></span>Se connecter<span class="nt"></button></span>
      <span class="nt"><div</span> <span class="na">id=</span><span class="s">"loginError"</span> <span class="na">class=</span><span class="s">"error hidden"</span><span class="nt">></div></span>
    <span class="nt"></div></span>

    <span class="c"><!-- Informations utilisateur --></span>
    <span class="nt"><div</span> <span class="na">id=</span><span class="s">"userInfo"</span> <span class="na">class=</span><span class="s">"hidden user-info"</span><span class="nt">></span>
      <span class="nt"><h3></span>👤 Utilisateur connecté<span class="nt"></h3></span>
      <span class="nt"><p><strong></span>Username:<span class="nt"></strong></span> <span class="nt"><span</span> <span class="na">id=</span><span class="s">"userUsername"</span><span class="nt">></span></p></span>
      <span class="nt"><p><strong></span>Rôle:<span class="nt"></strong></span> <span class="nt"><span</span> <span class="na">id=</span><span class="s">"userRole"</span><span class="nt">></span></p></span>
      <span class="nt"><p><strong></span>User ID:<span class="nt"></strong></span> <span class="nt"><span</span> <span class="na">id=</span><span class="s">"userId"</span><span class="nt">></span></p></span>
      <span class="nt"><button</span> <span class="na">onclick=</span><span class="s">"logout()"</span> <span class="na">style=</span><span class="s">"margin-top: 10px;"</span><span class="nt">></span>Déconnexion<span class="nt"></button></span>
    <span class="nt"></div></span>

    <span class="c"><!-- Grille de sécurité --></span>
    <span class="nt"><div</span> <span class="na">id=</span><span class="s">"securityGrid"</span> <span class="na">class=</span><span class="s">"hidden security-grid"</span><span class="nt">></span>
      <span class="c"><!-- Permissions --></span>
      <span class="nt"><div</span> <span class="na">class=</span><span class="s">"security-card"</span><span class="nt">></span>
        <span class="nt"><h3></span>🔑 Permissions<span class="nt"></h3></span>
        <span class="nt"><div</span> <span class="na">id=</span><span class="s">"permissions"</span><span class="nt">></div></span>
      <span class="nt"></div></span>

      <span class="c"><!-- Rate Limit --></span>
      <span class="nt"><div</span> <span class="na">class=</span><span class="s">"security-card"</span><span class="nt">></span>
        <span class="nt"><h3></span>⏱️ Rate Limiting<span class="nt"></h3></span>
        <span class="nt"><div</span> <span class="na">id=</span><span class="s">"rateLimit"</span><span class="nt">></div></span>
      <span class="nt"></div></span>

      <span class="c"><!-- Quotas --></span>
      <span class="nt"><div</span> <span class="na">class=</span><span class="s">"security-card"</span><span class="nt">></span>
        <span class="nt"><h3></span>📊 Quotas<span class="nt"></h3></span>
        <span class="nt"><div</span> <span class="na">id=</span><span class="s">"quotas"</span><span class="nt">></div></span>
      <span class="nt"></div></span>
    <span class="nt"></div></span>
  <span class="nt"></div></span>

  <span class="nt"><script></span>
    <span class="kd">let</span> <span class="nx">token</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>

    <span class="k">async</span> <span class="kd">function</span> <span class="nx">login</span><span class="p">()</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">username</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">username</span><span class="dl">'</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
      <span class="kd">const</span> <span class="nx">password</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">password</span><span class="dl">'</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
      
      <span class="k">try</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">/auth/login</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
          <span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
          <span class="na">headers</span><span class="p">:</span> <span class="p">{</span> <span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span> <span class="p">},</span>
          <span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span> <span class="nx">username</span><span class="p">,</span> <span class="nx">password</span> <span class="p">})</span>
        <span class="p">});</span>

        <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>

        <span class="k">if</span> <span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">success</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">token</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">token</span><span class="p">;</span>
          <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">loginForm</span><span class="dl">'</span><span class="p">).</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">hidden</span><span class="dl">'</span><span class="p">);</span>
          <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">userInfo</span><span class="dl">'</span><span class="p">).</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">hidden</span><span class="dl">'</span><span class="p">);</span>
          <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">securityGrid</span><span class="dl">'</span><span class="p">).</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">hidden</span><span class="dl">'</span><span class="p">);</span>
          <span class="k">await</span> <span class="nx">loadSecurityData</span><span class="p">();</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
          <span class="nx">showError</span><span class="p">(</span><span class="dl">'</span><span class="s1">Identifiants invalides</span><span class="dl">'</span><span class="p">);</span>
        <span class="p">}</span>
      <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">showError</span><span class="p">(</span><span class="dl">'</span><span class="s1">Erreur de connexion</span><span class="dl">'</span><span class="p">);</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">function</span> <span class="nx">logout</span><span class="p">()</span> <span class="p">{</span>
      <span class="nx">token</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
      <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">loginForm</span><span class="dl">'</span><span class="p">).</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">hidden</span><span class="dl">'</span><span class="p">);</span>
      <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">userInfo</span><span class="dl">'</span><span class="p">).</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">hidden</span><span class="dl">'</span><span class="p">);</span>
      <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">securityGrid</span><span class="dl">'</span><span class="p">).</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">hidden</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kd">function</span> <span class="nx">showError</span><span class="p">(</span><span class="nx">message</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">errorDiv</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">loginError</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">errorDiv</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">message</span><span class="p">;</span>
      <span class="nx">errorDiv</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">hidden</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">setTimeout</span><span class="p">(()</span> <span class="o">=></span> <span class="nx">errorDiv</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">hidden</span><span class="dl">'</span><span class="p">),</span> <span class="mi">3000</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">async</span> <span class="kd">function</span> <span class="nx">loadSecurityData</span><span class="p">()</span> <span class="p">{</span>
      <span class="c1">// Charger les infos utilisateur</span>
      <span class="kd">const</span> <span class="nx">meResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/me</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
        <span class="na">headers</span><span class="p">:</span> <span class="p">{</span> <span class="dl">'</span><span class="s1">Authorization</span><span class="dl">'</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="nx">token</span><span class="p">}</span><span class="s2">`</span> <span class="p">}</span>
      <span class="p">});</span>
      <span class="kd">const</span> <span class="nx">me</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">meResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>

      <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">userUsername</span><span class="dl">'</span><span class="p">).</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">me</span><span class="p">.</span><span class="nx">username</span><span class="p">;</span>
      <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">userRole</span><span class="dl">'</span><span class="p">).</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">me</span><span class="p">.</span><span class="nx">role</span><span class="p">;</span>
      <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">userId</span><span class="dl">'</span><span class="p">).</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">me</span><span class="p">.</span><span class="nx">userId</span><span class="p">;</span>

      <span class="c1">// Afficher les permissions</span>
      <span class="kd">const</span> <span class="nx">permissionsDiv</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">permissions</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">permissionsDiv</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">me</span><span class="p">.</span><span class="nx">permissions</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">p</span> <span class="o">=></span> 
        <span class="s2">`<span class="permission-badge"></span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2"></span>`</span>
      <span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">''</span><span class="p">);</span>

      <span class="c1">// Afficher le rate limit</span>
      <span class="kd">const</span> <span class="nx">rateLimitDiv</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">rateLimit</span><span class="dl">'</span><span class="p">);</span>
      <span class="kd">const</span> <span class="nx">rateLimit</span> <span class="o">=</span> <span class="nx">me</span><span class="p">.</span><span class="nx">rateLimit</span><span class="p">;</span>
      <span class="kd">const</span> <span class="nx">percentage</span> <span class="o">=</span> <span class="p">(</span><span class="nx">rateLimit</span><span class="p">.</span><span class="nx">current</span> <span class="o">/</span> <span class="nx">rateLimit</span><span class="p">.</span><span class="nx">max</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">;</span>
      
      <span class="nx">rateLimitDiv</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s2">`
        <p><strong></span><span class="p">${</span><span class="nx">rateLimit</span><span class="p">.</span><span class="nx">current</span><span class="p">}</span><span class="s2"></strong> / </span><span class="p">${</span><span class="nx">rateLimit</span><span class="p">.</span><span class="nx">max</span><span class="p">}</span><span class="s2"> requêtes</p>
        <div class="quota-bar">
          <div class="quota-fill" style="width: </span><span class="p">${</span><span class="nx">percentage</span><span class="p">}</span><span class="s2">%"></div>
        </div>
        <p style="font-size: 0.9em; color: #64748b;">
          Reset: </span><span class="p">${</span><span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="nx">rateLimit</span><span class="p">.</span><span class="nx">resetsAt</span><span class="p">).</span><span class="nx">toLocaleTimeString</span><span class="p">()}</span><span class="s2">
        </p>
      `</span><span class="p">;</span>

      <span class="c1">// Charger les quotas</span>
      <span class="kd">const</span> <span class="nx">quotasResponse</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">/mcp/quotas</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
        <span class="na">headers</span><span class="p">:</span> <span class="p">{</span> <span class="dl">'</span><span class="s1">Authorization</span><span class="dl">'</span><span class="p">:</span> <span class="s2">`Bearer </span><span class="p">${</span><span class="nx">token</span><span class="p">}</span><span class="s2">`</span> <span class="p">}</span>
      <span class="p">});</span>
      <span class="kd">const</span> <span class="nx">quotasData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">quotasResponse</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>

      <span class="kd">const</span> <span class="nx">quotasDiv</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">quotas</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">quotasDiv</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">quotasData</span><span class="p">.</span><span class="nx">quotas</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">q</span> <span class="o">=></span> <span class="s2">`
        <p><strong></span><span class="p">${</span><span class="nx">q</span><span class="p">.</span><span class="nx">tool</span><span class="p">}</span><span class="s2">:</strong> </span><span class="p">${</span><span class="nx">q</span><span class="p">.</span><span class="nx">remaining</span> <span class="o">===</span> <span class="kc">null</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">Illimité</span><span class="dl">'</span> <span class="p">:</span> <span class="nx">q</span><span class="p">.</span><span class="nx">remaining</span><span class="p">}</span><span class="s2"></p>
      `</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">''</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="nt"></script></span>
<span class="nt"></body></span>
<span class="nt"></html></span>

Ouvrez http://localhost:3000/security.html pour voir le dashboard en action !

Bonnes Pratiques de Sécurité

Maintenant que vous avez un système complet, voici les bonnes pratiques à suivre :

1. Gestion des Secrets

Ne jamais commit les secrets dans le code. Utilisez des variables d’environnement :

<span class="c1">// Créez un fichier .env</span>
<span class="nx">SECRET_KEY</span><span class="o">=</span><span class="nx">your</span><span class="o">-</span><span class="k">super</span><span class="o">-</span><span class="nx">secret</span><span class="o">-</span><span class="nx">key</span><span class="o">-</span><span class="nx">change</span><span class="o">-</span><span class="nx">me</span><span class="o">-</span><span class="k">in</span><span class="o">-</span><span class="nx">production</span>
<span class="nx">JWT_EXPIRATION</span><span class="o">=</span><span class="mi">86400</span>

<span class="c1">// Dans votre code</span>
<span class="k">import</span> <span class="nx">dotenv</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">dotenv</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">dotenv</span><span class="p">.</span><span class="nx">config</span><span class="p">();</span>

<span class="kd">const</span> <span class="nx">SECRET_KEY</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">SECRET_KEY</span><span class="p">;</span>

2. Hash des Mots de Passe

En production, utilisez bcrypt pour hasher les mots de passe :

npm <span class="nb">install </span>bcrypt @types/bcrypt

<span class="k">import</span> <span class="nx">bcrypt</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">bcrypt</span><span class="dl">'</span><span class="p">;</span>

<span class="c1">// Lors de la création d'un utilisateur</span>
<span class="kd">const</span> <span class="nx">hashedPassword</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">bcrypt</span><span class="p">.</span><span class="nx">hash</span><span class="p">(</span><span class="nx">password</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>

<span class="c1">// Lors de l'authentification</span>
<span class="kd">const</span> <span class="nx">isValid</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">bcrypt</span><span class="p">.</span><span class="nx">compare</span><span class="p">(</span><span class="nx">password</span><span class="p">,</span> <span class="nx">user</span><span class="p">.</span><span class="nx">hashedPassword</span><span class="p">);</span>

3. HTTPS Obligatoire

En production, forcez HTTPS :

<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">((</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">header</span><span class="p">(</span><span class="dl">'</span><span class="s1">x-forwarded-proto</span><span class="dl">'</span><span class="p">)</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">https</span><span class="dl">'</span> <span class="o">&&</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NODE_ENV</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">production</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">res</span><span class="p">.</span><span class="nx">redirect</span><span class="p">(</span><span class="s2">`https://</span><span class="p">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">header</span><span class="p">(</span><span class="dl">'</span><span class="s1">host</span><span class="dl">'</span><span class="p">)}${</span><span class="nx">req</span><span class="p">.</span><span class="nx">url</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">next</span><span class="p">();</span>
  <span class="p">}</span>
<span class="p">});</span>

4. Logging de Sécurité

Logguez tous les événements de sécurité :

<span class="kd">function</span> <span class="nx">logSecurityEvent</span><span class="p">(</span><span class="nx">event</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">details</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// En production : envoyer vers un service d'audit</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span>
    <span class="na">timestamp</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">(),</span>
    <span class="na">event</span><span class="p">:</span> <span class="nx">event</span><span class="p">,</span>
    <span class="p">...</span><span class="nx">details</span>
  <span class="p">}));</span>
<span class="p">}</span>

<span class="c1">// Utilisation</span>
<span class="nx">logSecurityEvent</span><span class="p">(</span><span class="dl">'</span><span class="s1">AUTH_FAILED</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="nx">username</span><span class="p">,</span> <span class="na">ip</span><span class="p">:</span> <span class="nx">req</span><span class="p">.</span><span class="nx">ip</span> <span class="p">});</span>
<span class="nx">logSecurityEvent</span><span class="p">(</span><span class="dl">'</span><span class="s1">PERMISSION_DENIED</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">user</span><span class="p">:</span> <span class="nx">req</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">username</span><span class="p">,</span> <span class="nx">tool</span> <span class="p">});</span>

5. Audits Réguliers

Créez un système d’audit :

<span class="kd">class</span> <span class="nx">AuditLogger</span> <span class="p">{</span>
  <span class="nx">log</span><span class="p">(</span><span class="nx">action</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">userId</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">details</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// En production : envoyer vers un service d'audit</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">({</span>
      <span class="na">timestamp</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">(),</span>
      <span class="nx">action</span><span class="p">,</span>
      <span class="nx">userId</span><span class="p">,</span>
      <span class="p">...</span><span class="nx">details</span>
    <span class="p">});</span>
  <span class="p">}</span>
<span class="p">}</span>

Conclusion

Félicitations ! Vous avez maintenant un serveur MCP production-ready avec quatre couches de sécurité :

  • ✅ Validation complète des entrées
  • ✅ Authentification par tokens
  • ✅ Autorisation granulaire
  • ✅ Rate limiting et quotas

Votre serveur peut maintenant être exposé en production en toute confiance. Les IA peuvent l’utiliser de manière sécurisée, chaque utilisateur a ses permissions spécifiques, et les abus sont automatiquement bloqués.

Dans le prochain et dernier article de la série, nous allons connecter votre serveur sécurisé à Claude Desktop et tester l’intégration complète en conditions réelles. Vous verrez enfin tout le système fonctionner de bout en bout avec une véritable IA.

En attendant, testez votre système de sécurité ! Essayez de le contourner, testez les limites, vérifiez que tout est bien protégé. Un bon système de sécurité est un système qui a été attaqué et qui a résisté.


Article publié le 10 décembre 2025 par Nicolas Dabène - Expert PHP & PrestaShop avec 15+ ans d’expérience dans l’architecture logicielle et l’intégration d’IA

À lire aussi :

LinkedIn

Suivez mes analyses IA et e-commerce

Je partage des retours terrain sur les agents IA, PrestaShop, MCP et l automatisation pour les equipes e-commerce.

Me suivre sur LinkedIn