Comment authentifier et autoriser un utilisateur à l'aide de JWT dans NodeJS
L'authentification et l'autorisation représentent les fondements de la sécurité des systèmes informatiques. L'usage de vos identifiants, tels qu'un nom d'utilisateur et un mot de passe, permet de confirmer votre identité et de vous désigner comme un utilisateur enregistré, ouvrant ainsi la voie à des privilèges étendus.
Ce processus est également à l'œuvre lorsque vous vous connectez à des services en ligne via vos comptes Facebook ou Google.
Dans cet article, nous allons élaborer une API Node.js intégrant une authentification par jetons Web JSON (JWT). Les outils que nous allons exploiter dans ce tutoriel incluent :
- Express.js
- Une base de données MongoDB
- Mongoose
- Dotenv
- Bcrypt.js
- Jsonwebtoken
Distinction entre Authentification et Autorisation
Qu'est-ce que l'authentification ?
L'authentification est le procédé qui consiste à identifier les utilisateurs en obtenant des informations d'identification telles que l'adresse e-mail, le mot de passe et des jetons. Ces données sont comparées aux informations de l'utilisateur enregistré, conservées dans le système informatique local ou dans une base de données. Si les données fournies concordent avec celles enregistrées, l'authentification est réussie et l'utilisateur peut accéder aux ressources.
Qu'est-ce que l'autorisation ?
L'autorisation intervient après l'authentification. Elle est systématiquement précédée par un processus d'authentification. L'autorisation donne accès aux utilisateurs aux ressources d'un système ou d'un site web. Dans ce tutoriel, nous allons autoriser les utilisateurs connectés à consulter leurs propres informations. Un utilisateur non connecté ne pourra pas accéder à ces données.
Les plateformes de médias sociaux comme Facebook et Twitter sont d'excellents exemples d'autorisation. L'accès au contenu est impossible sans posséder un compte.
Un autre exemple d'autorisation se trouve dans le contenu accessible par abonnement. L'authentification se fait par la connexion au site, mais l'accès au contenu requiert un abonnement préalable.
Prérequis
Avant de débuter, il est préférable d'avoir une compréhension de base de JavaScript et de MongoDB ainsi qu'une bonne connaissance de Node.js.
Assurez-vous que Node.js et npm sont installés sur votre ordinateur. Pour vérifier leur installation, ouvrez l'invite de commande et tapez `node -v` et `npm -v`. Le résultat devrait être similaire à ce qui suit :
Vos versions peuvent différer. npm est automatiquement installé avec Node.js. Si vous n'avez pas encore Node.js, téléchargez-le depuis le site Web de Node.js.
Vous aurez besoin d'un EDI (environnement de développement intégré) pour écrire du code. Dans ce tutoriel, j'utilise l'éditeur de code VS Code. Vous pouvez utiliser un autre éditeur si vous le souhaitez. Si vous n'avez pas encore d'EDI installé, vous pouvez le télécharger depuis le site Web de Visual Studio et choisir la version compatible avec votre système.
Mise en place du projet
Créez un dossier nommé "nodeapi" où vous le souhaitez sur votre ordinateur, puis ouvrez-le avec VS Code. Ouvrez le terminal de VS Code et initialisez le gestionnaire de paquets de Node en tapant :
npm init -y
Assurez-vous que vous êtes dans le répertoire "nodeapi".
La commande ci-dessus créera un fichier "package.json" qui contiendra toutes les dépendances que nous utiliserons dans ce projet.
Nous allons maintenant télécharger tous les paquets mentionnés précédemment en tapant la commande suivante dans le terminal :
npm install express dotenv jsonwebtoken mongoose bcryptjs
Vous devriez maintenant avoir des fichiers et des dossiers comme indiqué ci-dessous.

Création du serveur et connexion à la base de données
Créez un fichier nommé "index.js" et un dossier nommé "config". Dans "config", créez deux fichiers nommés "conn.js" pour établir la connexion à la base de données et "config.env" pour déclarer les variables d'environnement. Saisissez le code ci-dessous dans les fichiers correspondants.
index.js
const express = require('express');
const dotenv = require('dotenv');
//Configuration des fichiers dotenv avant tout autre fichier ou librairie
dotenv.config({path:'./config/config.env'});
//Création d'une application à partir d'express
const app = express();
//Utilisation d'express.json pour recevoir les requêtes en format json
app.use(express.json());
//Écoute du serveur
app.listen(process.env.PORT,()=>{
console.log(`Le serveur écoute sur le port ${process.env.PORT}`);
})
Si vous utilisez dotenv, configurez-le dans votre fichier "index.js" avant d'appeler un autre fichier qui utilise les variables d'environnement.
conn.js
const mongoose = require('mongoose');
mongoose.connect(process.env.URI,
{ useNewUrlParser: true,
useUnifiedTopology: true })
.then((data) => {
console.log(`Base de données connectée à ${data.connection.host}`)
})
config.env
URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority' PORT = 5000
J'utilise l'URI de MongoDB Atlas, mais vous pouvez également utiliser localhost.
Création des modèles et des routes
Le modèle est une représentation de vos données dans la base de données MongoDB, stockées au format JSON. Pour créer un modèle, nous allons utiliser le schéma de Mongoose.
Le routage définit la manière dont une application répond aux requêtes des clients. Nous utiliserons la fonctionnalité de routeur d'Express pour créer des routes.
Les méthodes de routage acceptent généralement deux arguments. Le premier est la route, et le second est la fonction de rappel définissant l'action à effectuer lors d'une requête.
Un troisième argument peut être ajouté en cas de besoin pour définir des fonctions intermédiaires (middleware), comme pour un processus d'authentification. Puisque nous construisons une API authentifiée, nous allons également utiliser des fonctions middleware pour authentifier et autoriser les utilisateurs.
Nous allons créer deux dossiers nommés "routes" et "models". Dans "routes", créez un fichier nommé "userRoute.js" et dans "models", créez un fichier nommé "userModel.js". Ajoutez le code suivant dans les fichiers respectifs.
userModel.js
const mongoose = require('mongoose');
//Création d'un schéma avec mongoose
const userSchema = new mongoose.Schema({
name: {
type:String,
required:true,
minLength:[4,'Le nom doit avoir au minimum 4 caractères']
},
email:{
type:String,
required:true,
unique:true,
},
password:{
type:String,
required:true,
minLength:[8,'Le mot de passe doit avoir au minimum 8 caractères']
},
token:{
type:String
}
})
//Création des modèles
const userModel = mongoose.model('user',userSchema);
module.exports = userModel;
userRoute.js
const express = require('express');
//Création d'un routeur express
const route = express.Router();
//Importation du modèle userModel
const userModel = require('../models/userModel');
//Création de la route pour l'enregistrement
route.post('/register',(req,res)=>{
})
//Création de la route pour la connexion
route.post('/login',(req,res)=>{
})
//Création de la route pour récupérer les données des utilisateurs
route.get('/user',(req,res)=>{
})
Implémentation de la fonctionnalité des routes et création de jetons JWT
Qu'est-ce que JWT ?
Les jetons Web JSON (JWT) sont une bibliothèque JavaScript qui génère et vérifie des jetons. Il s'agit d'une norme ouverte pour le partage d'informations entre deux parties : un client et un serveur. Nous allons utiliser deux fonctions de JWT : sign pour créer un nouveau jeton et verify pour le vérifier.
Qu'est-ce que bcrypt.js ?
Bcrypt.js est une fonction de hachage créée par Niels Provos et David Mazières. Elle utilise un algorithme de hachage pour hacher les mots de passe. Elle possède deux fonctions principales que nous allons utiliser dans ce projet. La première est `hash` pour générer une valeur de hachage et la seconde est `compare` pour comparer les mots de passe.
Implémentation de la fonctionnalité des routes
La fonction de rappel dans le routage accepte trois arguments : la requête, la réponse et la fonction suivante. L'argument `next` est facultatif ; il est utilisé lorsque cela s'avère nécessaire. Ces arguments doivent figurer dans l'ordre : requête, réponse et next. Modifiez maintenant les fichiers "userRoute.js", "config.env" et "index.js" avec le code suivant.
userRoute.js
//Importation des fichiers et librairies nécessaires
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
//Création d'un routeur express
const route = express.Router();
//Importation du modèle userModel
const userModel = require('../models/userModel');
//Création de la route pour l'enregistrement
route.post("/register", async (req, res) => {
try {
const { name, email, password } = req.body;
//Vérification que toutes les données sont présentes
if (!name || !email || !password) {
return res.json({ message: 'Veuillez entrer tous les détails' })
}
//Vérification de l'existence d'un utilisateur avec le même email
const userExist = await userModel.findOne({ email: req.body.email });
if (userExist) {
return res.json({ message: 'Un utilisateur existe déjà avec cet email' })
}
//Hashage du mot de passe
const salt = await bcrypt.genSalt(10);
const hashPassword = await bcrypt.hash(req.body.password, salt);
req.body.password = hashPassword;
const user = new userModel(req.body);
await user.save();
const token = await jwt.sign({ id: user._id }, process.env.SECRET_KEY, {
expiresIn: process.env.JWT_EXPIRE,
});
return res.cookie({ 'token': token }).json({ success: true, message: 'Utilisateur enregistré avec succès', data: user })
} catch (error) {
return res.json({ error: error });
}
})
//Création de la route pour la connexion
route.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
//Vérification que toutes les données sont présentes
if (!email || !password) {
return res.json({ message: 'Veuillez entrer tous les détails' })
}
//Vérification de l'existence d'un utilisateur avec le même email
const userExist = await userModel.findOne({email:req.body.email});
if(!userExist){
return res.json({message:'Mauvais identifiants'})
}
//Comparaison des mots de passe
const isPasswordMatched = await bcrypt.compare(password,userExist.password);
if(!isPasswordMatched){
return res.json({message:'Mauvais identifiants mot de passe'});
}
const token = await jwt.sign({ id: userExist._id }, process.env.SECRET_KEY, {
expiresIn: process.env.JWT_EXPIRE,
});
return res.cookie({"token":token}).json({success:true,message:'Connexion réussie'})
} catch (error) {
return res.json({ error: error });
}
})
//Création de la route pour récupérer les données des utilisateurs
route.get('/user', async (req, res) => {
try {
const user = await userModel.find();
if(!user){
return res.json({message:'Aucun utilisateur trouvé'})
}
return res.json({user:user})
} catch (error) {
return res.json({ error: error });
}
})
module.exports = route;
Si vous utilisez la fonction async, utilisez le bloc try-catch, sinon une erreur de rejet de promesse non gérée sera générée.
config.env
URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority' PORT = 5000 SECRET_KEY = KGGK>HKHVHJVKBKJKJBKBKHKBMKHB JWT_EXPIRE = 2d
index.js
const express = require('express');
const dotenv = require('dotenv');
//Configuration des fichiers dotenv avant tout autre fichier ou librairie
dotenv.config({path:'./config/config.env'});
require('./config/conn');
//Création d'une application à partir d'express
const app = express();
const route = require('./routes/userRoute');
//Utilisation d'express.json pour recevoir les requêtes en format json
app.use(express.json());
//Utilisation des routes
app.use('/api', route);
//Écoute du serveur
app.listen(process.env.PORT,()=>{
console.log(`Le serveur écoute sur le port ${process.env.PORT}`);
})
Création d'un middleware pour authentifier l'utilisateur
Qu'est-ce qu'un middleware ?
Un middleware est une fonction qui a accès à la requête, à l'objet de réponse et à la fonction suivante dans le cycle requête-réponse. La fonction suivante est appelée lorsque l'exécution de la fonction est terminée. Comme mentionné plus haut, utilisez next() lorsque vous devez exécuter une autre fonction de rappel ou une fonction middleware.
Créez maintenant un dossier nommé "middleware" et, à l'intérieur, créez un fichier nommé "auth.js" puis entrez le code suivant.
auth.js
const userModel = require('../models/userModel');
const jwt = require('jsonwebtoken');
const isAuthenticated = async (req,res,next)=>{
try {
const {token} = req.cookies;
if(!token){
return next('Veuillez vous connecter pour accéder aux données');
}
const verify = await jwt.verify(token,process.env.SECRET_KEY);
req.user = await userModel.findById(verify.id);
next();
} catch (error) {
return next(error);
}
}
module.exports = isAuthenticated;
Installez maintenant la librairie cookie-parser pour configurer cookieParser dans votre application. cookieParser vous permet d'accéder au jeton stocké dans le cookie. Si vous n'avez pas configuré cookieParser, vous ne pourrez pas accéder aux cookies dans les entêtes de l'objet requête. Tapez la commande suivante dans le terminal pour télécharger cookie-parser :
npm i cookie-parser
Maintenant que cookieParser est installé, configurez votre application en modifiant le fichier index.js et ajoutez un middleware à la route "/user/".
fichier index.js
const cookieParser = require('cookie-parser');
const express = require('express');
const dotenv = require('dotenv');
//Configuration des fichiers dotenv avant tout autre fichier ou librairie
dotenv.config({path:'./config/config.env'});
require('./config/conn');
//Création d'une application à partir d'express
const app = express();
const route = require('./routes/userRoute');
//Utilisation d'express.json pour recevoir les requêtes en format json
app.use(express.json());
//Configuration de cookie-parser
app.use(cookieParser());
//Utilisation des routes
app.use('/api', route);
//Écoute du serveur
app.listen(process.env.PORT,()=>{
console.log(`Le serveur écoute sur le port ${process.env.PORT}`);
})
userRoute.js
//Importation des fichiers et librairies nécessaires
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const isAuthenticated = require('../middleware/auth');
//Création d'un routeur express
const route = express.Router();
//Importation du modèle userModel
const userModel = require('../models/userModel');
//Création de la route pour récupérer les données des utilisateurs
route.get('/user', isAuthenticated, async (req, res) => {
try {
const user = await userModel.find();
if (!user) {
return res.json({ message: 'Aucun utilisateur trouvé' })
}
return res.json({ user: user })
} catch (error) {
return res.json({ error: error });
}
})
module.exports = route;
La route "/user" n'est accessible que si l'utilisateur est connecté.
Vérification des API avec POSTMAN
Avant de vérifier les API, vous devez modifier le fichier "package.json". Ajoutez les lignes de code suivantes :
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"start": "node index.js",
"dev": "nodemon index.js"
},
Vous pouvez démarrer le serveur en tapant "npm start", mais il ne s'exécutera qu'une seule fois. Pour que le serveur continue de fonctionner lors de la modification des fichiers, vous aurez besoin de nodemon. Téléchargez-le en tapant dans le terminal :
npm install -g nodemon
L'indicateur -g téléchargera nodemon globalement sur votre système local. Il ne sera plus nécessaire de le télécharger pour chaque nouveau projet.
Pour lancer le serveur, tapez "npm run dev" dans le terminal. Vous devriez obtenir le résultat suivant :

Votre code est maintenant terminé et le serveur fonctionne correctement. Vous pouvez passer à Postman pour effectuer des tests.
Qu'est-ce que POSTMAN ?
POSTMAN est un outil logiciel pour la conception, la construction, le développement et les tests d'API.

Si vous n'avez pas encore téléchargé Postman, téléchargez-le depuis le site web de Postman.
Ouvrez Postman et créez une nouvelle collection nommée "nodeAPItest", puis créez trois requêtes à l'intérieur : "register", "login" et "user". Vous devriez avoir les fichiers suivants :

Lorsque vous envoyez des données JSON à "localhost:5000/api/register", vous devriez obtenir le résultat suivant :

Puisque nous créons et stockons également les jetons dans des cookies lors de l'enregistrement, vous pouvez obtenir les informations de l'utilisateur en demandant la route "localhost:5000/api/user". Vous pouvez vérifier le reste des requêtes avec Postman.
Si vous souhaitez obtenir le code complet, vous pouvez le récupérer depuis mon compte GitHub.
Conclusion
Dans ce tutoriel, nous avons appris à mettre en œuvre l'authentification avec JWT dans une API NodeJS. Nous avons également mis en place l'autorisation d'accès aux données des utilisateurs.
BON CODAGE !