From a40b6d514bd8330a78a23ca44349861a39b8ce18 Mon Sep 17 00:00:00 2001 From: knighthawk4227 Date: Tue, 6 May 2025 23:33:05 -0700 Subject: [PATCH 1/8] forgot password js --- app.js | 25 ++++++- package-lock.json | 7 ++ package.json | 1 + src/auth/forgotPass.js | 55 ++++++++++++++ src/router/user.js | 150 +++++++++++++++++++-------------------- src/views/forgotPass.ejs | 25 +++++++ src/views/signup.ejs | 92 ++++++++++++------------ 7 files changed, 231 insertions(+), 124 deletions(-) create mode 100644 src/auth/forgotPass.js create mode 100644 src/views/forgotPass.ejs diff --git a/app.js b/app.js index fa59c80..8b23e80 100644 --- a/app.js +++ b/app.js @@ -71,7 +71,30 @@ app.get('/aboutUs', (req, res) => { return res.status(status.Ok); }); -app.post('/api/location', async (req,res) => { +app.get('/forgotPassword', (req, res) => { + res.render('forgotPass'); + return res.status(status.Ok); +}); + +app.get('/reset/:token', async (req, res) => { + const token = req.params.token; + + const user = await users.findOne({ + resetToken: token, + resetTokenExpires: { $gt: Date.now() }, + }); + + if (!user) { + req.session.error = 'reset link not valid or has expired'; + return res.redirect('/forgotPassword'); + } + res.render('resetPassword', { + token: token, + errMessage: req.session.error || '', + }); +}); + +app.post('/api/location', async (req, res) => { const { latitude, longitude } = req.body; const response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&result_type=country&key=${process.env.geolocation_api}`); diff --git a/package-lock.json b/package-lock.json index 95750e3..a416b45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "bcrypt": "^5.1.1", "connect-mongo": "^5.1.0", + "crypto": "^1.0.1", "dotenv": "^16.5.0", "ejs": "^3.1.10", "express": "^5.1.0", @@ -483,6 +484,12 @@ "node": ">=6.6.0" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", diff --git a/package.json b/package.json index 4f1048f..08ad27d 100755 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "dependencies": { "bcrypt": "^5.1.1", "connect-mongo": "^5.1.0", + "crypto": "^1.0.1", "dotenv": "^16.5.0", "ejs": "^3.1.10", "express": "^5.1.0", diff --git a/src/auth/forgotPass.js b/src/auth/forgotPass.js new file mode 100644 index 0000000..2f079c1 --- /dev/null +++ b/src/auth/forgotPass.js @@ -0,0 +1,55 @@ +const express = require('express'); +const { server } = require('ws'); +const crypto = require('crypto'); +const nodeMail = require('nodemailer'); +require('dotenv').config(); + +const transporter = nodeMail.createTransport({ + service: 'gmail', + auth: { + user: process.env.EMAIL_USER, + pass: process.env.PASS, + } +}); + +module.exports = (users) => { + const router = express.Router(); + + + router.post('/resetPass', async (req, res) => { + const { email } = req.body; + const user = await users.findOne({ email }); + + if (!user) { + req.session.error = 'No user found' + return res.redirect('/forgotPassword'); + } + + const token = crypto.randomBytes(32).toString('hex'); + const expiration = Date.now() + 360000; + + await users.updateOne({ email }, { + $set: { resetToken: token, resetTokenExpires: expiration } + }); + + const resetUrl = `http://localhost:3000/reset/${token}`; + + const mailSend = { + from: process.env.EMAIL_USER, + to: email, + subject: 'Password reset', + text: `reset your password here ${resetUrl} \n this link will expire within 1 hour`, + + }; + + try { + await transporter.sendMail(mailSend); + req.session.reset = 'Reset link sent Check your email'; + res.redirect('/login'); + } catch (err) { + console.log('there was an error', err); + res.status(500).send('email failed to send try again'); + } + }); + return router; +} diff --git a/src/router/user.js b/src/router/user.js index 9daa7cb..39fb28b 100644 --- a/src/router/user.js +++ b/src/router/user.js @@ -5,7 +5,7 @@ const salt = 12; module.exports = (middleware, users, plans) => { const router = require("express").Router(); - + router.use(middleware); router.get('/home', async (req, res) => { @@ -22,17 +22,17 @@ module.exports = (middleware, users, plans) => { if (!req.session.email) { return res.status(status.Unauthorized).redirect('/login'); } - + try { // console.log(req.user.email); - const userPlans = await plans.find({userEmail: req.user.email }).toArray(); + const userPlans = await plans.find({ userEmail: req.user.email }).toArray(); // console.log(userPlans); res.render('plans', { user: req.user, plans: userPlans }); - + } catch (err) { console.error("Error fetching plans:", err); req.session.errMessage = "Could not load your plans. Please try again."; @@ -42,7 +42,7 @@ module.exports = (middleware, users, plans) => { router.get('/newPlan', (req, res) => { const errMessage = req.session.errMessage; - req.session.errMessage = ""; + req.session.errMessage = ""; res.render('newPlan', { user: req.user, errMessage: errMessage }); }); @@ -51,45 +51,45 @@ module.exports = (middleware, users, plans) => { return res.status(status.Unauthorized).redirect('/login'); } - const planSchema = joi.object({ - name: joi.string().min(3).max(100).required(), - retirementAge: joi.number().min(18).max(120).required(), - retirementExpenses: joi.number().min(0).required(), - retirementAssets: joi.number().min(0).required(), - retirementLiabilities: joi.number().min(0).required(), - }); - - const validationOptions = { convert: true, abortEarly: false }; - const { error, value } = planSchema.validate(req.body, validationOptions); - - if (error) { - console.error("Plan validation error:", error.details); - req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); - res.status(status.BadRequest).redirect("/newPlan"); - return; - } - const newPlan = { - userEmail: req.user.email, - name: value.name, - retirementAge: value.retirementAge, - retirementExpenses: value.retirementExpenses, - retirementAssets: value.retirementAssets, - retirementLiabilities: value.retirementLiabilities, - progress: "0%" - }; - - try{ - await plans.insertOne(newPlan); - req.session.errMessage = ""; - res.redirect('/plans'); - } - catch(err){ - console.error("Error saving plan:", err); - req.session.errMessage = "An error occurred while saving your plan. Please try again."; - res.status(status.InternalServerError).redirect("/newPlan"); - } + const planSchema = joi.object({ + name: joi.string().min(3).max(100).required(), + retirementAge: joi.number().min(18).max(120).required(), + retirementExpenses: joi.number().min(0).required(), + retirementAssets: joi.number().min(0).required(), + retirementLiabilities: joi.number().min(0).required(), }); + const validationOptions = { convert: true, abortEarly: false }; + const { error, value } = planSchema.validate(req.body, validationOptions); + + if (error) { + console.error("Plan validation error:", error.details); + req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); + res.status(status.BadRequest).redirect("/newPlan"); + return; + } + const newPlan = { + userEmail: req.user.email, + name: value.name, + retirementAge: value.retirementAge, + retirementExpenses: value.retirementExpenses, + retirementAssets: value.retirementAssets, + retirementLiabilities: value.retirementLiabilities, + progress: "0%" + }; + + try { + await plans.insertOne(newPlan); + req.session.errMessage = ""; + res.redirect('/plans'); + } + catch (err) { + console.error("Error saving plan:", err); + req.session.errMessage = "An error occurred while saving your plan. Please try again."; + res.status(status.InternalServerError).redirect("/newPlan"); + } + }); + router.get('/more', (req, res) => { res.render('more', { user: req.user }); return res.status(status.Ok); @@ -107,13 +107,13 @@ module.exports = (middleware, users, plans) => { router.get('/questionnaire', (req, res) => { const errMessage = req.session.errMessage; - req.session.errMessage = ""; + req.session.errMessage = ""; res.render('questionnaire', { user: req.user, errMessage: errMessage }); }); router.post('/questionnaire', (req, res) => { // console.log("Questionnaire POST body:", req.body); - + const questionnaireSchema = joi.object({ dob: joi.date().required(), education: joi.string().valid('primary', 'secondary', 'tertiary', 'postgraduate').required(), @@ -123,22 +123,22 @@ module.exports = (middleware, users, plans) => { assets: joi.number().min(0).required(), liabilities: joi.number().min(0).required(), }); - - const validationOptions = { convert: true, abortEarly: false }; + + const validationOptions = { convert: true, abortEarly: false }; const { error, value } = questionnaireSchema.validate(req.body, validationOptions); - - if (error) { + + if (error) { console.error("Questionnaire validation error:", error.details); - req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); - res.status(status.BadRequest).redirect("/questionnaire"); - return; + req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); + res.status(status.BadRequest).redirect("/questionnaire"); + return; } - - users.updateOne( - { email: req.session.email }, - { - $set: { - financialData: true, + + users.updateOne( + { email: req.session.email }, + { + $set: { + financialData: true, dob: value.dob, education: value.education, maritalStatus: value.maritalStatus, @@ -146,26 +146,26 @@ module.exports = (middleware, users, plans) => { expenses: value.expenses, assets: value.assets, liabilities: value.liabilities, - } + } } - ).then((result) => { + ).then((result) => { if (result.matchedCount === 0) { console.log(`User not found during questionnaire update: ${req.session.email}`); - req.session.errMessage = "User session invalid. Please log in again."; - res.status(status.NotFound).redirect("/login"); + req.session.errMessage = "User session invalid. Please log in again."; + res.status(status.NotFound).redirect("/login"); return; } if (result.modifiedCount === 0 && result.matchedCount === 1) { console.log(`User questionnaire data unchanged (already up-to-date): ${req.session.email}`); } - - req.session.errMessage = ""; - res.status(status.Ok).redirect("/home"); - - }).catch(err => { + + req.session.errMessage = ""; + res.status(status.Ok).redirect("/home"); + + }).catch(err => { console.error("Error updating questionnaire in database:", err); req.session.errMessage = "An error occurred while saving your information. Please try again."; - res.status(status.InternalServerError).redirect("/questionnaire"); + res.status(status.InternalServerError).redirect("/questionnaire"); }); }); @@ -181,7 +181,7 @@ module.exports = (middleware, users, plans) => { if (valid.err) { req.session.errMessage = "Invalid input", - res.status(status.BadRequest); + res.status(status.BadRequest); return res.redirect("/profile"); } @@ -205,20 +205,20 @@ module.exports = (middleware, users, plans) => { ).then((result) => { if (result.matchedCount === 0) { console.log(`User not found during account update: ${req.session.email}`); - req.session.errMessage = "User session invalid. Please log in again."; - res.status(status.NotFound).redirect("/login"); + req.session.errMessage = "User session invalid. Please log in again."; + res.status(status.NotFound).redirect("/login"); return; } if (result.modifiedCount === 0 && result.matchedCount === 1) { console.log(`User account data unchanged (already up-to-date): ${req.session.email}`); } - - req.session.errMessage = ""; - return res.status(status.Ok).redirect("/profile"); - }).catch(err => { + + req.session.errMessage = ""; + return res.status(status.Ok).redirect("/profile"); + }).catch(err => { console.error("Error updating account in database:", err); req.session.errMessage = "An error occurred while saving your information. Please try again."; - return res.status(status.InternalServerError).redirect("/profile"); + return res.status(status.InternalServerError).redirect("/profile"); }); }); diff --git a/src/views/forgotPass.ejs b/src/views/forgotPass.ejs new file mode 100644 index 0000000..cd3a15f --- /dev/null +++ b/src/views/forgotPass.ejs @@ -0,0 +1,25 @@ +<%- include("./partials/fileHeader") %> + +
+
+
+
+

Reset Password

+
+
+ + +
+
+ +
+
+
+
+
+ + <%- include("./partials/footer") %> diff --git a/src/views/signup.ejs b/src/views/signup.ejs index 1234a2b..b3c7586 100644 --- a/src/views/signup.ejs +++ b/src/views/signup.ejs @@ -1,52 +1,48 @@ <%- include("./partials/fileHeader") %> -<%- include("./partials/headerStart") %> + <%- include("./partials/headerStart") %> -
-
-
-
-

Signup

-
-
- - -
-
- - -
-
- - -
-
- - - - -
-
<%= errMessage %>
-
- -
-
- - Login -
+
+
+ +
+

Signup

+
+
+ + +
+
+ + +
+
+ + + + +
+
+ <%= errMessage %> +
+
+ +
+
+ + Login +
+
+
- -
-
+ -<%- include("./partials/footer") %> \ No newline at end of file + <%- include("./partials/footer") %> From f0b1089e15003dcc05195b1073099eeda0a18b99 Mon Sep 17 00:00:00 2001 From: knighthawk4227 Date: Wed, 7 May 2025 00:52:01 -0700 Subject: [PATCH 2/8] routes for forgotPass --- app.js | 1 + src/auth/forgotPass.js | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 8b23e80..8ed961a 100644 --- a/app.js +++ b/app.js @@ -114,6 +114,7 @@ initDatabase().then(() => { // Import middleware & apply to user routes const middleware = require("./src/auth/middleware")(users, plans); app.use(require('./src/router/user')(middleware, users, plans)); + app.use(require('./src/auth/forgetPass')(users)); // 404 handler app.get('/*splat', (req, res) => { diff --git a/src/auth/forgotPass.js b/src/auth/forgotPass.js index 2f079c1..f83b392 100644 --- a/src/auth/forgotPass.js +++ b/src/auth/forgotPass.js @@ -1,5 +1,4 @@ const express = require('express'); -const { server } = require('ws'); const crypto = require('crypto'); const nodeMail = require('nodemailer'); require('dotenv').config(); @@ -11,11 +10,10 @@ const transporter = nodeMail.createTransport({ pass: process.env.PASS, } }); - +// users info module.exports = (users) => { const router = express.Router(); - router.post('/resetPass', async (req, res) => { const { email } = req.body; const user = await users.findOne({ email }); From 617fc8d8775aff1f1ce2f0e60cd26d049a88382d Mon Sep 17 00:00:00 2001 From: knighthawk4227 Date: Wed, 7 May 2025 10:49:08 -0700 Subject: [PATCH 3/8] indentation of dashboard --- app.js | 2 + src/views/dashboard.ejs | 136 ++++++++++++++++++++-------------------- 2 files changed, 70 insertions(+), 68 deletions(-) diff --git a/app.js b/app.js index 8ed961a..a4f0263 100644 --- a/app.js +++ b/app.js @@ -94,6 +94,8 @@ app.get('/reset/:token', async (req, res) => { }); }); +app.post('reset') + app.post('/api/location', async (req, res) => { const { latitude, longitude } = req.body; diff --git a/src/views/dashboard.ejs b/src/views/dashboard.ejs index 3de6635..182184e 100644 --- a/src/views/dashboard.ejs +++ b/src/views/dashboard.ejs @@ -1,99 +1,99 @@ <%- include("./partials/fileHeader") %> <%- include("./partials/header") %> -
- -
- - + -
- -
-
+ +
+ +
+
- - - -
+ + + +

- Retirement goal

+ Retirement goal

- $53k

-
-
+ class="block text-right antialiased tracking-normal font-sans text-2xl font-semibold leading-snug text-blue-gray-900"> + $53k +
+

- Make this percent bar or graph or something  than last - week + Make this percent bar or graph or something  than last + week

-
+
-
+

- Retirement goal

+ Retirement goal

- $53k

-
-
+ class="block text-right antialiased tracking-normal font-sans text-2xl font-semibold leading-snug text-blue-gray-900"> + $53k +
+

- Make this percent bar or graph or something  than last - week + Make this percent bar or graph or something  than last + week

-
+
- - - - - - - - - - - - - - - - - - - - - - - -
-
+ + + + + + + + + + + + + + + + + + + + + + + +
+
-

+

Retirement goal

-

$53k

-

+

Make this percent bar or graph or something  than last week -

+

-
-
-
+
+
+
-
-
+ + - <%- include("./partials/navBar") %> - <%- include("./partials/footer") %> +<%- include("./partials/navBar") %> + <%- include("./partials/footer") %> From 088f9ea55ae60fada9cffeb1a5a0546897b431c8 Mon Sep 17 00:00:00 2001 From: knighthawk4227 Date: Wed, 7 May 2025 13:03:26 -0700 Subject: [PATCH 4/8] reset pass logic --- app.js | 52 ++++++++++++++++++++++++++++++++++++---- package-lock.json | 11 ++++++++- package.json | 3 ++- src/auth/forgotPass.js | 8 +++++-- src/views/forgotPass.ejs | 3 ++- src/views/login.ejs | 4 ++-- src/views/resetPass.ejs | 36 ++++++++++++++++++++++++++++ 7 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 src/views/resetPass.ejs diff --git a/app.js b/app.js index a4f0263..9da8e44 100644 --- a/app.js +++ b/app.js @@ -72,10 +72,15 @@ app.get('/aboutUs', (req, res) => { }); app.get('/forgotPassword', (req, res) => { - res.render('forgotPass'); + res.render('forgotPass', { error: req.session.error, reset: req.session.reset }); return res.status(status.Ok); }); +app.get('reset', (req, res) => { + return res.render('resetPass'); +}) + +// Reset with token given to user via email app.get('/reset/:token', async (req, res) => { const token = req.params.token; @@ -88,13 +93,52 @@ app.get('/reset/:token', async (req, res) => { req.session.error = 'reset link not valid or has expired'; return res.redirect('/forgotPassword'); } + const error = req.session.error; + req.session.error = ''; + res.render('resetPassword', { token: token, - errMessage: req.session.error || '', + errMessage: error, }); }); -app.post('reset') +app.post('/resetLink', async (req, res) => { + const { token, password, confirmPassword, } = req.body; + + if (!token || !password || !confirmPassword) { + req.session.error = 'field may be missing'; + return res.redirect(`/reset/${token}`); + } + if (password !== confirmPassword) { + req.session.error = 'passwords do not match'; + return res.redirect(`/reset/${token}`); + } + const user = await users.findOne({ + resetToken: token, + resetTokenExpires: { $gt: Date.now() }, + }); + + if (!user) { + req.session.error = 'Reset link is invalid.'; + return res.redirect('/forgotPassword'); + } + const hashPassword = await bcrypt.hash(password, 12); + + await users.updateOne( + { + email: user.email + }, + { + $set: { + password: hashPassword, + resetToken: '', + resetTokenExpires: 0, + }, + } + ); + req.session.success = 'Password has been reset'; + res.redirect('/login'); +}); app.post('/api/location', async (req, res) => { const { latitude, longitude } = req.body; @@ -112,11 +156,11 @@ initDatabase().then(() => { // Import authentication handler app.use(require("./src/auth/authentication")(users)); + app.use(require('./src/auth/forgotPass')(users)); // Import middleware & apply to user routes const middleware = require("./src/auth/middleware")(users, plans); app.use(require('./src/router/user')(middleware, users, plans)); - app.use(require('./src/auth/forgetPass')(users)); // 404 handler app.get('/*splat', (req, res) => { diff --git a/package-lock.json b/package-lock.json index a416b45..fe45a7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "express": "^5.1.0", "express-session": "^1.18.1", "joi": "^17.13.3", - "mongodb": "^6.16.0" + "mongodb": "^6.16.0", + "nodemailer": "^7.0.2" }, "devDependencies": { "nodemon": "^3.1.10" @@ -1437,6 +1438,14 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/nodemailer": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.2.tgz", + "integrity": "sha512-SYsisPeLFYli5Q+BCGSyHT5CVvezPmQjHgINV9KVvVLV1aktuoD4E0Np9Q3ND9I481qIHzUQzVT+Tl/Tw7Ivdg==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", diff --git a/package.json b/package.json index 08ad27d..8f0f856 100755 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "express": "^5.1.0", "express-session": "^1.18.1", "joi": "^17.13.3", - "mongodb": "^6.16.0" + "mongodb": "^6.16.0", + "nodemailer": "^7.0.2" }, "devDependencies": { "nodemon": "^3.1.10" diff --git a/src/auth/forgotPass.js b/src/auth/forgotPass.js index f83b392..8e4880c 100644 --- a/src/auth/forgotPass.js +++ b/src/auth/forgotPass.js @@ -14,9 +14,12 @@ const transporter = nodeMail.createTransport({ module.exports = (users) => { const router = express.Router(); - router.post('/resetPass', async (req, res) => { + router.post('/auth/resetPass', async (req, res) => { + console.log("we are inside of the post"); const { email } = req.body; const user = await users.findOne({ email }); + req.session.error = ''; + req.session.reset = ''; if (!user) { req.session.error = 'No user found' @@ -43,7 +46,7 @@ module.exports = (users) => { try { await transporter.sendMail(mailSend); req.session.reset = 'Reset link sent Check your email'; - res.redirect('/login'); + res.redirect('/forgotPassword'); } catch (err) { console.log('there was an error', err); res.status(500).send('email failed to send try again'); @@ -51,3 +54,4 @@ module.exports = (users) => { }); return router; } + diff --git a/src/views/forgotPass.ejs b/src/views/forgotPass.ejs index cd3a15f..8412d3a 100644 --- a/src/views/forgotPass.ejs +++ b/src/views/forgotPass.ejs @@ -2,7 +2,7 @@
-
+

Reset Password


@@ -12,6 +12,7 @@ class="border focus:border-gray-600 w-full rounded-md text-base px-2 py-1 focus:outline-none focus:ring-0 " placeholder="Enter Email" />
+
<%= error%>
@@ -41,4 +41,4 @@
-<%- include("./partials/footer") %> \ No newline at end of file +<%- include("./partials/footer") %> diff --git a/src/views/resetPass.ejs b/src/views/resetPass.ejs new file mode 100644 index 0000000..d923d50 --- /dev/null +++ b/src/views/resetPass.ejs @@ -0,0 +1,36 @@ +<%- include("./partials/fileHeader") %> + +
+
+ +
+

Reset Password

+
+ +
+ + +
+
+ + +
+ <% if (typeof errMessage !== 'undefined') { %> +
+ <%= errMessage %> +
+ <% } %> +
+ +
+
+ +
+
+<%- include("./partials/footer") %> From 15dd4e0d20223cdfcaa05e7f83c4b97b880a5bf3 Mon Sep 17 00:00:00 2001 From: knighthawk4227 Date: Wed, 7 May 2025 19:09:25 -0700 Subject: [PATCH 5/8] forgot password validatation using joi --- app.js | 20 +++++++++++++++++--- src/auth/forgotPass.js | 17 ++++++++++++++--- src/views/forgotPass.ejs | 38 +++++++++++++++++++++++++------------- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/app.js b/app.js index cf8874e..17b3457 100644 --- a/app.js +++ b/app.js @@ -76,7 +76,11 @@ app.get('/aboutUs', (req, res) => { }); app.get('/forgotPassword', (req, res) => { - res.render('forgotPass', { error: req.session.error, reset: req.session.reset }); + const error = req.session.error; + const reset = req.session.reset; + req.session.reset = ''; + req.session.error = ''; + res.render('forgotPass', { error: error, reset: reset }); return res.status(status.Ok); }); @@ -105,7 +109,17 @@ app.get('/reset/:token', async (req, res) => { app.post('/resetLink', async (req, res) => { const { token, password, confirmPassword, } = req.body; - + const passwordSchema = joi.object({ + password: joi.string().max(20).min(6).required(), + confirmPassword: joi.string().max(20).min(6).required(), + }); + const valid = passwordSchema.validate(req.body); + if (valid.error) { + console.log("houston we have a problem"); + req.session.error = 'Invalid input'; + res.status(status.BadRequest); + return res.redirect(`/reset/${token}`); + } if (!token || !password || !confirmPassword) { req.session.error = 'field may be missing'; return res.redirect(`/reset/${token}`); @@ -121,7 +135,7 @@ app.post('/resetLink', async (req, res) => { if (!user) { req.session.error = 'Reset link is invalid.'; - return res.redirect('/forgotPassword'); + return res.redirect(`/reset`); } const hashPassword = await bcrypt.hash(password, 12); diff --git a/src/auth/forgotPass.js b/src/auth/forgotPass.js index 8e4880c..62260bf 100644 --- a/src/auth/forgotPass.js +++ b/src/auth/forgotPass.js @@ -1,5 +1,6 @@ const express = require('express'); const crypto = require('crypto'); +const joi = require('joi'); const nodeMail = require('nodemailer'); require('dotenv').config(); @@ -16,17 +17,27 @@ module.exports = (users) => { router.post('/auth/resetPass', async (req, res) => { console.log("we are inside of the post"); - const { email } = req.body; - const user = await users.findOne({ email }); + const resetSchema = joi.object({ + email: joi.string().email().required(), + }); req.session.error = ''; req.session.reset = ''; + const valid = resetSchema.validate(req.body); + if (valid.error) { + req.session.error = 'invalid email'; + return res.redirect('/forgotPassword') + } + const { email } = req.body; + const user = await users.findOne({ email }); + if (!user) { req.session.error = 'No user found' return res.redirect('/forgotPassword'); } const token = crypto.randomBytes(32).toString('hex'); + console.log(`The reset token is ${token}`) const expiration = Date.now() + 360000; await users.updateOne({ email }, { @@ -39,7 +50,7 @@ module.exports = (users) => { from: process.env.EMAIL_USER, to: email, subject: 'Password reset', - text: `reset your password here ${resetUrl} \n this link will expire within 1 hour`, + text: `reset your password here ${resetUrl} this link will expire within 1 hour`, }; diff --git a/src/views/forgotPass.ejs b/src/views/forgotPass.ejs index 8412d3a..811c32d 100644 --- a/src/views/forgotPass.ejs +++ b/src/views/forgotPass.ejs @@ -6,21 +6,33 @@

Reset Password


-
- - -
-
<%= error%>
-
- -
+ <% if (reset==='' ) { %> +
+ + +
+ <% } else { %> +
+

Reset link sent.

+
+ <% } %> + + +
+ <%= error%> +
+ <% if (reset==='' ) { %> +
+ +
+ <% } %>
- <%- include("./partials/footer") %> + <%- include("./partials/footer") %> <%- include("./partials/footer") %> From 29d682c4e81a4fbf4b058c770b64a7dd26588eb7 Mon Sep 17 00:00:00 2001 From: knighthawk4227 Date: Wed, 7 May 2025 19:26:00 -0700 Subject: [PATCH 6/8] fixing joi validation --- app.js | 6 +++--- src/views/resetPass.ejs | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app.js b/app.js index 17b3457..dd07121 100644 --- a/app.js +++ b/app.js @@ -110,10 +110,10 @@ app.get('/reset/:token', async (req, res) => { app.post('/resetLink', async (req, res) => { const { token, password, confirmPassword, } = req.body; const passwordSchema = joi.object({ - password: joi.string().max(20).min(6).required(), - confirmPassword: joi.string().max(20).min(6).required(), + password: joi.string().max(20).required(), + confirmPassword: joi.string().max(20).required(), }); - const valid = passwordSchema.validate(req.body); + const valid = passwordSchema.validate({ password, confirmPassword }); if (valid.error) { console.log("houston we have a problem"); req.session.error = 'Invalid input'; diff --git a/src/views/resetPass.ejs b/src/views/resetPass.ejs index d923d50..2673347 100644 --- a/src/views/resetPass.ejs +++ b/src/views/resetPass.ejs @@ -6,7 +6,7 @@

Reset Password


- +
- <% if (typeof errMessage !== 'undefined') { %> + <% if (typeof errMessage !=='undefined' ) { %>
<%= errMessage %>
- <% } %> -
- -
+ <% } %> +
+ +
-<%- include("./partials/footer") %> + <%- include("./partials/footer") %> From fc39bcf69358e4e8e9a474d4208690553e80b64c Mon Sep 17 00:00:00 2001 From: knighthawk4227 Date: Thu, 8 May 2025 10:32:10 -0700 Subject: [PATCH 7/8] nodemailer dependencies --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe45a7d..521a7be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "express-session": "^1.18.1", "joi": "^17.13.3", "mongodb": "^6.16.0", - "nodemailer": "^7.0.2" + "nodemailer": "^7.0.3" }, "devDependencies": { "nodemon": "^3.1.10" @@ -1439,9 +1439,9 @@ } }, "node_modules/nodemailer": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.2.tgz", - "integrity": "sha512-SYsisPeLFYli5Q+BCGSyHT5CVvezPmQjHgINV9KVvVLV1aktuoD4E0Np9Q3ND9I481qIHzUQzVT+Tl/Tw7Ivdg==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz", + "integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==", "engines": { "node": ">=6.0.0" } diff --git a/package.json b/package.json index 8f0f856..1071779 100755 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "express-session": "^1.18.1", "joi": "^17.13.3", "mongodb": "^6.16.0", - "nodemailer": "^7.0.2" + "nodemailer": "^7.0.3" }, "devDependencies": { "nodemon": "^3.1.10" From 1587f363f7affb78d3a6802d9fd6e663b362d6ed Mon Sep 17 00:00:00 2001 From: knighthawk4227 Date: Fri, 9 May 2025 09:50:38 -0700 Subject: [PATCH 8/8] fixing reset link session --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index d776381..91045f6 100644 --- a/app.js +++ b/app.js @@ -98,7 +98,7 @@ app.get('/reset/:token', async (req, res) => { return res.redirect('/forgotPassword'); } const error = req.session.error; - req.session.error = ''; + delete req.session.error; res.render('resetPass', { token: token,