Archives Wild Code School
Express.js avancé

Express 08 - đŸ—ïž Authentification avec JWT

QuĂȘte 2 sur 2 · 4 images archivĂ©es

⚠ Avant de commencer cette quĂȘte, on te recommande de jeter un coup d'Ɠil Ă  celle-ci :
QuĂȘtes liĂ©es

Dans les quĂȘtes prĂ©cĂ©dentes, nous avons vu comment crĂ©er des utilisateurs et des films.

De plus, nous avons appris à attribuer un mot de passe haché à un utilisateur.

Mais comment faire si nous voulons limiter des fonctionnalités aux utilisateurs authentifiés uniquement ? (par exemple, sur les réseaux sociaux, tu dois te connecter pour pouvoir publier quelque chose). Nous avons besoin d'un moyen pour que notre application nous reconnaisse.

C'est parti 🚀

đŸ€“ Dans cette quĂȘte, tu vas apprendre :

✅ Comment mettre en place un flux d'authentification ✅ Qu'est-ce qu'un JWT ✅ Comment crĂ©er et utiliser des JWT

🚀 OĂč nous allons

Nous allons ajouter une route de connexion Ă  notre application : la route vĂ©rifiera les identifiants dans le corps de la requĂȘte, et renverra une preuve d'authentification si tout va bien (appelons cette preuve un token, ou jeton en français : c'est l'Ă©tape 1 dans l'image ci-dessous). Sinon, la route rĂ©pondra avec le statut "Unauthorized" (401).

Ensuite, nous protĂ©gerons la route POST /api/movies. Si la requĂȘte POST ne contient aucun jeton, l'application n'acceptera plus de crĂ©er le film (Ă©tape 2a). La crĂ©ation fonctionnera comme d'habitude si le jeton est prĂ©sent (Ă©tape 2b).

auth flow

Allons-y une Ă©tape Ă  la fois đŸȘœ

⌚ Login

Tu as déjà utilisé un formulaire de connexion sur un site internet :

  1. Tu remplis le formulaire avec ton email/pseudo/id et ton mot de passe
  2. Tu cliques sur le bouton "envoyer"
  3. Tu as maintenant accÚs à tes paramÚtres privés

Maintenant que tu as acquis plus d'expĂ©rience, tu comprends ce que signifie soumettre un formulaire : tu envoies une requĂȘte HTTP au serveur.

Si nous avions une route POST /api/login dans notre application express, une requĂȘte de connexion Ă  notre serveur ressemblerait Ă  ceci :

shell
POST http://localhost:5000/api/login
Content-type: application/json

{
  "email": "dwight@theoffice.com",
  "password": "123456"
}

C'est par lĂ  que tu vas commencer : crĂ©ez une route POST /api/login. RĂ©ponds "Credentials are valid" si le corps de la requĂȘte contient "dwight@theoffice.com" et "123456" comme email et mot de passe. Sinon, envoie le statut 401. Ce ne sera pas le code dĂ©finitif, juste une mise en place ;)

hidden
Afficher la solution|||javascript|||Tu as des doutes sur comment faire ?|||0|||Masquer
// in app.js

const isItDwight = (req, res) => {
  if (req.body.email === "dwight@theoffice.com" && req.body.password === "123456") {
    res.send("Credentials are valid");
  } else {
    res.sendStatus(401);
  }
};

app.post("/api/login", isItDwight);
Info

Avant d'aller plus loin, tu devrais créer un utilisateur avec un mot de passe dont tu te souviendras, afin d'avoir un email et un mot de passe pour tester ta connexion.

Maintenant que ta route existe, nous devons penser au processus de connexion pour remplacer le middleware isItDwight :

  1. Tu vas d'abord rĂ©cupĂ©rer dans la base de donnĂ©es le mot de passe hachĂ© de l'utilisateur pour l'e-mail envoyĂ© dans la requĂȘte.
  2. Ensuite, vous tu vas vĂ©rifier ce mot de passe hachĂ© par rapport au mot de passe en clair envoyĂ© dans la requĂȘte.

Une bonne pratique ici serait de découper ce processus en plusieurs middlewares. Quelque chose comme ca :

javascript
app.post(
  "/api/login",
  userHandlers.getUserByEmailWithPasswordAndPassToNext,
  verifyPassword
);

Tu trouves peut ĂȘtre le nom getUserByEmailWithPasswordAndPassToNext compliquĂ©, mais prends le temps d'y penser :

  • La partie getUserByEmail est simple : c'est un miroir du middleware getUserById.
  • Nous avons dit prĂ©cĂ©demment que nos mĂ©thodes "get" ne devaient pas sĂ©lectionner le mot de passe pour des raisons de sĂ©curitĂ©. Sauf qu'ici nous devons sĂ©lectionner le mot de passe : cela doit ĂȘtre explicite dans le nom, d'oĂč la partie WithPassword.
  • Jusqu'Ă  prĂ©sent, nos mĂ©thodes "get" renvoyaient la rĂ©ponse au client aprĂšs avoir rĂ©cupĂ©rer les informations de la base de donnĂ©es. Sauf qu'ici, nous voulons passer ces informations au middleware suivant (verifyPassword), d'oĂč la partie AndPassToNext.

Cela fait un nom trÚs long, mais nous ne pouvons pas nous tromper sur ce que ce middleware fera, et sur ses spécificités par rapport aux autres middlewares.

Avec verifyPassword prototypé comme ceci dans app.js :

javascript
// ...

const userHandlers = require("./userHandlers");

// ...

const verifyPassword = (req, res) => {
  res.send(req.user);
}

app.post(
  "/api/login",
  userHandlers.getUserByEmailWithPasswordAndPassToNext,
  verifyPassword
);

// ...

Essaie de créer getUserByEmailWithPasswordAndPassToNext dans userHandlers.js. Tu peux t'inspirer de getUserById :

javascript
const getUserById = (req, res) => {
  const id = parseInt(req.params.id);

  database
    .query("select id, firstname, lastname, email, city, language from users where id = ?", [id])
    .then(([users]) => {
      if (users[0] != null) {
        res.json(users[0]);
      } else {
        res.status(404).send("Not Found");
      }
    })
    .catch((err) => {
      console.error(err);
      res.status(500).send("Error retrieving data from database");
    });
};

Sauf que :

  • Nous voulons sĂ©lectionner par un e-mail, en utilisant celui du corps de la requĂȘte.
  • N'oublie pas de sĂ©lectionner Ă©galement hashedPassword dans la table users.
  • En cas de succĂšs, ne renvoie pas de rĂ©ponse : remplis req.user avec l'utilisateur sĂ©lectionnĂ©, et passe au middleware suivant.
  • Si tu ne trouves pas d'utilisateur pour l'e-mail donnĂ©, envoie le statut 401 sans aucun message.
hidden
Afficher la solution|||javascript|||Tu as des doutes sur comment faire ?|||0|||Masquer
// ...

const getUserByEmailWithPasswordAndPassToNext = (req, res, next) => {
  const { email } = req.body;

  database
    .query("select * from users where email = ?", [email])
    .then(([users]) => {
      if (users[0] != null) {
        req.user = users[0];

        next();
      } else {
        res.sendStatus(401);
      }
    })
    .catch((err) => {
      console.error(err);
      res.status(500).send("Error retrieving data from database");
    });
};

module.exports = {
  // ...
  getUserByEmailWithPasswordAndPassToNext, // don't forget to export
};

Essaie ta route en utilisant Postman ou un autre outil : un email et un mot de passe valides doivent te retourner les données de l'utilisateur avec le mot de passe haché.

Continuons avec verifyPassword. Tu devrais d'abord le déplacer dans auth.js, avec le middleware hashPassword :

javascript
// in auth.js

const verifyPassword = (req, res) => {
  res.send(req.user);
}

module.exports = {
  hashPassword,
  verifyPassword, // don't forget to export
};

// in app.js

const userHandlers = require("./userHandlers");
const { hashPassword, verifyPassword } = require("./auth");

app.post(
  "/api/login",
  userHandlers.getUserByEmailWithPasswordAndPassToNext,
  verifyPassword
);

// ...

Tu ne peux pas "dĂ©-hacher" le mot de passe hachĂ©. C'est lĂ  que argon2.verify peut t'aider : en lui donnant un mot de passe hachĂ© et un mot de passe en clair, il rĂ©pondra s'ils correspondent ou non. Sans rentrer dans les dĂ©tails, il hachera le mot de passe en clair avec la mĂȘme configuration que le mot de passe hachĂ©. Il se retrouvera donc avec 2 hachages compatibles et les comparera.

Regarde la documentation d'argon2, et comment tu as utilisé les promesses dans hashPassword :

javascript
const hashPassword = (req, res, next) => {
  argon2
    .hash(req.body.password, hashingOptions)
    .then((hashedPassword) => {
      // do something with the result
    })
    .catch((err) => {
      console.error(err);
      res.sendStatus(500);
    });
};

(Tu peux également essayer la version async/wait si tu es d'humeur aventureuse)

Essaie maintenant d'utiliser argon2.verify dans verifyPassword. N'oublie pas : nous avons stocké les données de l'utilisateur dans req.user, tu peux donc accéder au mot de passe haché avec req.user.hashedPassword. Si argon2 peut vérifier le mot de passe, réponds par "Credentials are valid" pour cette étape. Sinon, envoie le statut 401.

hidden
Afficher la solution|||javascript|||Tu as des doutes sur comment faire ?|||0|||Masquer
const verifyPassword = (req, res) => {
  argon2
    .verify(req.user.hashedPassword, req.body.password)
    .then((isVerified) => {
      if (isVerified) {
        res.send("Credentials are valid");
      } else {
        res.sendStatus(401);
      }
    })
    .catch((err) => {
      console.error(err);
      res.sendStatus(500);
    });
};

Test ta route : maintenant, ton application est capable de vérifier un e-mail et un mot de passe par rapport aux données stockées dans la base de données lors de l'inscription.

Prochaine Ă©tape : renvoyer un token comme preuve d'authentification đŸ’Ș

đŸ§Ÿ JWT

Habituellement sur un site web, tu n'envoies pas tes identifiants pour chaque page. Une fois le login passĂ©, le serveur renvoie une preuve d'authentification. Cette preuve d'authentification est ensuite stockĂ©e par le client (ton navigateur) et envoyĂ©e au serveur dans les requĂȘtes suivantes.

Cela peut ĂȘtre un cookie avec un identifiant de session par exemple. Mais l'utilisation d'une session signifie que le serveur stocke des donnĂ©es pour chaque personne connectĂ©e. Un principe des APIs Rest est qu'une API doit ĂȘtre sans Ă©tat, ce qui signifie que le serveur ne doit stocker aucune information sur qui fait les requĂȘtes. Au lieu de cela, il doit fournir une chaĂźne de caractĂšres unique (chaque personne doit avoir une chaĂźne diffĂ©rente) pour identifier ses clients. Cet identifiant unique est appelĂ© un token (ou jeton en français).

Nous devons créer une chaßne unique (une par client) qui peut contenir des informations sur la personne connectée (comme son identifiant), et que seul notre serveur peut créer.

Heureusement, il existe une norme qui couvre cela. Elle s'appelle JWT.

Tout d'abord, voici à quoi ressemble un "JSON Web Token" :

image

Comme tu peux le voir, il y a 3 parties encodées, séparées par des points et des couleurs, dans le token :

How JSON Web Token(JWT) authentication works? | by Suresh Kumar | Medium

  1. Header : inclut le type de jeton et l'algorithme d'encodage (pour JWT, nous utiliserons HS256, qui est plus sûr).
  2. Payload : comprend la version encodée des informations de l'utilisateur (ou toute autre information).
  3. Signature : une version hachĂ©e de l'en-tĂȘte + payload. Cette partie garantira que le token n'a pas Ă©tĂ© altĂ©rĂ© : si le header ou le payload sont modifiĂ©s par un hacker, il devra gĂ©nĂ©rer un nouveau hash pour la signature, ce qui nĂ©cessitera un code secret (ton code secret doit rester secret).
↗
Jetons JWT et sĂ©curitĂ© – Principes et cas d’utilisation

Description des Jetons JWT

vaadata.com

Maintenant, comment créer ce JWT sur ton serveur ? Tout d'abord, tu dois installer le paquet jsonwebtoken :

shell
npm install jsonwebtoken

Ensuite, tu dois importer la bibliothĂšque et utiliser la fonction sign. Elle prend 2 arguments obligatoires :

  1. Le payload Ă  encoder.
  2. Le code secret Ă  utiliser. Nous avons besoin de ce code secret pour "signer" notre encodage et nous assurer que personne ne pourra crĂ©er le mĂȘme jeton. Sans ce code secret, le jeton n'est pas valide.

En utilisant un troisiÚme argument optionnel, tu peux ajouter des options comme un délai d'expiration par exemple.

À partir de la documentation de jsonwebtoken, essaie de complĂ©ter ton middleware verifyPassword avec les instructions suivantes :

  1. Utilise la version synchrone pour plus de simplicité.
  2. Le payload doit contenir l'identifiant de l'utilisateur sous le claim sub (sub signifie "sujet", l'utilisateur pour lequel le jeton est créé).
  3. Le jeton doit ĂȘtre signĂ© Ă  l'aide d'une chaĂźne secrĂšte stockĂ©e dans ton fichier .env (tu peux appeler la variable JWT_SECRET : n'oublie pas de mettre Ă  jour ton fichier .env.sample).
  4. Le jeton doit expirer 1 heure aprÚs sa création.
  5. Envoie le jeton dans la réponse. Tu peux également envoyer les données de l'utilisateur pour plus de commodité, mais n'envoie pas de mot de passe !
hidden
Afficher la solution|||javascript|||Tu as des doutes sur comment faire ?|||0|||Masquer
// ...

const jwt = require("jsonwebtoken"); // don't forget to import

// ...

const verifyPassword = (req, res) => {
  argon2
    .verify(req.user.hashedPassword, req.body.password)
    .then((isVerified) => {
      if (isVerified) {
        const payload = { sub: req.user.id };

        const token = jwt.sign(payload, process.env.JWT_SECRET, {
          expiresIn: "1h",
        });

        delete req.user.hashedPassword;
        res.send({ token, user: req.user });
      } else {
        res.sendStatus(401);
      }
    })
    .catch((err) => {
      console.error(err);
      res.sendStatus(500);
    });
};

Essaie de te connecter et d'obtenir un token de ton serveur.

Et voici la meilleure partie de JWT : N'importe qui peut lire le contenu des jetons, mais SEUL ton serveur peut les créer.

Pour vérifier cela, rends toi sur jwt.io et copie/colle un de tes tokens :

the decoded payload

Comme tu peux le voir, ton payload peut ĂȘtre entiĂšrement dĂ©codĂ©e (mais pas la signature).

đŸ›Ąïž ProtĂšge tes routes

Tu peux maintenant obtenir une preuve d'authentification de ton serveur : il est temps de protĂ©ger tes routes. Nous allons commencer par POST /api/movies. À l'heure actuelle, n'importe qui peut publier un nouveau film sur le serveur en utilisant une requĂȘte comme celle-ci :

shell
POST http://localhost:5000/api/movies
Content-type: application/json

{
  "title": "Citizen Kane",
  "director": "Orson Wells",
  "year": "1941",
  "color": "0",
  "duration": 120
}

Ton objectif est de rejeter une telle demande Ă  moins qu'elle ne contienne un en-tĂȘte "Authorization" avec le schĂ©ma d'authentification "Bearer" suivi par un jeton valide. Quelque chose comme ca :

shell
POST http://localhost:5000/api/movies
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjcsImlhdCI6MTY2MDIwMjczMywiZXhwIjoxNjYwMjA2MzMzfQ._5PYuoPoBfDtzq6fMsUn_8YGhfqtt_OaBVMHhe-FHlU
Content-type: application/json

{
  "title": "Citizen Kane",
  "director": "Orson Wells",
  "year": "1941",
  "color": "0",
  "duration": 120
}

Crée un nouveau middleware verifyToken dans auth.js comme ceci :

javascript
// ...

const verifyToken = (req, res, next) => {
  try {
    next();
  } catch (err) {
    console.error(err);
    res.sendStatus(401);
  }
};

module.exports = {
  hashPassword,
  verifyPassword,
  verifyToken, // don't forget to export
};

Pour l'instant, verifyToken ne vĂ©rifie rien : il se contente de passer au middleware suivant. Comme d'habitude, il est prĂ©fĂ©rable de commencer par une implĂ©mentation minimale et testable (mĂȘme si elle est incomplĂšte) puis de construire Ă©tape par Ă©tape.

Utilise verifyToken dans app.js :

javascript
// ...

const { hashPassword, verifyPassword, verifyToken } = require("./auth"); // don't forget to import

// ...

app.post("/api/movies", verifyToken, movieHandlers.postMovie);

// ...

C'est à toi maintenant ! Compléte le bloc try dans verifyToken en suivant ces étapes :

  1. RĂ©cupĂšre l'en-tĂȘte "Authorization" de la requĂȘte : gĂ©nĂšre une erreur si l'en-tĂȘte est manquant.
  2. Split l'en-tĂȘte en 2 sous-chaines type et token : gĂ©nĂšre une erreur si le type n'est pas "Bearer".
  3. Vérifie le jeton de maniÚre synchrone : s'il est valide, remplis req.payload avec le payload décodé.
  4. Passe au suivant -- tu as déjà celle-ci ;)
hidden
Afficher la solution|||javascript|||Tu as des doutes sur comment faire ?|||0|||Masquer
// ...

const verifyToken = (req, res, next) => {
  try {
    const authorizationHeader = req.get("Authorization");

    if (authorizationHeader == null) {
      throw new Error("Authorization header is missing");
    }

    const [type, token] = authorizationHeader.split(" ");

    if (type !== "Bearer") {
      throw new Error("Authorization header has not the 'Bearer' type");
    }

    req.payload = jwt.verify(token, process.env.JWT_SECRET);

    next();
  } catch (err) {
    console.error(err);
    res.sendStatus(401);
  }
};

module.exports = {
  hashPassword,
  verifyPassword,
  verifyToken, // don't forget to export
};

Essaie ta route POST /api/movies avec et sans jeton. Maintenant, ta route est protégée par authentification.

đŸ§± Le mur d'authentification

Une derniĂšre astuce. Ajouter le middleware verifyToken sur chaque route que tu souhaites protĂ©ger peut ĂȘtre fastidieux :

javascript
// ...

app.post("/api/movies", verifyToken, movieHandlers.postMovie);
app.put("/api/movies/:id", verifyToken, movieHandlers.updateMovie);
app.delete("/api/movies/:id", verifyToken, movieHandlers.deleteMovie);

// ...

Un moyen pratique consiste Ă  d'abord rĂ©partir tes routes entre celles publiques et celles qui doivent ĂȘtre protĂ©gĂ©es :

javascript
// the public routes

app.get("/api/movies", movieHandlers.getMovies);
app.get("/api/movies/:id", movieHandlers.getMovieById);

app.post(
  "/api/login",
  userHandlers.getUserByEmailWithPasswordAndPassToNext,
  verifyPassword
); // /!\ login should be a public route

// then the routes to protect

app.post("/api/movies", movieHandlers.postMovie);
app.put("/api/movies/:id", movieHandlers.updateMovie);
app.delete("/api/movies/:id", movieHandlers.deleteMovie);

// ...

Ensuite, tu vas construire un "mur d'authentification" entre les 2 parties de ton fichier : chaque route avant le mur restera inchangĂ©e, mais chaque route aprĂšs le mur va nĂ©cessiter un jeton pour fonctionner. Tu peux construire un tel mur en activant le middleware verifyToken avec app.use sans aucun path : le middleware sera exĂ©cutĂ© pour toutes les requĂȘtes Ă  l'application, mais uniquement pour les routes dĂ©clarĂ©es aprĂšs l'appel Ă  app.use. Quelque chose comme ca :

javascript
// the public routes

app.get("/api/movies", movieHandlers.getMovies);
app.get("/api/movies/:id", movieHandlers.getMovieById);

app.post(
  "/api/login",
  userHandlers.getUserByEmailWithPasswordAndPassToNext,
  verifyPassword
); // /!\ login should be a public route

// then the routes to protect

app.use(verifyToken); // authentication wall : verifyToken is activated for each route after this line

app.post("/api/movies", movieHandlers.postMovie);
app.put("/api/movies/:id", movieHandlers.updateMovie);
app.delete("/api/movies/:id", movieHandlers.deleteMovie);

// ...

đŸ’Ș Challenge:

Réorganise ton fichier app.js :

  1. Toutes les routes GET doivent ĂȘtre publiques.
  2. Les routes "register" (POST /api/users) et "login" (POST /api/login) doivent ĂȘtre publiques.
  3. Toutes les autres routes POST, PUT et DELETE doivent ĂȘtre protĂ©gĂ©es.

Héberge ton application améliorée sur GitHub et partage l'URL comme solution.

Bonus:

  1. PUT /api/users/:id et DELETE /api/users/:id ne devraient fonctionner que si l'id correspond à celui du payload du token (req.payload.sub). Si ce n'est pas le cas, réponds avec un statut 403 ("Forbidden").

🧐 Critùres de validation

  • [ ] L'application gĂ©nĂšre un JWT lorsque l'utilisateur se connecte.
  • [ ] Les routes GET, la route "register" et la route "login" sont publiques.
  • [ ] Toutes les autres routes sont protĂ©gĂ©es.

==$==

Ce que tu devrais avoir dans :

javascript
require("dotenv").config();

const express = require("express");

const app = express();

app.use(express.json());

const port = process.env.APP_PORT ?? 5000;

const welcome = (req, res) => {
  res.send("Welcome to my favourite movie list");
};

app.get("/", welcome);

const movieHandlers = require("./movieHandlers");
const userHandlers = require("./userHandlers");
const { hashPassword, verifyPassword, verifyToken } = require("./auth");

app.get("/api/movies", movieHandlers.getMovies);
app.get("/api/movies/:id", movieHandlers.getMovieById);

app.get("/api/users", userHandlers.getUsers);
app.get("/api/users/:id", userHandlers.getUserById);
app.post("/api/users", hashPassword, userHandlers.postUser);

app.post(
  "/api/login",
  userHandlers.getUserByEmailWithPasswordAndPassToNext,
  verifyPassword
);

app.use(verifyToken);

app.post("/api/movies", movieHandlers.postMovie);
app.put("/api/movies/:id", movieHandlers.updateMovie);
app.delete("/api/movies/:id", movieHandlers.deleteMovie);

app.put("/api/users/:id", hashPassword, userHandlers.updateUser);
app.delete("/api/users/:id", userHandlers.deleteUser);

app.listen(port, (err) => {
  if (err) {
    console.error("Something bad happened");
  } else {
    console.log(`Server is listening on ${port}`);
  }
});
javascript
const argon2 = require("argon2");
const jwt = require("jsonwebtoken");

const hashingOptions = {
  type: argon2.argon2id,
  memoryCost: 2 ** 16,
  timeCost: 5,
  parallelism: 1,
};

const hashPassword = (req, res, next) => {
  argon2
    .hash(req.body.password, hashingOptions)
    .then((hashedPassword) => {
      req.body.hashedPassword = hashedPassword;
      delete req.body.password;

      next();
    })
    .catch((err) => {
      console.error(err);
      res.sendStatus(500);
    });
};

const verifyPassword = (req, res) => {
  argon2
    .verify(req.user.hashedPassword, req.body.password)
    .then((isVerified) => {
      if (isVerified) {
        const payload = { sub: req.user.id };

        const token = jwt.sign(payload, process.env.JWT_SECRET, {
          expiresIn: "1h",
        });

        delete req.user.hashedPassword;
        res.send({ token, user: req.user });
      } else {
        res.sendStatus(401);
      }
    })
    .catch((err) => {
      console.error(err);
      res.sendStatus(500);
    });
};

const verifyToken = (req, res, next) => {
  try {
    const authorizationHeader = req.get("Authorization");

    if (authorizationHeader == null) {
      throw new Error("Authorization header is missing");
    }

    const [type, token] = authorizationHeader.split(" ");

    if (type !== "Bearer") {
      throw new Error("Authorization header has not the 'Bearer' type");
    }

    req.payload = jwt.verify(token, process.env.JWT_SECRET);

    next();
  } catch (err) {
    console.error(err);
    res.sendStatus(401);
  }
};

module.exports = {
  hashPassword,
  verifyPassword,
  verifyToken,
};
javascript
// ...

const updateUser = (req, res) => {
  const id = parseInt(req.params.id);

  if (id !== req.payload.sub) {
    return res.sendStatus(403);
  }

  const { firstname, lastname, email, city, language, hashedPassword } = req.body;

  database
    .query(
      "update users set firstname = ?, lastname = ?, email = ?, city = ?, language = ?, hashedPassword = ? where id = ?",
      [firstname, lastname, email, city, language, hashedPassword, id]
    )
    .then(([result]) => {
      if (result.affectedRows === 0) {
        res.status(404).send("Not Found");
      } else {
        res.sendStatus(204);
      }
    })
    .catch((err) => {
      console.error(err);
      res.status(500).send("Error editing the user");
    });
};

const deleteUser = (req, res) => {
  const id = parseInt(req.params.id);

  if (id !== req.payload.sub) {
    return res.sendStatus(403);
  }

  database
    .query("delete from users where id = ?", [id])
    .then(([result]) => {
      if (result.affectedRows === 0) {
        res.status(404).send("Not Found");
      } else {
        res.sendStatus(204);
      }
    })
    .catch((err) => {
      console.error(err);
      res.status(500).send("Error deleting the user");
    });
};

const getUserByEmailWithPasswordAndPassToNext = (req, res, next) => {
  const { email } = req.body;

  database
    .query("select * from users where email = ?", [email])
    .then(([users]) => {
      if (users[0] != null) {
        req.user = users[0];

        next();
      } else {
        res.sendStatus(401);
      }
    })
    .catch((err) => {
      console.error(err);
      res.status(500).send("Error retrieving data from database");
    });
};

module.exports = {
  getUsers,
  getUserById,
  postUser,
  updateUser,
  deleteUser,
  getUserByEmailWithPasswordAndPassToNext,
};

Ressources partagĂ©es sur cette quĂȘte