Fix file paths and add password reset functionality

This commit is contained in:
newtbot 2024-01-31 03:17:24 +08:00
parent 12597ad774
commit fea986a841
12 changed files with 169 additions and 39 deletions

View File

@ -2,7 +2,7 @@ const { tokenModel } = require("../database/model/tokenModel.js");
const { userModel } = require("../database/model/userModel"); const { userModel } = require("../database/model/userModel");
const { hash, compareHash } = require("./bcrypt.js"); const { hash, compareHash } = require("./bcrypt.js");
const { generateUUID } = require("./generateUUID.js"); const { generateUUID } = require("./generateUUID.js");
const { isValid } = require("./isValid"); const { isValid , resetIsValid } = require("./isValid");
async function getTokenByToken(token) { async function getTokenByToken(token) {
const splitAuthToken = token.split("-"); const splitAuthToken = token.split("-");
@ -53,7 +53,7 @@ async function addToken(userId, permission, isKey ,expiry) {
async function addPasswordResetToken(data , token){ async function addPasswordResetToken(data , token){
let hashtoken = await hash(token); let hashtoken = await hash(token);
let currentDate = new Date(); let currentDate = new Date();
let tokenToLive = new Date(currentDate.getTime() + 15 * 60000); let tokenToLive = new Date(currentDate.getTime() + 5 * 60000);
let tokenRes = await tokenModel.create({ let tokenRes = await tokenModel.create({
userid: data.id, userid: data.id,
@ -62,7 +62,7 @@ async function addPasswordResetToken(data , token){
isKey: "isNotKey", isKey: "isNotKey",
expiration: tokenToLive, expiration: tokenToLive,
}); });
return true; return tokenRes.id
} }
async function checkToken(id) { async function checkToken(id) {
@ -77,6 +77,31 @@ async function checkToken(id) {
return tokenRes; return tokenRes;
} }
async function checkTokenByrowID(token) {
if (!token) return false;
//split
const splitAuthToken = token.split("-");
const rowid = splitAuthToken[0];
const suppliedToken = splitAuthToken.slice(1).join("-");
let tokenRes = await tokenModel.findByPk(rowid);
//console.log(tokenRes);
if (!tokenRes) return false;
if (!compareHash(suppliedToken, tokenRes.token)) return false;
module.exports = { addToken, getTokenByToken , checkToken , addPasswordResetToken}; //pass tokemRes.expiration to isValid
if (!isValid(tokenRes.expiration)) {
//add boolean to token table
tokenRes.destroy();
return false;
}
return tokenRes;
}
module.exports = { addToken, getTokenByToken , checkToken , addPasswordResetToken , checkTokenByrowID};

View File

@ -11,4 +11,16 @@ function isValid(time) {
} }
module.exports = { isValid }; //5 minutes
function resetIsValid(time) {
if (
Math.floor(new Date(time).getTime() / 1000) <
Math.floor(new Date().getTime() / 1000)
) {
return false;
}
return true;
}
module.exports = { isValid , resetIsValid };

View File

@ -84,7 +84,7 @@ async function sendResetPasswordEmail(email, resetToken) {
subject: "Reset Password", subject: "Reset Password",
html: ` html: `
<h1>Reset Password</h1> <h1>Reset Password</h1>
<p><strong>Reset Password Link:</strong> <a href="localhost/api/v0/auth/resetpassword/${resetToken}">Reset Password Link </p> <p><strong>Reset Password Link:</strong> <a href="localhost/resetpassword/${resetToken}">Reset Password Link </p>
<p><strong>From:</strong> Eco Saver</p> <p><strong>From:</strong> Eco Saver</p>
<p>Kindly click on the link to reset your password!</p> <p>Kindly click on the link to reset your password!</p>
<p>Regards,</p> <p>Regards,</p>

View File

@ -163,6 +163,23 @@ async function checkEmailDetails(email) {
} }
async function resetPass(userid , data ){
let hashed = await hash(data.password);
let updateUser = await userModel.update(
{
password: hashed,
},
{
where: {
id: userid,
},
}
);
if (!updateUser) return false;
return true;
}
module.exports = { module.exports = {
getUserByID, getUserByID,
@ -171,5 +188,7 @@ module.exports = {
loginUser, loginUser,
updateProfile, updateProfile,
checkEmail, checkEmail,
checkEmailDetails checkEmailDetails,
resetPass,
}; };

View File

@ -3,13 +3,17 @@ const {
loginUser, loginUser,
checkEmail, checkEmail,
checkEmailDetails, checkEmailDetails,
resetPass,
} = require("../functions/user"); } = require("../functions/user");
const { sendContactEmail } = require("../functions/nodeMail"); const { sendContactEmail } = require("../functions/nodeMail");
const { generateUUID } = require("../functions/generateUUID"); const { generateUUID } = require("../functions/generateUUID");
const { addPasswordResetToken } = require("../functions/api"); const { addPasswordResetToken } = require("../functions/api");
const { sendResetPasswordEmail } = require("../functions/nodeMail"); const { sendResetPasswordEmail } = require("../functions/nodeMail");
const { checkTokenByrowID } = require("../functions/api");
const express = require("express"); const express = require("express");
const { render } = require("ejs");
const router = express.Router(); const router = express.Router();
// /user/register // /user/register
@ -91,13 +95,16 @@ router.post("/checkemail", async (req, res, next) => {
let data = await checkEmailDetails(req.body.email); let data = await checkEmailDetails(req.body.email);
//console.log(data); //console.log(data);
//token generation and insert into token table //token generation and insert into token table
const token = await generateUUID(); let token = await generateUUID();
let tokenRes = await addPasswordResetToken(data, token); let tokenRes = await addPasswordResetToken(data, token);
//email user with temp token link //email user with temp token link
if (!tokenRes) return false; if (!tokenRes) return false;
//apend table id to token
token = tokenRes + "-" + token;
//email logic to send reset password link //email logic to send reset password link
sendResetPasswordEmail(req.body.email, token); sendResetPasswordEmail(req.body.email, token);
@ -111,15 +118,33 @@ router.post("/checkemail", async (req, res, next) => {
} }
}); });
router.get("/resetpassword/:token", async (req, res, next) => {
//pass token to reset password page
});
//reset password //reset password
router.post("/resetpassword", async (req, res, next) => { router.post("/resetpassword/:token", async (req, res, next) => {
console.log(req.body);
console.log(req.params.token);
//if token is valid
let tokenRes = await checkTokenByrowID(req.params.token);
if (!tokenRes) {
let error = new Error("Token not found");
error.status = 400;
return next(error);
}
//token is valid and reset password
else{
let Res = await resetPass(tokenRes.userid, req.body);
if (!Res) return false;
else{
res.json({
message: "Password reset successfully",
});
tokenRes.destroy();
}
}
}); });
module.exports = router; module.exports = router;

View File

@ -1,4 +1,5 @@
"use strict"; "use strict";
const { checkTokenByrowID } = require("../functions/api");
var router = require("express").Router(); var router = require("express").Router();
@ -33,7 +34,7 @@ router.get("/forgotpassword", function (req, res, next) {
res.render("forgotpassword"); res.render("forgotpassword");
}); });
//resetted password page //resetting password page
router.get("/resetpassword", function (req, res, next) { router.get("/resetpassword", function (req, res, next) {
res.render("resetpassword"); res.render("resetpassword");
}); });
@ -63,4 +64,31 @@ router.get("/sensor-data", function (req, res, next) {
res.render("sensor-data"); res.render("sensor-data");
}); });
//reset password page
router.get("/resetpassword/:token", async (req, res, next) => {
try{
//pass token to reset password page
//console.log(req.params.token);
//check if token is valid
let tokenRes = await checkTokenByrowID(req.params.token);
if (!tokenRes) {
let error = new Error("Token not found");
error.status = 400;
return next(error);
}
else {
let token = req.params.token;
console.log(token);
res.render("resetpassword", { token: token });
}
}catch(error){
console.error(error);
next(error);
}
});
module.exports = router; module.exports = router;

View File

@ -81,7 +81,7 @@
</p> </p>
</div> </div>
</footer> </footer>
<script src="js/search.js"></script> <script src="/js/search.js"></script>
</body> </body>

View File

@ -7,6 +7,8 @@
<div class="error-contents"> <div class="error-contents">
<h3>Please check your email for the reset password link</h3> <h3>Please check your email for the reset password link</h3>
</div> </div>
<br>
<br>
<br> <br>
<a>Dont have an account?</a> <a href="/login">Sign Up</a> <a>Dont have an account?</a> <a href="/login">Sign Up</a>

View File

@ -6,14 +6,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content=""> <meta name="description" content="">
<meta name="author" content=""> <meta name="author" content="">
<link rel="shortcut icon" type="images/logo.ico" href="images/logo.ico" /> <link rel="shortcut icon" type="images/logo.ico" href="/images/logo.ico" />
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"> integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<!-- Custom styles for this template --> <!-- Custom styles for this template -->
<link rel="stylesheet" href="css/sp.css" /> <link rel="stylesheet" href="/css/sp.css" />
<!-- jQuery library --> <!-- jQuery library -->
@ -30,7 +30,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.2.0/socket.io.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.2.0/socket.io.min.js"></script>
<!-- jquery app.js --> <!-- jquery app.js -->
<script src="js/app.js"></script> <script src="/js/app.js"></script>
</head> </head>
@ -66,7 +66,7 @@
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top"> <nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top">
<div class="container"> <div class="container">
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="images/logo.png" alt="logo" /> <img src="/images/logo.png" alt="logo" />
</a> </a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">

View File

@ -5,9 +5,12 @@
<section class="wrapper"> <section class="wrapper">
<div class="form"> <div class="form">
<header>Reset Password</header> <header>Reset Password</header>
<form action="auth/resetPassword" > <div class="card-header shadow actionMessage" style="display:none"></div>
<input type="password" name="password" placeholder="password" required> <!-- <form action="auth/resetpassword/<%= token.token%>" method="post" onsubmit="formAJAX(this)"> -->
<input type="cpassword" name="cpassword" placeholder="cpassword" required> <form action="auth/resetpassword/<%= token%>" method="post" onsubmit="formAJAX(this)" evalAJAX="app.auth.logInRedirect();">
<input type="password" id="password" name="password" placeholder="Password" required />
<input type="password" id="confirmPassword" name="confirmPassword" placeholder="Confirm Password"
required />
<input type="submit" value="Reset Password" /> <input type="submit" value="Reset Password" />
</form> </form>
<br> <br>
@ -15,12 +18,29 @@
</div> </div>
</section> </section>
<table class="footer">
<tr>
<td>
<p>&copy; 2024 EcoSaver</p>
</td>
</tr>
</table>
</body> <script>
//both password fields must match
var password = document.getElementById("password");
var confirm_password = document.getElementById("confirmPassword");
function validatePassword() {
var passwordValue = password.value;
// Strong password regex pattern
var strongPasswordPattern = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/;
if (passwordValue != confirm_password.value) {
confirm_password.setCustomValidity("Passwords Don't Match");
} else if (!strongPasswordPattern.test(passwordValue)) {
confirm_password.setCustomValidity("Password must be at least 8 characters long and include at least one letter, one number, and one special character.");
} else {
confirm_password.setCustomValidity('');
}
}
password.onchange = validatePassword;
confirm_password.onkeyup = validatePassword;
</script>

View File

@ -4,7 +4,6 @@
//app.auth.redirectIfLoggedIn(); //app.auth.redirectIfLoggedIn();
</script> </script>
<body>
<section class="wrapper"> <section class="wrapper">
<div class="form signup iot-card"> <div class="form signup iot-card">
<!--<div class="form signup card" --> <!--<div class="form signup card" -->
@ -67,7 +66,7 @@
confirm_password.onkeyup = validatePassword; confirm_password.onkeyup = validatePassword;
const wrapper = document.querySelector(".wrapper"), const wrapper = document.querySelector(".wrapper"),
signupHeader = document.querySelector(".signup header"), signupHeader = document.querySelector(".signup header"),
loginHeader = document.querySelector(".login header"); loginHeader = document.querySelector(".login header");

View File

@ -9,7 +9,7 @@
<meta http-equiv="cleartype" content="on" /> <meta http-equiv="cleartype" content="on" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" type="images/logo.ico" href="images/logo.ico" /> <link rel="shortcut icon" type="images/logo.ico" href="/images/logo.ico" />
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
@ -17,8 +17,8 @@
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" /> integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" />
<!-- Custom styles for this template --> <!-- Custom styles for this template -->
<link href="css/all.css" rel="stylesheet" /> <link href="/css/all.css" rel="stylesheet" />
<link href="css/style.css" rel="stylesheet" /> <link href="/css/style.css" rel="stylesheet" />
<!-- weird api page cdn --> <!-- weird api page cdn -->
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
@ -46,10 +46,10 @@
<!-- <script src="https://cdn.jsdelivr.net/npm/jquery.fancytable/dist/fancyTable.min.js"></script> --> <!-- <script src="https://cdn.jsdelivr.net/npm/jquery.fancytable/dist/fancyTable.min.js"></script> -->
<!-- jq-repeat --> <!-- jq-repeat -->
<script src="js/jq-repeat.js"></script> <script src="/js/jq-repeat.js"></script>
<!-- jquery public app.js --> <!-- jquery public app.js -->
<script src="js/app.js"></script> <script src="/js/app.js"></script>
</head> </head>
<!-- javascript function to check if user is auth --> <!-- javascript function to check if user is auth -->
<script> <script>
@ -80,7 +80,7 @@
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top"> <nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top">
<div class="container"> <div class="container">
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="images/logo.png" alt="logo" /> <img src="/images/logo.png" alt="logo" />
</a> </a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">