> ## Documentation Index
> Fetch the complete documentation index at: https://translations.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Protégez votre API Express.js

> Ce guide explique comment protéger les points de terminaison d’une API Express.js à l’aide de jetons d’accès JWT et du SDK @auth0/auth0-express-api (bêta).

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
              processedNode = processedNode.replaceAll(new RegExp(escapedKey, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****MASQUÉ*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

export const HowToSchema = () => <script type="application/ld+json">
    {'{"@context":"https://schema.org","@type":"HowTo"}'}
  </script>;

<HowToSchema />

export const envSnippet = `AUTH0_DOMAIN={yourDomain}
AUTH0_AUDIENCE=YOUR_API_IDENTIFIER`;

export const envSnippetDashboard = `AUTH0_DOMAIN=YOUR_AUTH0_DOMAIN
AUTH0_AUDIENCE=YOUR_API_IDENTIFIER`;

<Warning>
  Ce Quickstart est actuellement en **bêta**. Nous aimerions connaître vos commentaires!
</Warning>

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  **Prérequis :** Avant de commencer, assurez-vous d’avoir installé les éléments suivants :

  * [Node.js](https://nodejs.org/) 22 LTS ou une version ultérieure
  * [npm](https://www.npmjs.com/) 10+ ou [yarn](https://yarnpkg.com/) 1.22+
</Callout>

<div id="get-started">
  ## Premiers pas
</div>

Ce Quickstart explique comment protéger des point de terminaison d’API Express.js à l’aide de jetons d’accès JWT. Vous créerez une API sécurisée qui valide les jetons d’accès Auth0, protège les routes et implémente une autorisation fondée sur les scopes et les claims.

<Steps>
  <Step title="Créer un nouveau projet" stepNumber={1}>
    Créez un nouveau répertoire pour votre API Express et initialisez un projet Node.js.

    <AuthCodeGroup>
      ```shellscript Mac theme={null}
      mkdir auth0-express-api && cd auth0-express-api
      npm init -y
      touch server.js .env
      ```

      ```shellscript Windows theme={null}
      mkdir auth0-express-api; cd auth0-express-api
      npm init -y
      New-Item server.js, .env
      ```
    </AuthCodeGroup>

    Mettez à jour votre fichier `package.json` pour utiliser les modules ES et ajoutez des scripts de démarrage :

    ```json theme={null}
    {
      "name": "auth0-express-api",
      "version": "1.0.0",
      "type": "module",
      "main": "server.js",
      "scripts": {
        "start": "node server.js",
        "dev": "node --watch server.js"
      }
    }
    ```
  </Step>

  <Step title="Installer le SDK" stepNumber={2}>
    Installez `@auth0/auth0-express-api`, ainsi que `express` et `dotenv` :

    ```shell theme={null}
    npm install @auth0/auth0-express-api@beta express dotenv
    ```
  </Step>

  <Step title="Configurez votre API Auth0" stepNumber={3}>
    Vous devez créer une API dans votre tenant Auth0 et configurer vos variables d’environnement.

    <Tabs>
      <Tab title="CLI">
        Exécutez la commande suivante à la racine de votre projet pour créer une API Auth0 :

        <AuthCodeGroup>
          ```shellscript Mac theme={null}
          # Installer Auth0 CLI (s'il n'est pas déjà installé)
          brew tap auth0/auth0-cli && brew install auth0

          # Créer une API Auth0
          auth0 apis create \
            --name "My Express API" \
            --identifier https://my-express-api.example.com
          ```

          ```powershell Windows theme={null}
          # Installer Auth0 CLI (s'il n'est pas déjà installé)
          scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
          scoop install auth0

          # Créer une API Auth0
          auth0 apis create `
            --name "My Express API" `
            --identifier https://my-express-api.example.com
          ```
        </AuthCodeGroup>

        Une fois l’API créée, copiez les valeurs **Identifier** et **Domain**, puis créez votre fichier `.env` :

        <AuthCodeBlock children={envSnippet} language="shellscript" filename=".env" />

        Remplacez `YOUR_API_IDENTIFIER` par l’identifiant utilisé ci-dessus (par exemple, `https://my-express-api.example.com`).
      </Tab>

      <Tab title="Dashboard">
        1. Accédez à [Auth0 Dashboard](https://manage.auth0.com/) → **Applications > APIs** → **Create API**
        2. Entrez un nom (par exemple, "My Express API")
        3. Définissez l’**Identifier** : il s’agit de l’audience de votre API (par exemple, `https://my-express-api.example.com`). Il n’a pas besoin d’être une véritable URL.
        4. Laissez **Signing Algorithm** à **RS256**
        5. Cliquez sur **Create**
        6. Copiez le **Domain** de votre tenant et l’**Identifier** dans **API Settings**

        Créez votre fichier `.env` :

        <AuthCodeBlock children={envSnippetDashboard} language="shellscript" filename=".env" />

        <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
          Remplacez `YOUR_AUTH0_DOMAIN` par le domaine de votre tenant Auth0 (par exemple, `dev-abc123.us.auth0.com`) et `YOUR_API_IDENTIFIER` par l’identifiant de l’API défini ci-dessus.
        </Callout>
      </Tab>
    </Tabs>
  </Step>

  <Step title="Configurer le middleware JWT" stepNumber={4}>
    Enregistrez `createAuth0Api()` dans votre application Express pour configurer la validation des JWT. Ajoutez ensuite des routes publiques et protégées.

    ```javascript server.js theme={null}
    import 'dotenv/config';
    import express from 'express';
    import { createAuth0Api, requiresAuth } from '@auth0/auth0-express-api';

    const app = express();
    const port = process.env.PORT || 3001;

    app.use(express.json());
    app.use(createAuth0Api());

    // Route publique — aucun token requis
    app.get('/api/public', (req, res) => {
      res.json({
        message: 'Hello from a public endpoint! No authentication required.',
        timestamp: new Date().toISOString(),
      });
    });

    // Route protégée — requiert un access token valide
    app.get('/api/private', requiresAuth(), (req, res) => {
      res.json({
        message: 'Hello from a protected endpoint! You are authenticated.',
        sub: req.auth0.user?.sub,
        timestamp: new Date().toISOString(),
      });
    });

    app.listen(port, () => {
      console.log(`API server running at http://localhost:${port}`);
    });
    ```

    **Fonctionnement :**

    * `createAuth0Api()` lit automatiquement `AUTH0_DOMAIN` et `AUTH0_AUDIENCE` depuis les variables d’environnement
    * `requiresAuth()` valide l’en-tête `Authorization: Bearer <token>` de chaque requête
    * `req.auth0.user` contient les claims JWT décodés des requêtes authentifiées — `sub` est l’identifiant unique de l’utilisateur
  </Step>

  <Step title="Protéger une route avec un scope requis" stepNumber={5}>
    En plus d’exiger un token valide, vous pouvez exiger un scope précis. Passez l’option `scopes` à `requiresAuth()` : le SDK renvoie `403 insufficient_scope` si le token ne comprend pas ce scope.

    ```javascript server.js theme={null}
    // Requiert le scope « read:messages »
    app.get('/api/messages', requiresAuth({ scopes: ['read:messages'] }), (req, res) => {
      res.json({ messages: ['Hello!', 'World!'] });
    });
    ```

    <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
      Définissez la portée dans l’onglet **Permissions** de votre API (consultez [Utilisation avancée](#advanced-usage)) et demandez-la lors de l’obtention du jeton d’accès. Pour faire correspondre plusieurs portées ou autoriser en fonction de revendications personnalisées, le SDK offre également `scopesInclude`, `claimEquals`, `claimIncludes` et `claimCheck` — décrits dans [Utilisation avancée](#advanced-usage).
    </Callout>
  </Step>

  <Step title="Exécutez votre API" stepNumber={6}>
    Démarrez le serveur de développement :

    ```shell theme={null}
    npm run dev
    ```

    Votre API est maintenant accessible à l’adresse [http://localhost:3001](http://localhost:3001).
  </Step>

  <Step title="Testez votre API" stepNumber={7}>
    Testez le endpoint public (aucun token requis) :

    ```shell theme={null}
    curl http://localhost:3001/api/public
    ```

    Réponse attendue :

    ```json theme={null}
    {
      "message": "Hello from a public endpoint! No authentication required.",
      "timestamp": "2026-06-22T12:00:00.000Z"
    }
    ```

    Pour envoyer une requête à l’endpoint protégé, vous avez besoin d’un jeton d’accès :

    1. Accédez à [Auth0 Dashboard](https://manage.auth0.com/) → **Applications > API**
    2. Sélectionnez votre API → onglet **Test**
    3. Copiez le jeton d’accès généré

    Testez l’endpoint protégé :

    ```shell theme={null}
    curl http://localhost:3001/api/private \
      -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
    ```

    Réponse attendue :

    ```json theme={null}
    {
      "message": "Hello from a protected endpoint! You are authenticated.",
      "sub": "auth0|abc123...",
      "timestamp": "2026-06-22T12:00:00.000Z"
    }
    ```

    <Check>
      **Point de contrôle**

      Vous devriez maintenant disposer d’une API protégée. Votre API :

      1. Accepte les requêtes vers des endpoints publics sans token
      2. Renvoie la réponse protégée lorsqu’un access token valide est fourni
      3. Valide les JWT par rapport à votre domaine Auth0 et à votre audience
      4. Expose les claims décodés du token via `req.auth0.user`
    </Check>
  </Step>
</Steps>

***

<div id="advanced-usage">
  ## Utilisation avancée
</div>

<AccordionGroup>
  <Accordion title="Faire correspondre plusieurs scopes avec scopesInclude">
    Utilisez `scopesInclude` lorsqu'une route doit accepter l'un de plusieurs scopes ou en exiger plusieurs à la fois. Par défaut, cette fonction vérifie la présence d'**au moins un** des scopes indiqués; transmettez `{ match: 'all' }` pour les exiger tous. Les scopes peuvent être fournis sous forme de tableau ou de chaîne séparée par des espaces — ces exemples utilisent des tableaux.

    ```javascript server.js theme={null}
    import { scopesInclude } from '@auth0/auth0-express-api';

    // Nécessite L'UN de ces scopes
    app.get('/api/feed', requiresAuth(), scopesInclude(['read:feed', 'read:admin']), (req, res) => {
      res.json({ feed: [] });
    });

    // Nécessite TOUS ces scopes
    app.get('/api/admin/edit', requiresAuth(), scopesInclude(['read:admin', 'write:admin'], { match: 'all' }), (req, res) => {
      res.json({ message: 'Admin editor access granted.' });
    });
    ```
  </Accordion>

  <Accordion title="Autoriser selon des claims personnalisés">
    Lorsque l'autorisation dépend de claims autres que `scope`, utilisez `claimEquals`, `claimIncludes` ou `claimCheck`. Ces fonctions s'exécutent après `requiresAuth()` et renvoient `401 invalid_token` si l'exigence relative au claim n'est pas satisfaite.

    ```javascript server.js theme={null}
    import { claimEquals, claimIncludes, claimCheck } from '@auth0/auth0-express-api';

    // claimEquals — le claim doit correspondre exactement à une valeur
    app.get('/api/admin', requiresAuth(), claimEquals('isAdmin', true), (req, res) => {
      res.json({ message: 'Admin access granted.' });
    });

    // claimIncludes — le claim sous forme de tableau doit contenir toutes les valeurs indiquées
    app.get('/api/editor', requiresAuth(), claimIncludes('roles', ['admin', 'editor']), (req, res) => {
      res.json({ message: 'Editor access granted.' });
    });

    // claimCheck — logique personnalisée appliquée au jeton décodé
    app.get('/api/premium', requiresAuth(), claimCheck(
      (req, token) => token.tier === 'premium' || token.roles?.includes('admin'),
      { errorMessage: 'Premium tier or admin role required' }
    ), (req, res) => {
      res.json({ message: 'Premium content access granted.' });
    });
    ```
  </Accordion>

  <Accordion title="Définir des claims de jeton personnalisés avec TypeScript">
    Si vous utilisez TypeScript, étendez l'interface `Token` pour accéder aux claims personnalisés de façon sécuritaire du point de vue des types :

    ```typescript server.ts theme={null}
    import '@auth0/auth0-express-api';

    declare module '@auth0/auth0-express-api' {
      interface Token {
        tier: 'free' | 'premium';
        roles: string[];
        'https://myapp.com/org_id': string;
      }
    }
    ```

    Installez la prise en charge des types :

    ```shell theme={null}
    npm install -D typescript @types/express @types/node
    ```
  </Accordion>

  <Accordion title="Configuration CORS pour les clients Web">
    Activez CORS pour permettre à votre application Web d'appeler l'API :

    ```shell theme={null}
    npm install cors
    ```

    ```javascript server.js theme={null}
    import cors from 'cors';

    app.use(cors({
      origin: ['http://localhost:3000', 'http://localhost:5173'],
      allowedHeaders: ['Authorization', 'Content-Type'],
      exposedHeaders: ['WWW-Authenticate'],
    }));

    app.use(createAuth0Api());
    ```

    En production, indiquez les origines autorisées exactes plutôt que d'utiliser des caractères génériques.
  </Accordion>

  <Accordion title="Configurer les scopes dans Auth0 Dashboard">
    Pour utiliser l'autorisation basée sur les scopes, commencez par définir les permissions de votre API :

    1. Accédez à [Auth0 Dashboard](https://manage.auth0.com/) → **Applications > APIs** → votre API
    2. Ouvrez l'onglet **Permissions**
    3. Ajoutez des permissions telles que `read:messages`, `write:messages`, `read:admin`
    4. Cliquez sur **Save**

    Votre application cliente doit ensuite demander ces scopes lors de l'obtention d'un jeton d'accès. Si un jeton ne contient pas le scope requis, l'API renvoie `403 Forbidden`.
  </Accordion>
</AccordionGroup>

***

<div id="troubleshooting">
  ## Dépannage
</div>

<AccordionGroup>
  <Accordion title="401 avec un corps de réponse vide et seulement l’en-tête « WWW-Authenticate: Bearer »">
    **Cause :** L’en-tête `Authorization` est absent ou mal formé, de sorte qu’aucun jeton porteur n’a pu être extrait. Conformément à la [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750#section-3), le SDK renvoie dans ce cas un `401` avec seulement l’en-tête `WWW-Authenticate: Bearer` et aucun corps d’erreur. Ce cas est distinct de celui où un jeton est *présent, mais non valide ou expiré*, qui renvoie un `401` avec l’erreur `invalid_token` et un corps JSON (voir ci-dessous).

    **Correctif :**

    1. Assurez-vous que l’en-tête est présent : `Authorization: Bearer YOUR_TOKEN`
    2. Vérifiez que « Bearer » (avec un B majuscule et une espace) précède le jeton
  </Accordion>

  <Accordion title="« Invalid token » ou non-concordance entre l’audience et l’émetteur (401)">
    **Cause :** Le jeton n’a pas été émis pour cette API, ou les valeurs de domaine et d’audience ne correspondent pas.

    **Correctif :**

    1. Décodez votre jeton sur [jwt.io](https://jwt.io)
    2. Vérifiez que `iss` correspond à `https://{yourDomain}/` (notez la barre oblique finale)
    3. Vérifiez que `aud` correspond exactement à votre `AUTH0_AUDIENCE`
    4. Assurez-vous d’utiliser un **jeton d’accès**, et non un jeton ID — les jetons d’accès sont obtenus avec le paramètre `audience`
  </Accordion>

  <Accordion title="« Insufficient scope » (403)">
    **Cause :** Le jeton n’inclut pas le scope requis.

    **Correctif :**

    1. Vérifiez que le scope est défini dans l’onglet Permissions de votre API dans l’Auth0 Dashboard
    2. Assurez-vous que le client demande le scope lors de l’obtention du jeton d’accès
    3. Décodez le jeton sur [jwt.io](https://jwt.io) et vérifiez le claim `scope`
  </Accordion>

  <Accordion title="Variables d’environnement non chargées">
    **Cause :** `dotenv` n’est pas configuré ou les noms de variables sont incorrects.

    **Correctif :**

    1. Assurez-vous que `import 'dotenv/config'` est la première instruction d’importation dans votre fichier d’entrée
    2. Vérifiez que `.env` contient `AUTH0_DOMAIN` et `AUTH0_AUDIENCE`
    3. Déboguez :

    ```javascript theme={null}
    console.log({
      domain: !!process.env.AUTH0_DOMAIN,
      audience: !!process.env.AUTH0_AUDIENCE,
    });
    ```
  </Accordion>

  <Accordion title="Erreurs d’importation ESM (« Cannot use import statement »)">
    **Cause :** Le SDK `@auth0/auth0-express-api` utilise des modules ES.

    **Correctif :** Ajoutez `"type": "module"` à votre `package.json` :

    📁 **package.json**

    ```json theme={null}
    {
      "type": "module"
    }
    ```

    Ou renommez le fichier de votre serveur en `server.mjs`.
  </Accordion>
</AccordionGroup>

***

<div id="next-steps">
  ## Étapes suivantes
</div>

* **[Ajouter Login à une application web Express](/docs/fr-ca/quickstart/webapp/express-beta)** — Utilisez `@auth0/auth0-express` pour l’authentification par session dans les applications web
* **[Contrôle d’accès basé sur les rôles](https://auth0.com/docs/manage-users/access-control/rbac)** — Mettez en œuvre des permissions granulaires
* **[Bonnes pratiques relatives aux jetons d’accès](https://auth0.com/docs/secure/tokens/access-tokens)** — Découvrez comment gérer les jetons d’accès
* **[Surveiller votre API](https://auth0.com/docs/deploy-monitor/logs)** — Configurez la journalisation et la surveillance

***

<div id="resources">
  ## Ressources
</div>

* **[auth0/auth0-express-api GitHub](https://github.com/auth0/auth0-express/tree/main/packages/auth0-express-api)** — Code source et exemples
* **[Auth0 Community](https://community.auth0.com/)** — Obtenez de l’aide auprès de la communauté
* **[JWT.io](https://jwt.io/)** — Déboguez et décodez les JWTs
