Création d'une API REST sécurisée dans Node.js

Publié: 2022-03-11

Les interfaces de programmation d'applications (API) sont partout. Ils permettent aux logiciels de communiquer avec d'autres logiciels, internes ou externes, de manière cohérente, ce qui est un ingrédient clé de l'évolutivité, sans parler de la réutilisabilité.

Il est assez courant de nos jours que les services en ligne aient des API accessibles au public. Ceux-ci permettent à d'autres développeurs d'intégrer facilement des fonctionnalités telles que les connexions aux réseaux sociaux, les paiements par carte de crédit et le suivi des comportements. La norme de facto qu'ils utilisent pour cela s'appelle REpresentational State Transfer (REST).

Bien qu'une multitude de plates-formes et de langages de programmation puissent être utilisés pour la tâche, par exemple, ASP.NET Core, Laravel (PHP) ou Bottle (Python) - dans ce didacticiel, nous allons créer un back-end d'API REST basique mais sécurisé à l'aide de la pile suivante :

  • Node.js, avec lequel le lecteur devrait déjà être familiarisé
  • Express, qui simplifie considérablement la création de tâches de serveur Web courantes sous Node.js et est un tarif standard pour la création d'un back-end d'API REST
  • Mongoose, qui connectera notre back-end à une base de données MongoDB

Les développeurs qui suivent ce didacticiel doivent également être à l'aise avec le terminal (ou l'invite de commande).

Remarque : nous n'aborderons pas ici une base de code frontale, mais le fait que notre back-end soit écrit en JavaScript facilite le partage de code (modèles d'objets, par exemple) sur l'ensemble de la pile.

Anatomie d'une API REST

Les API REST sont utilisées pour accéder aux données et les manipuler à l'aide d'un ensemble commun d'opérations sans état. Ces opérations font partie intégrante du protocole HTTP et représentent les fonctionnalités essentielles de création, lecture, mise à jour et suppression (CRUD), mais pas de manière propre :

  • POST (créer une ressource ou généralement fournir des données)
  • GET (récupérer un index de ressources ou une ressource individuelle)
  • PUT (créer ou remplacer une ressource)
  • PATCH (mettre à jour/modifier une ressource)
  • DELETE (supprimer une ressource)

En utilisant ces opérations HTTP et un nom de ressource comme adresse, nous pouvons créer une API REST en créant un point de terminaison pour chaque opération. Et en implémentant le pattern, nous aurons une base stable et facilement compréhensible nous permettant de faire évoluer le code rapidement et de le maintenir par la suite. Comme mentionné précédemment, la même base sera utilisée pour intégrer des fonctionnalités tierces, dont la plupart utilisent également des API REST, ce qui accélère cette intégration.

Pour l'instant, commençons à créer notre API REST sécurisée à l'aide de Node.js !

Dans ce tutoriel, nous allons créer une API REST assez courante (et très pratique) pour une ressource appelée users .

Notre ressource aura la structure de base suivante :

  • id (un UUID généré automatiquement)
  • firstName
  • lastName
  • email
  • password
  • permissionLevel (qu'est-ce que cet utilisateur est autorisé à faire ?)

Et nous allons créer les opérations suivantes pour cette ressource :

  • POST sur le point de terminaison /users (créer un nouvel utilisateur)
  • GET sur le point de terminaison /users (liste de tous les utilisateurs)
  • GET sur le point de terminaison /users/:userId (obtenir un utilisateur spécifique)
  • PATCH sur le endpoint /users/:userId (mettre à jour les données pour un utilisateur spécifique)
  • DELETE sur le endpoint /users/:userId (supprimer un utilisateur spécifique)

Nous utiliserons également des jetons Web JSON (JWT) pour les jetons d'accès. À cette fin, nous allons créer une autre ressource appelée auth qui attendra l'e-mail et le mot de passe d'un utilisateur et, en retour, générera le jeton utilisé pour l'authentification sur certaines opérations. (L'excellent article de Dejan Milosevic sur JWT pour les applications REST sécurisées en Java donne plus de détails à ce sujet ; les principes sont les mêmes.)

Configuration du didacticiel de l'API REST

Tout d'abord, assurez-vous que la dernière version de Node.js est installée. Pour cet article, j'utiliserai la version 14.9.0 ; cela peut également fonctionner sur des versions plus anciennes.

Ensuite, assurez-vous que MongoDB est installé. Nous n'expliquerons pas les spécificités de Mongoose et MongoDB qui sont utilisées ici, mais pour faire fonctionner les bases, démarrez simplement le serveur en mode interactif (c'est-à-dire depuis la ligne de commande en tant que mongo ) plutôt qu'en tant que service. En effet, à un moment donné de ce didacticiel, nous devrons interagir directement avec MongoDB plutôt que via notre code Node.js.

Remarque : Avec MongoDB, il n'est pas nécessaire de créer une base de données spécifique comme cela pourrait être le cas dans certains scénarios RDBMS. Le premier appel d'insertion de notre code Node.js déclenchera automatiquement sa création.

Ce didacticiel ne contient pas tout le code nécessaire à un projet fonctionnel. Il est plutôt prévu que vous cloniez le référentiel compagnon et que vous suiviez simplement les faits saillants au fur et à mesure que vous lisez, mais vous pouvez également copier des fichiers et des extraits spécifiques du référentiel selon vos besoins, si vous préférez.

Accédez au dossier rest-api-tutorial/ résultant dans votre terminal. Vous verrez que notre projet contient trois dossiers de modules :

  • common (gestion de tous les services partagés et des informations partagées entre les modules utilisateur)
  • users (tout ce qui concerne les utilisateurs)
  • auth (gestion de la génération de JWT et du flux de connexion)

Maintenant, lancez npm install (ou yarn si vous l'avez).

Félicitations, vous disposez maintenant de toutes les dépendances et de la configuration requises pour exécuter notre API REST simple.

Création du module utilisateur

Nous utiliserons Mongoose, une bibliothèque de modélisation de données d'objet (ODM) pour MongoDB, pour créer le modèle utilisateur dans le schéma utilisateur.

Tout d'abord, nous devons créer le schéma Mongoose dans /users/models/users.model.js :

 const userSchema = new Schema({ firstName: String, lastName: String, email: String, password: String, permissionLevel: Number });

Une fois que nous avons défini le schéma, nous pouvons facilement attacher le schéma au modèle utilisateur.

 const userModel = mongoose.model('Users', userSchema);

Après cela, nous pouvons utiliser ce modèle pour implémenter toutes les opérations CRUD que nous souhaitons dans nos points de terminaison Express.

Commençons par l'opération "create user" en définissant la route dans users/routes.config.js :

 app.post('/users', [ UsersController.insert ]);

Ceci est extrait dans notre application Express dans le fichier principal index.js . L'objet UsersController est importé de notre contrôleur, où nous hachons le mot de passe de manière appropriée, défini dans /users/controllers/users.controller.js :

 exports.insert = (req, res) => { let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512',salt) .update(req.body.password) .digest("base64"); req.body.password = salt + "$" + hash; req.body.permissionLevel = 1; UserModel.createUser(req.body) .then((result) => { res.status(201).send({id: result._id}); }); };

À ce stade, nous pouvons tester notre modèle Mongoose en exécutant le serveur ( npm start ) et en envoyant une requête POST à /users avec des données JSON :

 { "firstName" : "Marcos", "lastName" : "Silva", "email" : "[email protected]", "password" : "s3cr3tp4sswo4rd" }

Il existe plusieurs outils que vous pouvez utiliser pour cela. Insomnia (couvert ci-dessous) et Postman sont des outils GUI populaires, et curl est un choix CLI courant. Vous pouvez même simplement utiliser JavaScript, par exemple, à partir de la console d'outils de développement intégrée de votre navigateur :

 fetch('http://localhost:3600/users', { method: 'POST', headers: { "Content-type": "application/json" }, body: JSON.stringify({ "firstName": "Marcos", "lastName": "Silva", "email": "[email protected]", "password": "s3cr3tp4sswo4rd" }) }) .then(function(response) { return response.json(); }) .then(function(data) { console.log('Request succeeded with JSON response', data); }) .catch(function(error) { console.log('Request failed', error); });

À ce stade, le résultat d'une publication valide sera uniquement l'identifiant de l'utilisateur créé : { "id": "5b02c5c84817bf28049e58a3" } . Nous devons également ajouter la méthode createUser au modèle dans users/models/users.model.js :

 exports.createUser = (userData) => { const user = new User(userData); return user.save(); };

Tout est prêt, nous devons maintenant voir si l'utilisateur existe. Pour cela, nous allons implémenter la fonctionnalité « get user by id » pour le point de terminaison suivant : users/:userId .

Tout d'abord, nous créons une route dans /users/routes/config.js :

 app.get('/users/:userId', [ UsersController.getById ]);

Ensuite, nous créons le contrôleur dans /users/controllers/users.controller.js :

 exports.getById = (req, res) => { UserModel.findById(req.params.userId).then((result) => { res.status(200).send(result); }); };

Et enfin, ajoutez la méthode findById au modèle dans /users/models/users.model.js :

 exports.findById = (id) => { return User.findById(id).then((result) => { result = result.toJSON(); delete result._id; delete result.__v; return result; }); };

La réponse sera comme ceci :

 { "firstName": "Marcos", "lastName": "Silva", "email": "[email protected]", "password": "Y+XZEaR7J8xAQCc37nf1rw==$p8b5ykUx6xpC6k8MryDaRmXDxncLumU9mEVabyLdpotO66Qjh0igVOVerdqAh+CUQ4n/E0z48mp8SDTpX2ivuQ==", "permissionLevel": 1, "id": "5b02c5c84817bf28049e58a3" }

Notez que nous pouvons voir le mot de passe haché. Pour ce tutoriel, nous montrons le mot de passe, mais la meilleure pratique évidente est de ne jamais révéler le mot de passe, même s'il a été haché. Une autre chose que nous pouvons voir est le permissionLevel , que nous utiliserons pour gérer les autorisations des utilisateurs plus tard.

En répétant le modèle décrit ci-dessus, nous pouvons maintenant ajouter la fonctionnalité pour mettre à jour l'utilisateur. Nous utiliserons l'opération PATCH puisqu'elle nous permettra de n'envoyer que les champs que nous voulons modifier. La route sera donc PATCH vers /users/:userid , et nous enverrons tous les champs que nous souhaitons modifier. Nous devrons également implémenter une validation supplémentaire car les modifications doivent être limitées à l'utilisateur en question ou à un administrateur, et seul un administrateur doit pouvoir modifier le permissionLevel . Nous allons ignorer cela pour le moment et y revenir une fois que nous aurons implémenté le module auth. Pour l'instant, notre contrôleur ressemblera à ceci :

 exports.patchById = (req, res) => { if (req.body.password){ let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64"); req.body.password = salt + "$" + hash; } UserModel.patchUser(req.params.userId, req.body).then((result) => { res.status(204).send({}); }); };

Par défaut, nous enverrons un code HTTP 204 sans corps de réponse pour indiquer que la requête a réussi.

Et nous devrons ajouter la méthode patchUser au modèle :

 exports.patchUser = (id, userData) => { return User.findOneAndUpdate({ _id: id }, userData); };

La liste d'utilisateurs sera implémentée en tant que GET à /users/ par le contrôleur suivant :

 exports.list = (req, res) => { let limit = req.query.limit && req.query.limit <= 100 ? parseInt(req.query.limit) : 10; let page = 0; if (req.query) { if (req.query.page) { req.query.page = parseInt(req.query.page); page = Number.isInteger(req.query.page) ? req.query.page : 0; } } UserModel.list(limit, page).then((result) => { res.status(200).send(result); }) };

La méthode modèle correspondante sera :

 exports.list = (perPage, page) => { return new Promise((resolve, reject) => { User.find() .limit(perPage) .skip(perPage * page) .exec(function (err, users) { if (err) { reject(err); } else { resolve(users); } }) }); };

La réponse de liste résultante aura la structure suivante :

 [ { "firstName": "Marco", "lastName": "Silva", "email": "[email protected]", "password": "z4tS/DtiH+0Gb4J6QN1K3w==$al6sGxKBKqxRQkDmhnhQpEB6+DQgDRH2qr47BZcqLm4/fphZ7+a9U+HhxsNaSnGB2l05Oem/BLIOkbtOuw1tXA==", "permissionLevel": 1, "id": "5b02c5c84817bf28049e58a3" }, { "firstName": "Paulo", "lastName": "Silva", "email": "[email protected]", "password": "wTsqO1kHuVisfDIcgl5YmQ==$cw7RntNrNBNw3MO2qLbx959xDvvrDu4xjpYfYgYMxRVDcxUUEgulTlNSBJjiDtJ1C85YimkMlYruU59rx2zbCw==", "permissionLevel": 1, "id": "5b02d038b653603d1ca69729" } ]

Et la dernière partie à implémenter est le DELETE à /users/:userId .

Notre contrôleur pour la suppression sera :

 exports.removeById = (req, res) => { UserModel.removeById(req.params.userId) .then((result)=>{ res.status(204).send({}); }); };

Comme précédemment, le contrôleur renverra le code HTTP 204 et aucun corps de contenu comme confirmation.

La méthode de modèle correspondante devrait ressembler à ceci :

 exports.removeById = (userId) => { return new Promise((resolve, reject) => { User.deleteMany({_id: userId}, (err) => { if (err) { reject(err); } else { resolve(err); } }); }); };

Nous avons maintenant toutes les opérations nécessaires pour manipuler la ressource utilisateur, et nous en avons terminé avec le contrôleur utilisateur. L'idée principale de ce code est de vous donner les concepts de base de l'utilisation du modèle REST. Nous devrons revenir à ce code pour y implémenter certaines validations et autorisations, mais d'abord, nous devrons commencer à construire notre sécurité. Créons le module auth.

Création du module d'authentification

Avant de pouvoir sécuriser le module users en implémentant le middleware d'autorisation et de validation, nous devons être en mesure de générer un jeton valide pour l'utilisateur actuel. Nous générerons un JWT en réponse à l'utilisateur fournissant un e-mail et un mot de passe valides. JWT est un jeton Web JSON remarquable que vous pouvez utiliser pour que l'utilisateur fasse plusieurs requêtes en toute sécurité sans valider à plusieurs reprises. Il a généralement un délai d'expiration et un nouveau jeton est recréé toutes les quelques minutes pour sécuriser la communication. Pour ce didacticiel, cependant, nous renoncerons à actualiser le jeton et le garderons simple avec un seul jeton par connexion.

Tout d'abord, nous allons créer un point de terminaison pour les requêtes POST vers la ressource /auth . Le corps de la requête contiendra l'e-mail et le mot de passe de l'utilisateur :

 { "email" : "[email protected]", "password" : "s3cr3tp4sswo4rd2" }

Avant d'engager le contrôleur, nous devons valider l'utilisateur dans /authorization/middlewares/verify.user.middleware.js :

 exports.isPasswordAndUserMatch = (req, res, next) => { UserModel.findByEmail(req.body.email) .then((user)=>{ if(!user[0]){ res.status(404).send({}); }else{ let passwordFields = user[0].password.split('$'); let salt = passwordFields[0]; let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64"); if (hash === passwordFields[1]) { req.body = { userId: user[0]._id, email: user[0].email, permissionLevel: user[0].permissionLevel, provider: 'email', name: user[0].firstName + ' ' + user[0].lastName, }; return next(); } else { return res.status(400).send({errors: ['Invalid email or password']}); } } }); };

Cela fait, nous pouvons passer au contrôleur et générer le JWT :

 exports.login = (req, res) => { try { let refreshId = req.body.userId + jwtSecret; let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(refreshId).digest("base64"); req.body.refreshKey = salt; let token = jwt.sign(req.body, jwtSecret); let b = Buffer.from(hash); let refresh_token = b.toString('base64'); res.status(201).send({accessToken: token, refreshToken: refresh_token}); } catch (err) { res.status(500).send({errors: err}); } };

Même si nous n'actualiserons pas le jeton dans ce didacticiel, le contrôleur a été configuré pour permettre une telle génération afin de faciliter son implémentation dans les développements ultérieurs.

Il ne nous reste plus qu'à créer la route et à invoquer le middleware approprié dans /authorization/routes.config.js :

 app.post('/auth', [ VerifyUserMiddleware.hasAuthValidFields, VerifyUserMiddleware.isPasswordAndUserMatch, AuthorizationController.login ]);

La réponse contiendra le JWT généré dans le champ accessToken :

 { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1YjAyYzVjODQ4MTdiZjI4MDQ5ZTU4YTMiLCJlbWFpbCI6Im1hcmNvcy5oZW5yaXF1ZUB0b3B0YWwuY29tIiwicGVybWlzc2lvbkxldmVsIjoxLCJwcm92aWRlciI6ImVtYWlsIiwibmFtZSI6Ik1hcmNvIFNpbHZhIiwicmVmcmVzaF9rZXkiOiJiclhZUHFsbUlBcE1PakZIRG1FeENRPT0iLCJpYXQiOjE1MjY5MjMzMDl9.mmNg-i44VQlUEWP3YIAYXVO-74803v1mu-y9QPUQ5VY", "refreshToken": "U3BDQXBWS3kyaHNDaGJNanlJTlFkSXhLMmFHMzA2NzRsUy9Sd2J0YVNDTmUva0pIQ0NwbTJqOU5YZHgxeE12NXVlOUhnMzBWMGNyWmdOTUhSaTdyOGc9PQ==" }

Après avoir créé le jeton, nous pouvons l'utiliser dans l'en-tête Authorization en utilisant le formulaire Bearer ACCESS_TOKEN .

Création d'intergiciels d'autorisations et de validations

La première chose que nous devrions définir est qui peut utiliser la ressource users . Voici les scénarios que nous devrons gérer :

  • Public pour la création d'utilisateurs (processus d'enregistrement). Nous n'utiliserons pas JWT pour ce scénario.
  • Privé pour l'utilisateur connecté et pour que les administrateurs mettent à jour cet utilisateur.
  • Privé pour l'administrateur uniquement pour la suppression des comptes d'utilisateurs.

Après avoir identifié ces scénarios, nous aurons d'abord besoin d'un middleware qui valide toujours l'utilisateur s'il utilise un JWT valide. Le middleware dans /common/middlewares/auth.validation.middleware.js peut être aussi simple que :

 exports.validJWTNeeded = (req, res, next) => { if (req.headers['authorization']) { try { let authorization = req.headers['authorization'].split(' '); if (authorization[0] !== 'Bearer') { return res.status(401).send(); } else { req.jwt = jwt.verify(authorization[1], secret); return next(); } } catch (err) { return res.status(403).send(); } } else { return res.status(401).send(); } };

Nous utiliserons les codes d'erreur HTTP pour gérer les erreurs de requête :

  • HTTP 401 pour une requête invalide
  • HTTP 403 pour une requête valide avec un jeton non valide ou un jeton valide avec des autorisations non valides

Nous pouvons utiliser l'opérateur ET au niveau du bit (bitmasking) pour contrôler les autorisations. Si nous définissons chaque autorisation requise comme une puissance de 2, nous pouvons traiter chaque bit de l'entier 32 bits comme une seule autorisation. Un administrateur peut alors avoir toutes les autorisations en définissant sa valeur d'autorisation sur 2147483647. Cet utilisateur peut alors avoir accès à n'importe quel itinéraire. Comme autre exemple, un utilisateur dont la valeur d'autorisation a été définie sur 7 aurait des autorisations sur les rôles marqués par des bits pour les valeurs 1, 2 et 4 (deux à la puissance de 0, 1 et 2).

Le middleware pour cela ressemblerait à ceci:

 exports.minimumPermissionLevelRequired = (required_permission_level) => { return (req, res, next) => { let user_permission_level = parseInt(req.jwt.permission_level); let user_id = req.jwt.user_id; if (user_permission_level & required_permission_level) { return next(); } else { return res.status(403).send(); } }; };

Le middleware est générique. Si le niveau d'autorisation de l'utilisateur et le niveau d'autorisation requis coïncident sur au moins un bit, le résultat sera supérieur à zéro et nous pouvons laisser l'action se poursuivre ; sinon, le code HTTP 403 sera renvoyé.

Maintenant, nous devons ajouter le middleware d'authentification aux routes du module de l'utilisateur dans /users/routes.config.js :

 app.post('/users', [ UsersController.insert ]); app.get('/users', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(PAID), UsersController.list ]); app.get('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(FREE), PermissionMiddleware.onlySameUserOrAdminCanDoThisAction, UsersController.getById ]); app.patch('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(FREE), PermissionMiddleware.onlySameUserOrAdminCanDoThisAction, UsersController.patchById ]); app.delete('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(ADMIN), UsersController.removeById ]);

Ceci conclut le développement de base de notre API REST. Il ne reste plus qu'à tout tester.

Courir et tester avec l'insomnie

Insomnia est un client REST décent avec une bonne version gratuite. La meilleure pratique consiste, bien sûr, à inclure des tests de code et à implémenter un rapport d'erreurs approprié dans le projet, mais les clients REST tiers sont parfaits pour tester et implémenter des solutions tierces lorsque le rapport d'erreurs et le débogage du service ne sont pas disponibles. Nous l'utiliserons ici pour jouer le rôle d'une application et avoir un aperçu de ce qui se passe avec notre API.

Pour créer un utilisateur, il nous suffit de POST les champs requis sur le point de terminaison approprié et de stocker l'ID généré pour une utilisation ultérieure.

Demande avec les données appropriées pour la création d'un utilisateur

L'API répondra avec l'ID utilisateur :

Réponse de confirmation avec userID

Nous pouvons maintenant générer le JWT en utilisant le point de terminaison /auth/ :

Demande avec données de connexion

Nous devrions obtenir un jeton comme réponse :

Confirmation contenant le jeton Web JSON correspondant

Saisissez le accessToken , préfixez-le avec Bearer (rappelez-vous l'espace) et ajoutez-le aux en-têtes de requête sous Authorization :

Configuration des en-têtes à transférer contenant le JWT d'authentification

Si nous ne le faisons pas maintenant que nous avons implémenté le middleware d'autorisations, chaque demande autre que l'enregistrement renverra le code HTTP 401. Avec le jeton valide en place, cependant, nous obtenons la réponse suivante de /users/:userId :

Réponse listant les données de l'utilisateur indiqué

De plus, comme cela a été mentionné précédemment, nous affichons tous les champs, à des fins éducatives et par souci de simplicité. Le mot de passe (haché ou autre) ne doit jamais être visible dans la réponse.

Essayons d'obtenir une liste d'utilisateurs :

Demander une liste de tous les utilisateurs

Surprendre! Nous obtenons une réponse 403.

Action refusée en raison d'un niveau d'autorisation insuffisant

Notre utilisateur n'a pas les autorisations pour accéder à ce point de terminaison. Nous devrons changer le niveau d' permissionLevel de notre utilisateur de 1 à 7 (ou même 5 ferait l'affaire, puisque nos niveaux d'autorisations gratuits et payants sont représentés respectivement par 1 et 4.) Nous pouvons le faire manuellement dans MongoDB, à son invite interactive , comme ceci (avec l'ID remplacé par votre résultat local) :

 db.users.update({"_id" : ObjectId("5b02c5c84817bf28049e58a3")},{$set:{"permissionLevel":5}})

Ensuite, nous devons générer un nouveau JWT.

Après cela, nous obtenons la réponse appropriée :

Réponse avec tous les utilisateurs et leurs données

Ensuite, testons la fonctionnalité de mise à jour en envoyant une requête PATCH avec certains champs à notre point de terminaison /users/:userId :

Demande contenant des données partielles à mettre à jour

Nous attendons une réponse 204 comme confirmation d'une opération réussie, mais nous pouvons demander à nouveau à l'utilisateur de vérifier.

Réponse après modification réussie

Enfin, nous devons supprimer l'utilisateur. Nous devrons créer un nouvel utilisateur comme décrit ci-dessus (n'oubliez pas de noter l'ID utilisateur) et assurez-vous que nous avons le JWT approprié pour un utilisateur administrateur. Le nouvel utilisateur aura besoin que ses autorisations soient définies sur 2053 (c'est-à-dire 2048 - ADMIN - plus nos 5 précédentes) pour pouvoir également effectuer l'opération de suppression. Une fois cela fait et un nouveau JWT généré, nous devrons mettre à jour notre en-tête de demande d' Authorization :

Demander la configuration pour supprimer un utilisateur

En envoyant une requête DELETE à /users/:userId , nous devrions obtenir une réponse 204 en guise de confirmation. Nous pouvons, encore une fois, vérifier en demandant à /users/ de lister tous les utilisateurs existants.

Prochaines étapes pour votre API REST

Avec les outils et les méthodes abordés dans ce didacticiel, vous devriez maintenant être en mesure de créer des API REST simples et sécurisées sur Node.js. De nombreuses bonnes pratiques qui ne sont pas essentielles au processus ont été ignorées, alors n'oubliez pas de :

  • Mettre en œuvre les validations appropriées (par exemple, s'assurer que l'adresse e-mail de l'utilisateur est unique)
  • Mettre en œuvre des tests unitaires et des rapports d'erreurs
  • Empêcher les utilisateurs de modifier leur propre niveau d'autorisation
  • Empêcher les administrateurs de se retirer
  • Empêcher la divulgation d'informations sensibles (par exemple, les mots de passe hachés)
  • Déplacer le secret JWT de common/config/env.config.js vers un mécanisme de distribution de secret hors dépôt et non basé sur l'environnement

Un dernier exercice pour le lecteur peut être de convertir la base de code de son utilisation des promesses JavaScript vers la technique async/wait.

Pour ceux d'entre vous qui pourraient être intéressés, une version TypeScript du projet est désormais disponible.

En relation: 5 choses que vous n'avez jamais faites avec une spécification REST