Connexion via Google (OAuth 2.0)
Ajoute l'authentification Google en complément de la connexion par mot de passe. Le callback OAuth émet le même JWT que la connexion classique, donc le middleware d'authentification et ProtectedRoute restent inchangés. Backend - stratégie passport-google-oauth20 dans src/config/passport.js - routes GET /api/auth/google et /google/callback, documentées dans Swagger - un compte existant avec le même email est rattaché automatiquement au compte Google, et son email passe vérifié - les routes répondent 503 si les identifiants ne sont pas configurés, plutôt que de faire échouer le démarrage du serveur - login et changePassword ne comparent plus un hash inexistant sur les comptes sans mot de passe Base de données - password devient nullable - colonnes provider, providerId et avatarUrl, index unique sur le couple provider/providerId Frontend - page /auth/callback qui récupère le token et redirige selon le rôle - boutons Google de Connexion et Inscription branchés - boutons Facebook désactivés en attendant les identifiants Meta Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -156,6 +156,14 @@ const login = async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Compte créé via un provider externe : aucun mot de passe à comparer.
|
||||
if (!user.password) {
|
||||
const providerLabel = user.provider === "FACEBOOK" ? "Facebook" : "Google";
|
||||
return res.status(401).json({
|
||||
message: `Ce compte utilise la connexion ${providerLabel}. Utilisez le bouton « ${providerLabel} » pour vous connecter.`,
|
||||
});
|
||||
}
|
||||
|
||||
const isPasswordValid = await bcrypt.compare(password, user.password);
|
||||
|
||||
if (!isPasswordValid) {
|
||||
@@ -354,6 +362,50 @@ const verifyCode = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Fin du parcours OAuth : passport a posé l'utilisateur sur req.user, on émet
|
||||
// le même JWT que la connexion classique et on renvoie le navigateur vers le
|
||||
// front, qui récupère le token dans l'URL.
|
||||
const oauthCallback = (req, res) => {
|
||||
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:5173";
|
||||
|
||||
try {
|
||||
const user = req.user;
|
||||
|
||||
if (!user) {
|
||||
return res.redirect(
|
||||
`${frontendUrl}/auth/callback?error=${encodeURIComponent(
|
||||
"Connexion externe refusée."
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
const token = jwt.sign(
|
||||
{ id: user.id, role: user.role },
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: "24h" }
|
||||
);
|
||||
|
||||
const payload = Buffer.from(
|
||||
JSON.stringify({
|
||||
id: user.id,
|
||||
firstname: user.firstname,
|
||||
lastname: user.lastname,
|
||||
email: user.email,
|
||||
role: user.role,
|
||||
})
|
||||
).toString("base64");
|
||||
|
||||
return res.redirect(`${frontendUrl}/auth/callback?token=${token}&user=${payload}`);
|
||||
} catch (error) {
|
||||
console.error("Erreur oauthCallback:", error);
|
||||
return res.redirect(
|
||||
`${frontendUrl}/auth/callback?error=${encodeURIComponent(
|
||||
"Erreur serveur lors de la connexion externe."
|
||||
)}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const me = async (req, res) => {
|
||||
return res.json({
|
||||
user: req.user,
|
||||
@@ -415,6 +467,13 @@ const changePassword = async (req, res) => {
|
||||
|
||||
const user = await prisma.user.findUnique({ where: { id: userId } });
|
||||
|
||||
if (!user.password) {
|
||||
return res.status(400).json({
|
||||
message:
|
||||
"Ce compte utilise une connexion externe et n'a pas de mot de passe à modifier.",
|
||||
});
|
||||
}
|
||||
|
||||
const isValid = await bcrypt.compare(currentPassword, user.password);
|
||||
if (!isValid) {
|
||||
return res.status(401).json({ message: "Mot de passe actuel incorrect." });
|
||||
@@ -440,4 +499,5 @@ module.exports = {
|
||||
forgotPassword,
|
||||
changePassword,
|
||||
deleteAccount,
|
||||
oauthCallback,
|
||||
};
|
||||
Reference in New Issue
Block a user