Leçon 14 / 20

Protéger les routes et les actions

15 min Node.js — production, files d’attente et sécurité Gratuit

Objectif : Appliquer une authentification et une autorisation adaptées aux routes de commandes et d’export.

Une route peut être techniquement accessible tout en étant réservée à certains utilisateurs. Sans contrôle explicite, une personne authentifiée peut aussi obtenir des droits trop larges, par exemple lancer un export alors qu’elle ne devrait que consulter des commandes. Vous allez placer des contrôles distincts avant les traitements sensibles.

Dans la leçon précédente, vous avez comparé les sessions et les jetons d’accès ; l’exemple ci-dessous retient un jeton transmis dans l’en-tête Authorization. Le serveur ne déduit pas lui-même l’identité du jeton : un vérificateur injecté contrôle celui-ci auprès du mécanisme retenu par votre service.

Créer les contrôles d’accès

Le middleware d’authentification extrait le jeton, demande sa vérification, puis place l’identité obtenue sur la requête Express. Il renvoie 401 si l’identité ne peut pas être établie. Le contrôle d’autorisation intervient ensuite : il vérifie les permissions nécessaires et renvoie 403 lorsque l’appelant est identifié, mais n’a pas le droit demandé.

src/middlewares/authentification.jsTexte
function creerAuthentification({ verifierJeton }) {
  if (typeof verifierJeton !== 'function') {
    throw new TypeError('verifierJeton doit être une fonction');
  }

  return async function authentifier(req, res, next) {
    const autorisation = req.get('authorization') || '';
    const correspondance = autorisation.match(/^Bearer\s+(.+)$/i);

    if (!correspondance) {
      return res.status(401).json({ erreur: 'Authentification requise' });
    }

    try {
      const utilisateur = await verifierJeton(correspondance[1]);

      if (!utilisateur || !Array.isArray(utilisateur.permissions)) {
        return res.status(401).json({ erreur: 'Jeton invalide' });
      }

      req.utilisateur = utilisateur;
      return next();
    } catch (erreur) {
      return next(erreur);
    }
  };
}

function exigerPermission(permission) {
  return function autoriser(req, res, next) {
    const permissions = req.utilisateur && req.utilisateur.permissions;

    if (!Array.isArray(permissions) || !permissions.includes(permission)) {
      return res.status(403).json({ erreur: 'Accès interdit' });
    }

    return next();
  };
}

module.exports = { creerAuthentification, exigerPermission };

Le vérificateur doit renvoyer un utilisateur et ses permissions, par exemple { id: 'u-42', permissions: ['commandes:lire'] }, ou une valeur vide si le jeton n’est pas valable. Dans une application Express, construisez ces contrôles au démarrage avec les dépendances disponibles, puis placez-les avant le gestionnaire de la route ; vous n’avez pas à réécrire le service d’export déjà créé.

Montage des middlewares dans un routeur ExpressTexte
const express = require('express');
const {
  creerAuthentification,
  exigerPermission
} = require('../middlewares/authentification');

function creerRouteurCommandes({ verifierJeton, lireCommandes, routeExport, routeAdministration }) {
  const routeur = express.Router();
  const authentifier = creerAuthentification({ verifierJeton });

  routeur.get(
    '/commandes',
    authentifier,
    exigerPermission('commandes:lire'),
    lireCommandes
  );

  routeur.get(
    '/commandes/export',
    authentifier,
    exigerPermission('commandes:exporter'),
    routeExport
  );

  routeur.post(
    '/administration/commandes',
    authentifier,
    exigerPermission('commandes:administrer'),
    routeAdministration
  );

  return routeur;
}

module.exports = { creerRouteurCommandes };

Ce routeur reçoit ses gestionnaires et son vérificateur par injection : il ne dépend ni d’un dépôt concret ni d’un secret inscrit dans le fichier. Les contrôles sont appliqués à chaque route protégée, et les permissions décrivent les actions plutôt que de donner automatiquement accès à toutes les fonctionnalités à un rôle général. Dans votre route d’export existante, passez le gestionnaire déjà construit comme routeExport.

Vérifier les réponses et les permissions

Vérifiez au minimum trois cas pour chaque route protégée : l’absence de jeton doit produire 401, un jeton invalide doit aussi produire 401, et un utilisateur authentifié sans permission doit recevoir 403. Un utilisateur possédant la permission appropriée doit atteindre le gestionnaire ; vérifiez en particulier que la permission de consulter les commandes ne donne pas implicitement le droit d’exporter ou d’administrer.

À retenir

  • Authentifiez l’appelant avant d’évaluer ses droits.
  • Utilisez 401 pour une identité absente ou invalide, et 403 pour une action interdite.
  • Attribuez une permission distincte aux commandes, aux exports et aux actions d’administration.
  • Injectez le vérificateur et les gestionnaires au lieu de lier les contrôles à des dépendances concrètes.

La prochaine leçon porte sur la validation des entrées et la protection des secrets utilisés par le service.

Envie d'aller plus loin ?

Les cours Enovatic sont gratuits et le resteront. Si vous cherchez un accompagnement encadré, nos formations professionnelles sont animées par les mêmes formateurs.