const express = require("express"); const { register, login, verifyEmail, verifyCode, forgotPassword, sendPasswordSetupLink, resetPassword, me, changePassword, setInitialPassword, deleteAccount, oauthCallback } = require("../controllers/auth.controller"); const authMiddleware = require("../middlewares/auth.middleware"); const validate = require("../middlewares/validate.middleware"); const verifyCaptcha = require("../middlewares/captcha.middleware"); const { registerSchema, loginSchema } = require("../validations/auth.validation"); const { passport, googleEnabled, facebookEnabled } = require("../config/passport"); const getFrontendUrl = require("../utils/frontendUrl"); const router = express.Router(); /** * @swagger * /api/auth/register: * post: * summary: Inscription d'un utilisateur * tags: * - Auth * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * firstname: * type: string * lastname: * type: string * email: * type: string * password: * type: string * responses: * 201: * description: Utilisateur créé */ router.post("/register", verifyCaptcha, validate(registerSchema), register); /** * @swagger * /api/auth/login: * post: * summary: Connexion utilisateur * tags: * - Auth * requestBody: * required: true * responses: * 200: * description: Connexion réussie */ router.post("/login", verifyCaptcha, validate(loginSchema), login); /** * @swagger * /api/auth/me: * get: * summary: Informations de l'utilisateur connecté * tags: * - Auth * security: * - bearerAuth: [] * responses: * 200: * description: Profil utilisateur */ router.get("/me", authMiddleware, me); /** * @swagger * /api/auth/verify-email/{token}: * get: * summary: Vérification de l'adresse email * tags: * - Auth * parameters: * - in: path * name: token * required: true * schema: * type: string * description: Token de validation envoyé par email * responses: * 200: * description: Email vérifié avec succès. * 400: * description: Lien de validation invalide ou expiré. * 500: * description: Erreur serveur. */ router.get("/verify-email/:token",verifyEmail); /** * @swagger * /api/auth/forgot-password: * post: * summary: Demande de réinitialisation du mot de passe * tags: * - Auth * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - email * properties: * email: * type: string * format: email * example: jean.dupont@gmail.com * captchaToken: * type: string * description: Jeton Cloudflare Turnstile (exigé si TURNSTILE_SECRET_KEY est configurée) * responses: * 200: * description: Si un compte existe avec cet email, un lien de réinitialisation a été envoyé. * 400: * description: Email requis, ou vérification anti-robot manquante. * 500: * description: Erreur serveur. */ // Page publique : le captcha empêche d'envoyer des emails en masse vers // n'importe quelle adresse. router.post("/forgot-password", verifyCaptcha, forgotPassword); /** * @swagger * /api/auth/password-setup-link: * post: * summary: Envoie à l'utilisateur connecté un lien pour définir son mot de passe * tags: * - Auth * security: * - bearerAuth: [] * responses: * 200: * description: Lien envoyé à l'adresse du compte. */ router.post("/password-setup-link", authMiddleware, sendPasswordSetupLink); /** * @swagger * /api/auth/reset-password/{token}: * post: * summary: Réinitialisation du mot de passe * tags: * - Auth * parameters: * - in: path * name: token * required: true * schema: * type: string * description: Token de réinitialisation reçu par email * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - password * properties: * password: * type: string * format: password * example: NouveauPassword123 * responses: * 200: * description: Mot de passe réinitialisé avec succès. * 400: * description: Lien invalide, expiré ou mot de passe invalide. * 500: * description: Erreur serveur. */ router.post("/reset-password/:token", resetPassword); router.post("/verify-code", verifyCode); router.patch("/change-password", authMiddleware, changePassword); /** * @swagger * /api/auth/initial-password: * post: * summary: Remplace le mot de passe provisoire d'un employé à sa première connexion * tags: * - Auth * security: * - bearerAuth: [] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - password * - confirmPassword * properties: * password: * type: string * confirmPassword: * type: string * responses: * 200: * description: Mot de passe enregistré, l'accès complet est rétabli. * 400: * description: Mot de passe invalide, non confirmé, identique au provisoire ou déjà défini. */ router.post("/initial-password", authMiddleware, setInitialPassword); router.delete("/account", authMiddleware, deleteAccount); /** * @swagger * /api/auth/google: * get: * summary: Démarre la connexion via Google (redirection OAuth 2.0) * tags: * - Auth * responses: * 302: * description: Redirection vers l'écran de consentement Google. * 503: * description: Connexion Google non configurée sur ce serveur. */ /** * @swagger * /api/auth/google/callback: * get: * summary: Retour de Google, émet le JWT et redirige vers le front * tags: * - Auth * responses: * 302: * description: Redirection vers FRONTEND_URL/auth/callback avec le token. */ /** * @swagger * /api/auth/facebook: * get: * summary: Démarre la connexion via Facebook (redirection OAuth 2.0) * tags: * - Auth * responses: * 302: * description: Redirection vers l'écran de consentement Facebook. * 503: * description: Connexion Facebook non configurée sur ce serveur. */ /** * @swagger * /api/auth/facebook/callback: * get: * summary: Retour de Facebook, émet le JWT et redirige vers le front * tags: * - Auth * responses: * 302: * description: Redirection vers FRONTEND_URL/auth/callback avec le token. */ function mountOAuthRoutes({ path, strategy, label, enabled, scope }) { if (!enabled) { // Sans identifiants configurés, on répond explicitement plutôt que de // laisser passport planter au démarrage. const notConfigured = (req, res) => res.status(503).json({ message: `La connexion ${label} n'est pas configurée sur ce serveur.` }); router.get(path, notConfigured); router.get(`${path}/callback`, notConfigured); return; } router.get(path, passport.authenticate(strategy, { scope, session: false })); router.get( `${path}/callback`, passport.authenticate(strategy, { session: false, failureRedirect: `${getFrontendUrl()}/auth/callback?error=${encodeURIComponent( `Connexion ${label} refusée.` )}`, }), oauthCallback ); } mountOAuthRoutes({ path: "/google", strategy: "google", label: "Google", enabled: googleEnabled, scope: ["profile", "email"], }); mountOAuthRoutes({ path: "/facebook", strategy: "facebook", label: "Facebook", enabled: facebookEnabled, scope: ["email"], }); module.exports = router;