Module d'Authentification & Sécurité (Bcrypt / JWT)

Retour à l'organigramme
SQLite / backend/server.js

Structure de la table "Comptes"

  • Intégrité des données : Le champ mail possède une contrainte UNIQUE stricte pour prévenir les conflits d'inscription.
  • Sécurité cryptographique : Le champ mot_de_passe est conçu pour stocker l'empreinte générée par Bcrypt, et non la chaîne de caractères brute.
  • Contrôle d'accès (RBAC) : Le champ role détermine le niveau de privilège de l'entité (etudiant, enseignant, admin).
/* Initialisation SQLite (server.js) */
CREATE TABLE IF NOT EXISTS Comptes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    nom TEXT NOT NULL,
    prenom TEXT NOT NULL,
    mail TEXT UNIQUE NOT NULL,
    mot_de_passe TEXT NOT NULL,
    role TEXT NOT NULL,
    classe TEXT
);
Node.js / backend/server.js

Traitement de l'Inscription (Registration)

  • Vérification de redondance : L'API interroge la base de données pour confirmer la disponibilité de l'adresse e-mail avant tout traitement.
  • Hachage : Utilisation de bcrypt.hash() avec un facteur de coût (salt rounds) fixé à 10 pour générer une empreinte sécurisée.
  • Injection SQL évitée : L'insertion des données s'effectue via une requête préparée avec des paramètres liés (paramètres ?).
app.post('/api/register', async (req, res) => {
    const { nom, prenom, mail, password, classe } = req.body;
    
    // Vérification de l'existence de l'email
    const existing = await db.all('SELECT * FROM Comptes WHERE mail = ?', [mail]);
    if (existing.length > 0) return res.status(400).json({ message: "Email déjà utilisé" });

    // Hachage du mot de passe
    const hashedPassword = await bcrypt.hash(password, 10);

    // Insertion sécurisée
    await db.run(
        'INSERT INTO Comptes (nom, prenom, mail, mot_de_passe, role, classe) VALUES (?, ?, ?, ?, ?, ?)',
        [nom, prenom, mail, hashedPassword, 'etudiant', classe || 'MMI-A1']
    );
    res.status(201).json({ message: "Compte créé" });
});
Node.js / backend/server.js

Authentification (Login API)

  • Recherche d'identité : Récupération de l'enregistrement utilisateur basé sur le champ mail.
  • Validation d'empreinte : Comparaison du mot de passe en clair avec le hash stocké via bcrypt.compare().
  • Émission du Token : Génération d'un JSON Web Token (JWT) signé symétriquement avec SECRET_KEY, contenant le role et l'id (Payload), avec une validité définie (2 heures).
app.post('/api/login', async (req, res) => {
    const { mail, password } = req.body;
    
    // Identification
    const users = await db.all('SELECT * FROM Comptes WHERE mail = ?', [mail]);
    if (users.length === 0) return res.status(401).json({ message: "Identifiants incorrects" });

    // Validation cryptographique
    const isValid = await bcrypt.compare(password, users[0].mot_de_passe);
    if (!isValid) return res.status(401).json({ message: "Identifiants incorrects" });

    // Création du JWT
    const token = jwt.sign(
        { id: users[0].id, role: users[0].role, classe: users[0].classe }, 
        SECRET_KEY, 
        { expiresIn: '2h' }
    );
    res.json({ token, role: users[0].role, nom: users[0].nom, prenom: users[0].prenom });
});
React / frontend/src/App.jsx

Persistance côté Client

  • Mise à jour d'état (State) : Hydratation des variables React (token, role) pour mettre à jour l'interface de manière réactive.
  • Stockage local (LocalStorage) : Enregistrement du JWT et des métadonnées dans le navigateur du client. Cela garantit le maintien de la session même après le rafraîchissement ou la fermeture de la page.
const saveAuthData = (data) => {
    // 1. Mise à jour de l'état local (Interface réactive)
    setToken(data.token); 
    setRole(data.role); 
    setPrenomUser(data.prenom);
    setUserClasse(data.classe);
    
    // 2. Persistance dans le navigateur
    localStorage.setItem('jwtToken', data.token); 
    localStorage.setItem('userRole', data.role); 
    localStorage.setItem('userPrenom', data.prenom);
    localStorage.setItem('userClasse', data.classe); 
};
Node.js / backend/server.js

Sécurisation des Routes API (Middleware)

  • Interception : Fonction exécutée avant chaque route protégée exigeant une authentification.
  • Extraction : Analyse de l'en-tête HTTP Authorization: Bearer <token>.
  • Vérification d'intégrité : L'appel à jwt.verify() s'assure que le token n'est pas expiré et que la signature correspond au serveur.
  • Délégation : Injection de l'objet user dans la requête (req) pour exploitation par le contrôleur final.
const verifierToken = (req, res, next) => {
    // Extraction du format "Bearer [TOKEN]"
    const authHeader = req.headers['authorization'];
    const token = authHeader && authHeader.split(' ')[1];
    
    if (!token) return res.status(401).json({ message: "Accès refusé" });

    // Vérification de la validité cryptographique
    jwt.verify(token, SECRET_KEY, (err, user) => {
        if (err) return res.status(403).json({ message: "Token invalide" });
        
        req.user = user; // Injection des données pour la route suivante
        next(); // Autorisation de passage
    });
};