118 lines
3.2 KiB
JavaScript
118 lines
3.2 KiB
JavaScript
/**
|
|
* Middleware d'authentification JWT
|
|
*/
|
|
import jwt from 'jsonwebtoken';
|
|
import { pool } from '../../db.js';
|
|
import { AppError } from './errorHandler.js';
|
|
|
|
/**
|
|
* Vérifie le token JWT et ajoute l'utilisateur à req.user
|
|
*/
|
|
export const authenticateToken = async (req, res, next) => {
|
|
try {
|
|
// Récupérer le token depuis le header Authorization
|
|
const authHeader = req.headers.authorization;
|
|
const token = authHeader && authHeader.split(' ')[1]; // Format: "Bearer TOKEN"
|
|
|
|
if (!token) {
|
|
return next(new AppError('Token manquant. Veuillez vous authentifier.', 401));
|
|
}
|
|
|
|
// Vérifier et décoder le token
|
|
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
|
|
// Récupérer l'utilisateur depuis la base de données
|
|
const result = await pool.query(
|
|
'SELECT id, email, role, first_name, last_name, is_verified FROM users WHERE id = $1',
|
|
[decoded.userId]
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return next(new AppError('Utilisateur non trouvé', 401));
|
|
}
|
|
|
|
const user = result.rows[0];
|
|
|
|
// Vérifier que l'email est vérifié (optionnel selon vos besoins)
|
|
// if (!user.is_verified) {
|
|
// return next(new AppError('Email non vérifié. Veuillez vérifier votre email.', 403));
|
|
// }
|
|
|
|
// Ajouter l'utilisateur à la requête
|
|
req.user = {
|
|
id: user.id,
|
|
email: user.email,
|
|
role: user.role,
|
|
firstName: user.first_name,
|
|
lastName: user.last_name,
|
|
isVerified: user.is_verified,
|
|
};
|
|
|
|
next();
|
|
} catch (error) {
|
|
if (error.name === 'JsonWebTokenError') {
|
|
return next(new AppError('Token invalide', 403));
|
|
}
|
|
if (error.name === 'TokenExpiredError') {
|
|
return next(new AppError('Token expiré', 403));
|
|
}
|
|
return next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Vérifie que l'utilisateur a l'un des rôles autorisés
|
|
* @param {...string} roles - Liste des rôles autorisés
|
|
*/
|
|
export const authorizeRoles = (...roles) => {
|
|
return (req, res, next) => {
|
|
if (!req.user) {
|
|
return next(new AppError('Utilisateur non authentifié', 401));
|
|
}
|
|
|
|
if (!roles.includes(req.user.role)) {
|
|
return next(
|
|
new AppError(`Accès refusé. Rôle requis: ${roles.join(' ou ')}`, 403)
|
|
);
|
|
}
|
|
|
|
next();
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Middleware optionnel qui n'échoue pas si le token est absent
|
|
* Utile pour les routes qui peuvent fonctionner avec ou sans authentification
|
|
*/
|
|
export const optionalAuth = async (req, res, next) => {
|
|
try {
|
|
const authHeader = req.headers.authorization;
|
|
const token = authHeader && authHeader.split(' ')[1];
|
|
|
|
if (!token) {
|
|
return next(); // Continuer sans utilisateur
|
|
}
|
|
|
|
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
const result = await pool.query(
|
|
'SELECT id, email, role, first_name, last_name FROM users WHERE id = $1',
|
|
[decoded.userId]
|
|
);
|
|
|
|
if (result.rows.length > 0) {
|
|
req.user = {
|
|
id: result.rows[0].id,
|
|
email: result.rows[0].email,
|
|
role: result.rows[0].role,
|
|
firstName: result.rows[0].first_name,
|
|
lastName: result.rows[0].last_name,
|
|
};
|
|
}
|
|
|
|
next();
|
|
} catch (error) {
|
|
// En cas d'erreur, on continue quand même sans utilisateur
|
|
next();
|
|
}
|
|
};
|