Node

JsonWebToken

Utiliser les tokens d'authentification avec NodeJS

Structure

Les tokens d'authentification sont des jetons qui sont obtenus lors de la connexion d'un utilisateur. Ce dernier reçoit une clé cryptée qui contient des informations permettant de l'identifier. Ces jetons sont souvents stockés dans le LocalStorage ou les Cookies sur les navigateurs.

À chaque fois que l'utilisateur tentera de se rendre sur des ressources sécuriséesle serveur vérifiera la validé du token. Si le token est correct et correspond bien à l'utilisateur alors il autorisera l'accès à la ressource sinon il le rejettera (erreur 401 Unauthorized).

Les tokens sont donc très pratique car ils évitent à un utilisateur de devoir se reconnecter systmétiquement à chaque requête au serveur. D'autant qu'on peut leur donner une durée de vie ce qui permettra d'éviter la reconnexion pendant ce laps de temps.

Pour utiliser les tokens avec nodeJS nous utilisons ce que l'on appelle les jsonwebtoken ou JWT. Il s'agit d'un format standard défini ici. Sa structure se décompose en 3 parties:

  • HEADER: en-tête qui définit le type de token ainsi que son algorithme d'encryptage de signature. C'est un objet JSON.
  • PAYLOAD: qui possède les data que l'on souhaite stocker dans le JWT, comme l'id utilisateur, son rôle, (...). C'est un objet JSON.
  • SIGNATURE : Une signature numérique qui permet le chiffrement et le déchiffrement de notre JWT. On l'obtient en chiffrant le HEADER et le PAYLOAD avec l'encodage base64url. Ensuite, on les concatène en les séparant par un point. On obtient la signature de ce résultat avec l'algorithme choisi. Cette signature est ajoutée au résultat de la même manière (encodée et séparée par un point). Généralement on rajoute à cela une clé de chiffrement définie par nos soins. Le chiffrement est capital car il permet de vérifier l'intégrité du token.

JWT Structure

Si vous définissez une clé de chiffrement dans votre projet veillez à bien la définir dans vos variables d'environnement pour que personne ne puisse y accéder depuis le cloud, cela serait catastrophique car on pourrait pirater votre application.

Créer un JWT

Pour utiliser ce standard nous utiliserons le package jsonwebtoken. Le jsonwebtoken sera ensuite appelé dans l'endpoint de connexion (généralement l'endpoint /auth) grâce à sa fonction sign qui sert à chiffrer un nouveau token.

npm install jsonwebtoken
npm install -D @types/jsonwebtoken

Prenons l'exemple de la fonction qui réalise plusieurs opérations et finit par renvoyer l'utilisateur renseigné dans la requête. En plus des données de l'utilisateurs on va chercher à renvoyer dans la réponse un JWT.

import { Request, Response } from 'express';
import jwt from 'jsonwebtoken';

export const login = async (req: Request, res: Response) => {
  const { username, password } = req.body;

  if (username !== 'admin' || password !== 'password') {
    res.status(401).send('Identifiants invalides');
    return;
  }
  const token = jwt.sign(
    { username }, // Payload
    process.env.JWT_SECRET as jwt.Secret, // Secret
    { expiresIn: process.env.JWT_EXPIRES_IN } // Expiration
  );

  res.status(200).json({
    token,
  });
};

Les variables d'environnement JWT_SECRET et JWT_EXPIRES_IN doivent être définies dans un fichier .env à la racine de votre projet.

Vérifier un JWT

Votre réponse de connexion à renvoyé un JWT. Votre application ou vous-même allez désormais stocker ce JWT et l'utiliser dans vos prochaines requêtes afin de s'assurer que les endpoints nécessitant une connexion aient connaissance de votre token et vous délivre l'accès à leurs fonctions.

Afin qu'un endpoint cloisonnent ses accès uniquement à un utilisateur possédant un JWT valide nous allons créer un middleware d'authentification qui s'éxecutera avant les controlleurs.

Pour commencer il est de coutume d'envoyer le jwt dans l'entête de votre requête et plus spécifiquement dans la clé authorization. On va donc récupérer le token contenu dans cette clé puis le vérifier à l'aide la fonction verify qui prendra en paramètre le JWT et la clé de chiffrement secrète 'SECRET_TOKEN.

import { NextFunction, Response, Request } from 'express';
import jwt from 'jsonwebtoken';

export const verifyJWT = (req: Request, res: Response, next: NextFunction) => {
  const authHeader = req.headers.authorization;
  if (authHeader) {
    const token = authHeader.split(' ')[1]; // On récupère le token
    const decodedToken = jwt.verify(
      token,
      process.env.JWT_SECRET as jwt.Secret
    ) as {
      userId: string;
    };
    const userId = decodedToken.userId;
    req.query = {
      userId: userId,
    };
    next();
  } else {
    res.sendStatus(401); // Pas de token fourni
  }
};

Point securité

  • Ne pas stocker de JWT sensibles dans localStorage : Préférez les cookies sécurisés et HTTP-only.
  • Configurer une durée de vie appropriée : Les tokens doivent expirer rapidement (exp).
  • Utiliser un algorithme de signature robuste : Préférer RS256 ou ES256 (asymétrique) à HS256.
  • Vérifier toujours la signature et les claims : Assurez-vous que le token est valide et les informations dans le payload sont autorisées.