Skip to main content

Module 3 : ImplĂ©mentation de l'authentication dans notre projet đŸ’»

Pour ajouter l'authentification à votre projet Express, nous allons suivre plusieurs étapes. Nous allons créer un systÚme d'authentification basé sur les tokens JWT (JSON Web Tokens). Voici un plan détaillé pour implémenter cette fonctionnalité :

Étape 1: PrĂ©paration de la base de donnĂ©es​

  1. Créer une table users dans la base de données:
  • Cette table stockera les informations des utilisateurs, y compris leur email et mot de passe (hashĂ©).
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Étape 2: Installation des dĂ©pendances nĂ©cessaires​

  1. Installer les packages nécessaires:
  • bcryptjs pour hasher les mots de passe.
  • jsonwebtoken pour gĂ©nĂ©rer et vĂ©rifier les tokens JWT.
npm install bcryptjs jsonwebtoken

Étape 3: CrĂ©ation des modĂšles​

  1. Créer un modÚle UserManager.js:
  • Ce fichier contiendra les fonctions pour interagir avec la table users.
const createConnection = require('./../../db');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

const createUser = async (userData) => {
try {
const mysqlConnection = await createConnection();
const hashedPassword = await bcrypt.hash(userData.password, 10);

const [result] = await mysqlConnection.query(
`INSERT INTO users (email, password) VALUES (?, ?)`,
[userData.email, hashedPassword]
);

await mysqlConnection.end();

if (result.insertId) {
return await getUserById(result.insertId);
}

return null;
} catch (err) {
console.error(err);
throw new Error('Erreur serveur');
}
};

const getUserById = async (id) => {
try {
const mysqlConnection = await createConnection();
const [results] = await mysqlConnection.query(
`SELECT * FROM users WHERE id = ?`,
[id]
);

await mysqlConnection.end();

return results[0] || null;
} catch (err) {
console.error(err);
throw new Error('Erreur serveur');
}
};

const getUserByEmail = async (email) => {
try {
const mysqlConnection = await createConnection();
const [results] = await mysqlConnection.query(
`SELECT * FROM users WHERE email = ?`,
[email]
);

await mysqlConnection.end();

return results[0] || null;
} catch (err) {
console.error(err);
throw new Error('Erreur serveur');
}
};

const generateToken = (user) => {
return jwt.sign({ id: user.id, email: user.email }, 'votre_secret_key', { expiresIn: '1h' });
};

module.exports = {
createUser,
getUserById,
getUserByEmail,
generateToken
};

Étape 4: CrĂ©ation des contrĂŽleurs​

  1. Créer un contrÎleur AuthController.js:
  • Ce fichier contiendra les routes pour l'inscription et la connexion des utilisateurs.
const express = require('express');
const app = express();
const { createUser, getUserByEmail, generateToken } = require('./../model/UserManager');
const bcrypt = require('bcryptjs');

// Route pour l'inscription
app.post('/register', async (req, res) => {
try {
const user = await createUser(req.body);
const token = generateToken(user);

res.status(201).json({ user, token });
} catch (err) {
res.status(500).json({ error: err.message });
}
});

// Route pour la connexion
app.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = await getUserByEmail(email);

if (!user) {
return res.status(400).json({ error: 'Utilisateur non trouvé' });
}

const isMatch = await bcrypt.compare(password, user.password);

if (!isMatch) {
return res.status(400).json({ error: 'Mot de passe incorrect' });
}

const token = generateToken(user);

res.json({ user, token });
} catch (err) {
res.status(500).json({ error: err.message });
}
});

module.exports = app;

Étape 5: IntĂ©gration dans l'application principale​

  1. Modifier index.js pour inclure les routes d'authentification:
const express = require('express');
const app = express();
const port = 3555;
const data = require('./data.json');
const BookController = require('./src/controller/BookController');
const AuthController = require('./src/controller/AuthController');

// Middleware pour parser le JSON
app.use(express.json());

// Routes pour les livres
app.use('/api/books', BookController);

// Routes pour l'authentification
app.use('/api/auth', AuthController);

// Démarrer le serveur
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});

Étape 6: Middleware d'authentification​

  1. Créer un middleware pour vérifier les tokens JWT:
  • Ce middleware sera utilisĂ© pour protĂ©ger les routes qui nĂ©cessitent une authentification.
const jwt = require('jsonwebtoken');

const authMiddleware = (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');

if (!token) {
return res.status(401).json({ error: 'AccÚs non autorisé' });
}

try {
const decoded = jwt.verify(token, 'votre_secret_key');
req.user = decoded;
next();
} catch (err) {
res.status(401).json({ error: 'Token invalide' });
}
};

module.exports = authMiddleware;
  1. Utiliser le middleware dans les routes protégées:
  • Par exemple, pour protĂ©ger la route de suppression d'un livre.
const authMiddleware = require('./../middleware/authMiddleware');

app.delete('/:id', authMiddleware, async (req, res) => {
const id = req.params.id;
const results = await deleteBook(id);

res.json(results);
});

Étape 7: GĂ©nĂ©ration d'une clĂ© secrete pour renforcer la gĂ©nĂ©ration du JWT​

Dans le terminal, on entre les lignes suivantes une par une :

node
require('crypto').randomBytes(64).toString('hex')

Étape 8: Utilisation de variable d'environnement​

Imaginez que vous construisez une maison. Vous ne laisseriez pas vos clĂ©s sur la porte, n'est-ce pas ? De la mĂȘme maniĂšre, quand vous dĂ©veloppez une application, vous ne voulez pas exposer vos informations sensibles directement dans votre code. C'est lĂ  qu'intervient le fichier .env.

Qu'est-ce qu'un fichier .env ?

Un fichier .env (pour "environment", ou environnement en français) est un fichier spécial qui agit comme un coffre-fort pour vos secrets. Il contient des variables d'environnement sous forme de paires clé-valeur. Nous allons donc créer un fichier .env à la racine de notre application et y ajouter la clé secrete que nous venons de générer, nous allons au passage rajouter les identifiants de la base de donnée

.env
JWT_SECRET=7298d8352743a94a5b04ecf0493e87af24058c74d677690e3dcbe1c2101ee15391b251c6496f7711cd7fb679ee1f392dd40d47d41e5a5f3245201470ecb9cffe
DATABASE_NAME=bibliotheque
DATABASE_PASSWORD=rootpassword
DATABASE_USER=root

Pour pouvoir utiliser ces valeurs dans notre application nous allons devoir créer un autre fichier que l'on nommera constants.js. Nous allons créer un dossier /config et y placer à l'intérieur ce fichier :

constants.js
// config/constants.js
require('dotenv').config();

module.exports = {
JWT_SECRET: process.env.JWT_SECRET,
DATABASE_NAME: process.env.DATABASE_NAME,
DATABASE_PASSWORD: process.env.DATABASE_PASSWORD,
DATABASE_USER: process.env.DATABASE_USER
};

Servons-nous maintenant de ce fichier dans UserManager.js, AuthMiddleware.js et db.js. Nous n'utiliserons donc plus les valeurs en dur mais faire un require pour chercher ses valeurs depuis le constants.js

AuthMiddleware.js
const jwt = require('jsonwebtoken');
const { JWT_SECRET } = require('../../config/constants');

const AuthMiddleware = (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');

if (!token) {
return res.status(401).json({error: 'Unauthorized'})
}

try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded;
next();
} catch(e) {
res.status(401).json({error: 'Token invalide'})
}
}

module.exports = AuthMiddleware;
UserManager.js
const { JWT_SECRET } = require('../../config/constants');
//...

const generateToken = (user) => {
return jwt.sign({id: user.id, email: user.email}, JWT_SECRET, {expiresIn: '24h'});
}

db.js
const mysql = require('mysql2/promise');
const { DATABASE_NAME, DATABASE_PASSWORD, DATABASE_USER } = require('./config/constants');

const createConnection = async () => {
return await mysql.createConnection({
host: 'localhost',
port: '3306',
user: DATABASE_USER,
database: DATABASE_NAME,
password: DATABASE_PASSWORD
});
}

module.exports = createConnection;
warning

DĂ©sormais, toutes les valeurs sensibles devront ĂȘtre entrĂ©es de cette maniĂšre grĂące au .env dans l'application, et non plus en dur dans le code comme auparavant

Étape 8: Tester l'authentification​

  1. Tester les routes d'authentification:
  • Utilisez des outils comme Postman pour tester les routes /api/auth/register et /api/auth/login.
  • Assurez-vous que les routes protĂ©gĂ©es nĂ©cessitent un token valide.

Conclusion​

Vous avez maintenant un systÚme d'authentification basé sur JWT intégré à votre application Express. Les utilisateurs peuvent s'inscrire, se connecter, et accéder à des routes protégées en fournissant un token valide. Vous pouvez étendre ce systÚme en ajoutant des rÎles d'utilisateurs, des permissions, etc.