From: Benjamin Auder Date: Fri, 22 Mar 2019 18:19:09 +0000 (+0100) Subject: Fix login/register system X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/css/img/doc/pieces/cp.svg?a=commitdiff_plain;h=1aeed627be63a298d3a093797c3728e3de30b464;p=vchess.git Fix login/register system --- diff --git a/client/src/App.vue b/client/src/App.vue index 4b8e447c..c5008f2e 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -26,7 +26,7 @@ | {{ st.tr["Problems"] }} #rightMenu .clickable(onClick="doClick('modalUser')") - | {{ !st.user.id ? "Login" : "Update" }} + | {{ st.user.id > 0 ? "Update" : "Login" }} .clickable(onClick="doClick('modalSettings')") | {{ st.tr["Settings"] }} .clickable#flagContainer(onClick="doClick('modalLang')") diff --git a/client/src/components/UpsertUser.vue b/client/src/components/UpsertUser.vue index 86f8558d..7b92cf9a 100644 --- a/client/src/components/UpsertUser.vue +++ b/client/src/components/UpsertUser.vue @@ -12,22 +12,22 @@ div label(for="username") Name input#username(type="text" v-model="user.name") fieldset - - + label(for="useremail") Email + input#useremail(type="email" v-model="user.email") fieldset - - + label(for="notifyNew") Notify new moves & games + input#notifyNew(type="checkbox" v-model="user.notify") div(v-show="stage=='Login'") fieldset - - + label(for="nameOrEmail") Name or Email + input#nameOrEmail(type="text" v-model="nameOrEmail") .button-group - button#submit(@click="onSubmit()") + button#submit(type="button" @click="onSubmit()") span {{ submitMessage }} i.material-icons send button(v-if="stage!='Update'" @click="toggleStage()") span {{ stage=="Login" ? "Register" : "Login" }} - button(v-if="stage=='Update'" onClick="location.replace('/logout')") + button(v-else onClick="location.replace('/logout')") span Logout #dialog(:style="{display: displayInfo}") {{ infoMsg }} @@ -40,9 +40,9 @@ export default { name: 'my-upsert-user', data: function() { return { - user: store.state.user, //initialized with global user object + user: store.state.user, nameOrEmail: "", //for login - stage: (!store.state.user.id ? "Login" : "Update"), + stage: (store.state.user.id > 0 ? "Update" : "Login"), //TODO? infoMsg: "", enterTime: Number.MAX_SAFE_INTEGER, //for a basic anti-bot strategy }; @@ -124,25 +124,17 @@ export default { ajax(this.ajaxUrl(), this.ajaxMethod(), this.stage == "Login" ? { nameOrEmail: this.nameOrEmail } : this.user, res => { - - console.log("receive login infos"); - console.log(res); - this.infoMsg = this.infoMessage(); if (this.stage != "Update") { this.nameOrEmail = ""; this.user["email"] = ""; - this.user["name"] = ""; - - debugger; //TODO: 2 passages ici au lieu d'1 lors du register - + // Update global object + this.user["name"] = res.name; + this.user["id"] = res.id; // Store our identifiers in local storage (by little anticipation...) localStorage["myid"] = res.id; localStorage["myname"] = res.name; - // Also in global object - this.st.user.id = res.id; - this.st.user.name = res.name; } setTimeout(() => { this.infoMsg = ""; diff --git a/client/src/router.js b/client/src/router.js index 82f01009..2030397a 100644 --- a/client/src/router.js +++ b/client/src/router.js @@ -8,6 +8,9 @@ function loadView(view) { return () => import(/* webpackChunkName: "view-[request]" */ `@/views/${view}.vue`) } +import { ajax } from "@/utils/ajax"; +import { store } from "@/store"; + export default new Router({ routes: [ { @@ -25,6 +28,39 @@ export default new Router({ name: "rules", component: loadView("Rules"), }, + { + path: "/authenticate/:token", + name: "authenticate", + beforeEnter: (to, from, next) => { + ajax( + "/authenticate", + "GET", + {token: to.params["token"]}, + (res) => { + store.state.user.id = res.id; + store.state.user.name = res.name; + } + ); + next(); + }, + redirect: "/", + }, + { + path: "/logout", + name: "logout", + beforeEnter: (to, from, next) => { + ajax( + "/logout", + "GET", + () => { + store.state.user.id = 0; + store.state.user.name = ""; //TODO: localStorage myId myname mysid ? + } + ); + next(); + }, + redirect: "/", + }, // { // path: "/about", // name: "about", diff --git a/client/src/utils/ajax.js b/client/src/utils/ajax.js index 46edca06..0a50a104 100644 --- a/client/src/utils/ajax.js +++ b/client/src/utils/ajax.js @@ -26,16 +26,17 @@ export function ajax(url, method, data, success, error) xhr.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { + let res_json = ""; try { - let res_json = JSON.parse(xhr.responseText); - if (!res_json.errmsg) - success(res_json); - else - error(res_json.errmsg); - } catch (e) { + res_json = JSON.parse(xhr.responseText); + } catch (e) { // Plain text (e.g. for rules retrieval) - success(xhr.responseText); - } + return success(xhr.responseText); + } + if (!res_json.errmsg) + success(res_json); + else + error(res_json.errmsg); } }; @@ -46,7 +47,10 @@ export function ajax(url, method, data, success, error) } xhr.open(method, params.serverUrl + url, true); xhr.setRequestHeader('X-Requested-With', "XMLHttpRequest"); - if (["POST","PUT"].includes(method)) + // Next line because logout and authenticate set (cross-domain in dev mode) cookies + if (url.startsWith("/authenticate") || url.startsWith("/logout")) + xhr.withCredentials = true; + if (["POST","PUT"].includes(method)) { xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xhr.send(JSON.stringify(data)); diff --git a/server/app.js b/server/app.js index 13cf1dbf..f97d925b 100644 --- a/server/app.js +++ b/server/app.js @@ -35,7 +35,8 @@ if (params.cors.enable) { app.use(function(req, res, next) { res.header("Access-Control-Allow-Origin", params.cors.allowedOrigin); - res.header("Access-Control-Allow-Headers", + res.header("Access-Control-Allow-Credentials", true); //for cookies + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE"); next(); diff --git a/server/config/parameters.js.dist b/server/config/parameters.js.dist index 14c100a2..5b4301a6 100644 --- a/server/config/parameters.js.dist +++ b/server/config/parameters.js.dist @@ -9,7 +9,7 @@ module.exports = // CORS: cross-origin resource sharing options cors: { enable: true, - allowedOrigin: "*", + allowedOrigin: "http://localhost:8080", }, // Lifespan of a (login) cookie diff --git a/server/routes/users.js b/server/routes/users.js index 1d9b0423..ebbfa1e6 100644 --- a/server/routes/users.js +++ b/server/routes/users.js @@ -18,13 +18,9 @@ function setAndSendLoginToken(subject, to, res) const body = "Hello " + to.name + "!\n" + "Access your account here: " + - params.siteURL + "/authenticate?token=" + token + "\\n" + + params.siteURL + "/#/authenticate/" + token + "\\n" + "Token will expire in " + params.token.expire/(1000*60) + " minutes." sendEmail(params.mail.noreply, to.email, subject, body, err => { - - console.log("send login infos ::"); - console.log(to); - // "id" is generally the only info missing on client side, // but the name is also unknown if log-in with the email. res.json(err || {id: to.id, name: to.name}); @@ -64,8 +60,8 @@ router.get('/sendtoken', access.unlogged, access.ajax, (req,res) => { }); }); -router.get('/authenticate', access.unlogged, (req,res) => { - UserModel.getOne("loginToken", req.query.token, (err,user) => { +router.get('/authenticate', access.unlogged, access.ajax, (req,res) => { + UserModel.getOne("loginToken", req.query.token, (err,user) => { access.checkRequest(res, err, user, "Invalid token", () => { // If token older than params.tokenExpire, do nothing if (Date.now() > user.loginTime + params.token.expire) @@ -80,7 +76,7 @@ router.get('/authenticate', access.unlogged, (req,res) => { secure: !!params.siteURL.match(/^https/), maxAge: params.cookieExpire, }); - res.redirect("/"); + res.json({name:user.name, id:user.id}); }); }); }); @@ -103,10 +99,9 @@ router.put('/update', access.logged, access.ajax, (req,res) => { }); }); -// Logout on server because the token cookie is httpOnly -router.get('/logout', access.logged, (req,res) => { +router.get('/logout', access.logged, access.ajax, (req,res) => { res.clearCookie("token"); - res.redirect('/'); + res.json({}); }); module.exports = router;