Checkpoint: Ajout d'une page de connexion locale permettant aux utilisateurs de se connecter avec leur identifiant et mot de passe (username/password) au lieu de l'OAuth Manus. Implémentation complète avec route d'authentification backend, hashage bcrypt, session JWT, et reconnaissance automatique des sessions locales vs OAuth. Tests unitaires et manuels réussis avec l'utilisateur adminServFormation.
This commit is contained in:
108
server/_core/localAuth.ts
Normal file
108
server/_core/localAuth.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { Router } from "express";
|
||||
import bcrypt from "bcryptjs";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { getDb } from "../db";
|
||||
import { users } from "../../drizzle/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { ENV } from "./env";
|
||||
import { COOKIE_NAME } from "@shared/const";
|
||||
import { getSessionCookieOptions } from "./cookies";
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* Route d'authentification locale avec username/password
|
||||
* POST /api/auth/local/login
|
||||
* Body: { username: string, password: string }
|
||||
*/
|
||||
router.post("/login", async (req, res) => {
|
||||
try {
|
||||
const { username, password } = req.body;
|
||||
|
||||
// Validation des champs
|
||||
if (!username || !password) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Identifiant et mot de passe requis",
|
||||
});
|
||||
}
|
||||
|
||||
// Récupérer l'utilisateur par username
|
||||
const db = await getDb();
|
||||
if (!db) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Erreur de connexion à la base de données",
|
||||
});
|
||||
}
|
||||
|
||||
const result = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username))
|
||||
.limit(1);
|
||||
|
||||
if (result.length === 0) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: "Identifiant ou mot de passe incorrect",
|
||||
});
|
||||
}
|
||||
|
||||
const user = result[0];
|
||||
|
||||
// Vérifier que l'utilisateur a un mot de passe défini
|
||||
if (!user.password) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: "Cet utilisateur n'a pas de mot de passe défini",
|
||||
});
|
||||
}
|
||||
|
||||
// Comparer le mot de passe
|
||||
const isPasswordValid = await bcrypt.compare(password, user.password);
|
||||
|
||||
if (!isPasswordValid) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: "Identifiant ou mot de passe incorrect",
|
||||
});
|
||||
}
|
||||
|
||||
// Mettre à jour la date de dernière connexion
|
||||
await db
|
||||
.update(users)
|
||||
.set({ lastSignedIn: new Date() })
|
||||
.where(eq(users.id, user.id));
|
||||
|
||||
// Créer le token JWT
|
||||
const token = jwt.sign(
|
||||
{
|
||||
openId: user.openId,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
},
|
||||
ENV.jwtSecret,
|
||||
{ expiresIn: "7d", algorithm: "HS256" }
|
||||
);
|
||||
|
||||
// Définir le cookie de session
|
||||
const cookieOptions = getSessionCookieOptions(req);
|
||||
res.cookie(COOKIE_NAME, token, cookieOptions);
|
||||
|
||||
// Retourner l'utilisateur (sans le mot de passe)
|
||||
const { password: _, ...userWithoutPassword } = user;
|
||||
return res.json({
|
||||
success: true,
|
||||
user: userWithoutPassword,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[LocalAuth] Error during login:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Erreur lors de la connexion",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user