107 lines
3.7 KiB
TypeScript
107 lines
3.7 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
import { getUserById } from './userStore.js';
|
|
|
|
// ============================================================
|
|
// Auth Middleware — Session-based avec vérification passwordChangedAt
|
|
// ============================================================
|
|
|
|
/**
|
|
* Middleware d'authentification.
|
|
* Vérifie que l'utilisateur est connecté ET que sa session n'a pas été
|
|
* invalidée par un changement de mot de passe.
|
|
*/
|
|
const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
|
|
const session = req.session as any;
|
|
|
|
if (!session.user || !session.user.id) {
|
|
return res.status(401).json({ error: "Non autorisé. Veuillez vous connecter." });
|
|
}
|
|
|
|
// Bloquer les APIs si changement de mot de passe requis
|
|
if (session.user.mustChangePassword && req.path !== '/api/change-password' && req.path !== '/api/logout') {
|
|
return res.status(403).json({ error: "Changement de mot de passe obligatoire.", mustChangePassword: true });
|
|
}
|
|
|
|
// Vérifier que le user existe encore et que le password n'a pas changé depuis le login
|
|
const user = getUserById(session.user.id);
|
|
if (!user) {
|
|
// User supprimé depuis le login → détruire la session
|
|
session.destroy(() => {});
|
|
return res.status(401).json({ error: "Session invalide. Veuillez vous reconnecter." });
|
|
}
|
|
|
|
// Comparer loginAt avec passwordChangedAt — si le password a changé après le login, invalider
|
|
if (session.user.loginAt && user.passwordChangedAt) {
|
|
const loginTime = new Date(session.user.loginAt).getTime();
|
|
const passwordChangeTime = new Date(user.passwordChangedAt).getTime();
|
|
if (passwordChangeTime > loginTime) {
|
|
session.destroy(() => {});
|
|
return res.status(401).json({ error: "Votre mot de passe a été modifié. Veuillez vous reconnecter." });
|
|
}
|
|
}
|
|
|
|
next();
|
|
};
|
|
|
|
/**
|
|
* Middleware admin.
|
|
* Doit être utilisé APRÈS authMiddleware (session.user déjà vérifié).
|
|
* Vérifie que l'utilisateur a le rôle admin.
|
|
*/
|
|
export const requireAdmin = (req: Request, res: Response, next: NextFunction) => {
|
|
const session = req.session as any;
|
|
|
|
if (!session.user || session.user.role !== 'admin') {
|
|
return res.status(403).json({ error: "Accès refusé. Droits administrateur requis." });
|
|
}
|
|
|
|
next();
|
|
};
|
|
|
|
/**
|
|
* Middleware de vue d'authentification.
|
|
* Identique à authMiddleware mais redirige vers /login au lieu de retourner du JSON.
|
|
*/
|
|
export const viewAuthMiddleware = (req: Request, res: Response, next: NextFunction) => {
|
|
const session = req.session as any;
|
|
|
|
if (!session.user || !session.user.id) {
|
|
return res.redirect('/login');
|
|
}
|
|
|
|
// Même vérification passwordChangedAt
|
|
const user = getUserById(session.user.id);
|
|
if (!user) {
|
|
session.destroy(() => {});
|
|
return res.redirect('/login');
|
|
}
|
|
|
|
if (session.user.loginAt && user.passwordChangedAt) {
|
|
const loginTime = new Date(session.user.loginAt).getTime();
|
|
const passwordChangeTime = new Date(user.passwordChangedAt).getTime();
|
|
if (passwordChangeTime > loginTime) {
|
|
session.destroy(() => {});
|
|
return res.redirect('/login');
|
|
}
|
|
}
|
|
|
|
next();
|
|
};
|
|
|
|
/**
|
|
* Middleware de vue exigeant le rôle admin.
|
|
* Redirige les non-admins vers /trending en déposant un cookie d'erreur.
|
|
*/
|
|
export const viewRequireAdmin = (req: Request, res: Response, next: NextFunction) => {
|
|
const session = req.session as any;
|
|
|
|
if (!session.user || session.user.role !== 'admin') {
|
|
res.cookie('authError', "Accès refusé. Droits administrateur requis.", { maxAge: 10000 });
|
|
return res.redirect('/trending');
|
|
}
|
|
|
|
next();
|
|
};
|
|
|
|
export default authMiddleware;
|