Revue processus connexion compte avec mot de passe ou envoi de lien.
This commit is contained in:
parent
1c056db3bd
commit
7bafca1dc9
@ -336,17 +336,20 @@ exports.checkToken = async (req, res, next) =>
|
||||
}
|
||||
}
|
||||
|
||||
// Reçoit les données du formulaire de connexion avec mot de passe.
|
||||
exports.login = async (req, res, next) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
const db = require("../models/index");
|
||||
// Est-ce qu'un compte existe pour l'adresse e-mail envoyée ?
|
||||
const emailSend=tool.trimIfNotNull(req.body.email);
|
||||
const user=await db["User"].findOne({ attributes: ["id", "password", "email", "name", "status"], where: { email: emailSend } });
|
||||
if(!user)
|
||||
res.status(404).json({ errors: [txt.emailNotFound] });
|
||||
else
|
||||
{
|
||||
// Est-ce ce compte a déjà été validé par l'utilisateur ? Si non, on lui envoie un nouveau lien de validation.
|
||||
const subscription=await db["Subscription"].findOne({ attributes: ["id"], where: { UserId: user.id } });
|
||||
if(!subscription)
|
||||
{
|
||||
@ -356,30 +359,35 @@ exports.login = async (req, res, next) =>
|
||||
else
|
||||
{
|
||||
const nowTS=new Date().getTime();
|
||||
// L'utilisateur n'a-t-il pas testé de se connecter de trop nombreuses fois sans succès ?
|
||||
const countLogin=await toolFile.readJSON(config.dirTmpLogin, slugify(emailSend));
|
||||
if(countLogin && countLogin.nb >= config.maxLoginFail && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*3600*1000))
|
||||
if(countLogin && countLogin.nb >= config.maxLoginFail && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*60*1000))
|
||||
res.status(401).json({ errors: [txt.tooManyLoginFails.replace("MINUTES", config.loginFailTimeInMinutes)] });
|
||||
else
|
||||
{
|
||||
// Le mot du passe envoyé est-il cohérent avec celui de la base de données après chiffrement ?
|
||||
const valid = await bcrypt.compare(req.body.password, user.password);
|
||||
if (!valid)
|
||||
{
|
||||
res.status(401).json({ errors: [txt.badPassword] });
|
||||
// On comptabilise l'erreur :
|
||||
let newCountLogin={ nb:1, lastTime:nowTS };
|
||||
if(countLogin.nb && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*3600*1000))
|
||||
if(countLogin.nb && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*60*1000))
|
||||
newCountLogin.nb=countLogin.nb+1;
|
||||
await toolFile.createJSON(config.dirTmpLogin, slugify(emailSend), newCountLogin);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Si tout est ok, on enregistre la date de connexion + retourne un token de connexion.
|
||||
const now=new Date();
|
||||
const timeDifference=req.body.timeDifference;// permet d'actualiser en cas de déplacements/heures d'été, etc.
|
||||
const timeDifference=req.body.timeDifference;
|
||||
db["User"].update({ connectedAt: now, timeDifference: timeDifference }, { where: { id : user.id }, limit:1 });
|
||||
creaUserJson(user.id);
|
||||
// Connexion à rallonge uniquement possible pour utilisateur de base :
|
||||
let loginTime=config.tokenConnexionMinTimeInHours;
|
||||
if((req.body.keepConnected==="true") && (user.status==="user"))
|
||||
loginTime=config.tokenConnexionMaxTimeInDays;
|
||||
// si des données concernant un quiz ont été transmises, je les enregistre ici :
|
||||
// Si des données concernant un quiz ont été transmises, on les enregistre ici :
|
||||
req.body.UserId=user.id;
|
||||
if(req.body.QuestionnaireId)
|
||||
{
|
||||
@ -410,18 +418,21 @@ exports.login = async (req, res, next) =>
|
||||
}
|
||||
}
|
||||
|
||||
// Reçoit les données du formulaire de connexion avec demande de recevoir un lien de connexion.
|
||||
exports.getLoginLink = async (req, res, next) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Est-ce qu'un compte existe pour l'adresse e-mail envoyée ?
|
||||
const emailSend=tool.trimIfNotNull(req.body.email);
|
||||
const userDatas=await searchUserByEmail(emailSend);
|
||||
if(!userDatas)
|
||||
res.status(404).json({ errors: [txt.emailNotFound] });
|
||||
else if(userDatas.User.status!=="user")
|
||||
else if(userDatas.User.status!=="user") // seuls les utilisateurs de base peuvent se connecter de cette façon.
|
||||
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
||||
else
|
||||
{
|
||||
// Est-ce ce compte a déjà été validé par l'utilisateur ? Si non, on lui envoie un nouveau lien de validation.
|
||||
if(!userDatas.Subscription)
|
||||
{
|
||||
await sendValidationLink(userDatas.User);
|
||||
@ -447,7 +458,7 @@ exports.getLoginLink = async (req, res, next) =>
|
||||
mailRecipientAddress: userDatas.User.email
|
||||
}
|
||||
await toolMail.sendMail(userDatas.User.smtp, userDatas.User.email, txt.mailLoginLinkSubject, tool.replaceAll(txt.mailLoginLinkBodyTxt, mapMail), "", mailDatas);
|
||||
res.status(200).json({ message: txt.mailLoginLinkMessage+config.tokenLoginLinkTimeInHours+"." });
|
||||
res.status(200).json({ message: txt.mailLoginLinkMessage.replace("*TIMING*", config.tokenLoginLinkTimeInHours) });
|
||||
}
|
||||
}
|
||||
next();
|
||||
|
@ -30,7 +30,7 @@
|
||||
<div id="prompt" class="cardboard">
|
||||
<a href="/" title="Page d'accueil WikLerni"><img src="/themes/wikilerni/img/wikilerni-purple-2-512.png" alt="Logo WikiLerni" title="W I K I L E R N I" /></a>
|
||||
<p class="cardboard">Cultivons notre jardin !</p>
|
||||
<div id="response" class="error">Si vous voyez ce message, c'est que votre lien de connexion n'est pas valide. Vous pouvez <a href="/connexion.html">en demander un nouveau en cliquant ici</a>.</div>
|
||||
<div id="response">Si vous voyez ce message, c'est que votre lien de connexion n'est pas valide ou a expiré. Vous pouvez <a href="/connexion.html">en demander un nouveau en cliquant ici</a>.</div>
|
||||
</div>
|
||||
|
||||
<footer class="cardboard">
|
||||
|
@ -1,10 +1,9 @@
|
||||
// -- GESTION DU FORMULAIRE PERMETTANT DE SE CONNECTER
|
||||
|
||||
/// L'utilisateur peut avoir répondu à un quiz avant d'arriver sur la page de connexion
|
||||
/// Dans ce cas il faut enregistrer son résultat en même temps, une fois la connexion validée
|
||||
/// L'utilisateur peut avoir répondu à un quiz avant d'arriver sur la page de connexion.
|
||||
/// Dans ce cas il faut enregistrer son résultat en même temps, une fois la connexion validée.
|
||||
|
||||
/// Le connexion peut se faire directement ici via la saisie d'un mot de passe
|
||||
/// Ou via l'envoi d'un token par e-mail
|
||||
/// Le connexion peut se faire directement ici via la saisie d'un mot de passe ou via l'envoi d'un token par e-mail.
|
||||
|
||||
// Fichier de configuration tirés du backend :
|
||||
import { apiUrl, availableLangs, siteUrl, theme } from "../../config/instance.js";
|
||||
@ -13,17 +12,18 @@ const lang=availableLangs[0];
|
||||
import { connectionRoute, getLoginLinkRoute, userRoutes } from "../../config/users.js";
|
||||
const configTemplate = require("../../views/"+theme+"/config/"+lang+".js");
|
||||
|
||||
// Importation des fonctions utile au script :
|
||||
// Importation des fonctions utiles au script :
|
||||
import { getLocaly, removeLocaly, saveLocaly } from "./tools/clientstorage.js";
|
||||
import { addElement } from "./tools/dom.js";
|
||||
import { helloDev } from "./tools/everywhere.js";
|
||||
import { getDatasFromInputs } from "./tools/forms.js";
|
||||
import { isEmpty } from "../../tools/main";
|
||||
import { checkAnswerDatas, checkSession, getConfig, getTimeDifference, setSession } from "./tools/users.js";
|
||||
import { checkAnswerDatas, checkSession, getTimeDifference, setSession } from "./tools/users.js";
|
||||
|
||||
// Dictionnaires :
|
||||
const txt = require("../../lang/"+lang+"/general");
|
||||
const txtUsers = require("../../lang/"+lang+"/user");
|
||||
const txtServerError = require("../../lang/"+lang+"/general").serverError;
|
||||
const txtAlreadyConnected = require("../../lang/"+lang+"/user").alreadyConnected;
|
||||
const txtNeedChooseLoginWay = require("../../lang/"+lang+"/user").needChooseLoginWay;
|
||||
|
||||
// Principaux éléments du DOM manipulés :
|
||||
const myForm = document.getElementById("connection");
|
||||
@ -40,11 +40,10 @@ const initialise = async () =>
|
||||
const isConnected=await checkSession();
|
||||
if(isConnected)
|
||||
{
|
||||
saveLocaly("message", { message: txtUsers.alreadyConnected, color:"information" });// pour l'afficher sur la page suivante
|
||||
saveLocaly("message", { message: txtAlreadyConnected, color:"info" });// pour l'afficher sur la page suivante
|
||||
const user=getLocaly("user", true);
|
||||
const homePage=user.status+"HomePage";
|
||||
window.location.assign("/"+configTemplate[homePage]);
|
||||
addElement(divResponse, "p", txtUsers.alreadyConnected, "", ["information"]);// au cas où blocage redirection
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -58,7 +57,7 @@ const initialise = async () =>
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
addElement(divResponse, "p", txt.serverError, "", ["error"]);
|
||||
addElement(divResponse, "p", txtServerError, "", ["error"]);
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
@ -73,7 +72,7 @@ myForm.addEventListener("submit", function(e)
|
||||
divResponse.innerHTML="";// efface d'éventuels messages déjà affichés
|
||||
let datas=getDatasFromInputs(myForm);
|
||||
if(isEmpty(datas.password) && isEmpty(datas.getLoginLink))
|
||||
addElement(divResponse, "div", txtUsers.needChooseLoginWay, "", ["error"]);
|
||||
addElement(divResponse, "div", txtNeedChooseLoginWay, "", ["error"]);
|
||||
else
|
||||
{
|
||||
const xhr = new XMLHttpRequest();
|
||||
@ -89,21 +88,20 @@ myForm.addEventListener("submit", function(e)
|
||||
if (this.status === 200)
|
||||
{
|
||||
if(!isEmpty(response.message))
|
||||
{ // cas d'une demande de lien de connexion
|
||||
{ // cas d'une demande de lien de connexion avec succès.
|
||||
myForm.style.display="none";
|
||||
addElement(divResponse, "p", response.message, "", ["success"]);
|
||||
}
|
||||
else if(!isEmpty(response.userId) && !isEmpty(response.connexionTime) && !isEmpty(response.token))
|
||||
{ // cas d'une connexion directe, on créé une session de connexion et redirige l'utilisateur
|
||||
{ // cas d'une connexion via mot de passe avec succès : on crée une session de connexion et redirige l'utilisateur.
|
||||
let connexionMaxTime=Date.now();
|
||||
if(response.connexionTime.endsWith("days"))
|
||||
if(response.connexionTime.endsWith("days"))// l'utilisateur a demandé à rester connecté sur la durée.
|
||||
connexionMaxTime+=parseInt(response.connexionTime,10)*24*3600*1000;
|
||||
else
|
||||
connexionMaxTime+=parseInt(response.connexionTime,10)*3600*1000;
|
||||
setSession(response.userId, response.token, connexionMaxTime);
|
||||
removeLocaly("lastAnswer");// ! important pour ne pas enregister plusieurs fois le résultat
|
||||
removeLocaly("lastAnswer");// ! important pour ne pas enregister plusieurs fois son éventuel résultat au quiz.
|
||||
myForm.style.display="none";
|
||||
//addElement(divResponse, "p", txtUsers.connectionOk, "", ["success"]);// au cas où blocage redirection
|
||||
// l'utilisateur peut avoir tenté d'accéder à une autre page que sa page d'accueil :
|
||||
let url=getLocaly("url", true);
|
||||
if(!isEmpty(url) && url.href.indexOf(siteUrl)!==-1)
|
||||
@ -112,29 +110,26 @@ myForm.addEventListener("submit", function(e)
|
||||
removeLocaly("url");
|
||||
}
|
||||
else
|
||||
url=configTemplate[response.status+"HomePage"]
|
||||
url=configTemplate[response.status+"HomePage"];
|
||||
window.location.assign(url);
|
||||
}
|
||||
else
|
||||
addElement(divResponse, "p", txt.serverError, "", ["error"]);
|
||||
addElement(divResponse, "p", txtServerError, "", ["error"]);
|
||||
}
|
||||
else if (response.errors)
|
||||
{
|
||||
if(Array.isArray(response.errors))
|
||||
response.errors = response.errors.join("<br>");
|
||||
else
|
||||
response.errors = txt.serverError;
|
||||
response.errors = response.errors.join("<br>");
|
||||
addElement(divResponse, "p", response.errors, "", ["error"]);
|
||||
}
|
||||
else
|
||||
addElement(divResponse, "p", txt.serverError, "", ["error"]);
|
||||
addElement(divResponse, "p", txtServerError, "", ["error"]);
|
||||
}
|
||||
}
|
||||
xhr.setRequestHeader("Content-Type", "application/json");
|
||||
if(datas)
|
||||
{
|
||||
datas.timeDifference=getTimeDifference();
|
||||
// si l'utilisateur a précédement répondu à un quiz, j'ajoute les infos de son résultat :
|
||||
// Si l'utilisateur a répondu à un quiz, j'ajoute les infos de son résultat aux données envoyées :
|
||||
datas=checkAnswerDatas(datas);
|
||||
xhr.send(JSON.stringify(datas));
|
||||
}
|
||||
@ -142,7 +137,7 @@ myForm.addEventListener("submit", function(e)
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
addElement(divResponse, "p", txt.serverError, "", ["error"]);
|
||||
addElement(divResponse, "p", txtServerError, "", ["error"]);
|
||||
console.error(e);
|
||||
}
|
||||
});
|
@ -15,7 +15,7 @@ import { getLocaly, removeLocaly, saveLocaly } from "./tools/clientstorage.js";
|
||||
import { addElement } from "./tools/dom.js";
|
||||
import { helloDev } from "./tools/everywhere.js";
|
||||
import { getDatasFromInputs, setAttributesToInputs } from "./tools/forms.js";
|
||||
import { checkAnswerDatas, checkSession, getConfig, getPassword, getTimeDifference } from "./tools/users.js";
|
||||
import { checkAnswerDatas, checkSession, getPassword, getTimeDifference } from "./tools/users.js";
|
||||
|
||||
// Dictionnaires :
|
||||
const txtServerError = require("../../lang/"+lang+"/general").serverError;
|
||||
|
@ -44,14 +44,14 @@ module.exports =
|
||||
needBeConnected: "Vous devez être connecté pour accéder à cette page.",
|
||||
connectionOk: "Connexion réussie.",
|
||||
needChooseLoginWay: "Vous devez soit saisir votre mot de passe, soit cocher la case vous permettant de recevoir un lien de connexion par e-mail.",
|
||||
needValidationToLogin : "Vous devez d'abord valider votre compte avant de vous connecter. Pour ce faire, un lien vient de vous être envoyé par e-mail.",
|
||||
needValidationToLogin : "Vous devez d'abord valider votre compte avant de pouvoir vous connecter. Pour ce faire, un nouveau lien vient de vous être envoyé par e-mail.",
|
||||
tooManyLoginFails : "Désolé mais il y a eu trop de tentatives de connexion infructueuses pour cette adresse e-mail. Vous devez attendre MINUTES minutes pour essayer de nouveau.",
|
||||
badPassword: "Aucun compte utilisateur ne correspond aux informations saisies.",
|
||||
mailLoginLinkSubject : "Votre lien de connexion.",
|
||||
mailLoginLinkTxt : "Me connecter.",
|
||||
mailLoginLinkBodyTxt : "Bonjour USER_NAME,\n\nPour vous connecter à votre compte, cliquez sur le lien suivant sans tarder :\nLINK_URL",
|
||||
mailLoginLinkBodyHTML : "<h3>Bonjour USER_NAME,</h3><p>Pour vous connecter à votre compte, cliquez sur le lien suivant sans tarder :</p>",
|
||||
mailLoginLinkMessage : "Un lien de connexion vient de vous être envoyé sur votre adresse e-mail. Ne tardez pas à l'utiliser, car il n'est valable que durant ",
|
||||
mailLoginLinkMessage : "Un lien de connexion vient de vous être envoyé sur votre adresse e-mail. Ne tardez pas à l'utiliser, car il n'est valable que durant *TIMING* !",
|
||||
updatedOkMessage: "Vos informations ont bien été mises à jour.",
|
||||
updatedNeedGoodEmail : "Mais la nouvelle adresse e-mail n'a pu être enregistrée, car elle n'a pas un format correct.",
|
||||
updatedNeedUniqueEmail : "Mais la nouvelle adresse e-mail saisie (NEW_EMAIL) n'a pu être enregistrée, car elle est déjà utilisée pour un autre compte.",
|
||||
|
Loading…
Reference in New Issue
Block a user