diff --git a/server/handlers/auth.handler.js b/server/handlers/auth.handler.js index c5c2afc..2787fb8 100644 --- a/server/handlers/auth.handler.js +++ b/server/handlers/auth.handler.js @@ -223,7 +223,7 @@ async function generateApiKey(req, res) { return res.status(201).send({ apikey }); } -async function resetPasswordRequest(req, res) { +async function resetPassword(req, res) { const user = await query.user.update( { email: req.body.email }, { @@ -239,7 +239,7 @@ async function resetPasswordRequest(req, res) { } if (req.isHTML) { - res.render("partials/reset_password/form", { + res.render("partials/reset_password/request_form", { message: "If the email address exists, a reset password email will be sent to it." }); return; @@ -250,28 +250,29 @@ async function resetPasswordRequest(req, res) { }); } -async function resetPassword(req, res, next) { - const resetPasswordToken = req.params.resetPasswordToken; +async function newPassword(req, res) { + const { new_password, reset_password_token } = req.body; - if (resetPasswordToken) { - const user = await query.user.update( - { - reset_password_token: resetPasswordToken, - reset_password_expires: [">", utils.dateToUTC(new Date())] - }, - { reset_password_expires: null, reset_password_token: null } - ); - - if (user) { - const token = utils.signToken(user); - utils.deleteCurrentToken(res); - utils.setToken(res, token); - res.locals.token_verified = true; - req.cookies.token = token; + const salt = await bcrypt.genSalt(12); + const password = await bcrypt.hash(req.body.new_password, salt); + + const user = await query.user.update( + { + reset_password_token, + reset_password_expires: [">", utils.dateToUTC(new Date())] + }, + { + reset_password_expires: null, + reset_password_token: null, + password, } + ); + + if (!user) { + throw new CustomError("Could not set the password. Please try again later."); } - next(); + res.render("partials/reset_password/new_password_success"); } async function changeEmailRequest(req, res) { @@ -386,8 +387,8 @@ module.exports = { jwtPage, local, login, + newPassword, resetPassword, - resetPasswordRequest, signup, verify, } diff --git a/server/handlers/locals.handler.js b/server/handlers/locals.handler.js index d0bd2a5..46f17c5 100644 --- a/server/handlers/locals.handler.js +++ b/server/handlers/locals.handler.js @@ -37,6 +37,11 @@ async function user(req, res, next) { next(); } +function newPassword(req, res, next) { + res.locals.reset_password_token = req.body.reset_password_token; + next(); +} + function createLink(req, res, next) { res.locals.show_advanced = !!req.body.show_advanced; next(); @@ -73,6 +78,7 @@ module.exports = { createLink, editLink, isHTML, + newPassword, noLayout, protected, user, diff --git a/server/handlers/renders.handler.js b/server/handlers/renders.handler.js index 83a80d6..17ec268 100644 --- a/server/handlers/renders.handler.js +++ b/server/handlers/renders.handler.js @@ -2,6 +2,12 @@ const query = require("../queries"); const utils = require("../utils"); const env = require("../env"); +/** +* +* PAGES +* +**/ + async function homepage(req, res) { // redirect to custom domain homepage if it is set by user const host = utils.removeWww(req.headers.host); @@ -100,9 +106,25 @@ async function resetPassword(req, res) { }); } -async function resetPasswordResult(req, res) { - res.render("reset_password_result", { +async function resetPasswordSetNewPassword(req, res) { + const reset_password_token = req.params.resetPasswordToken; + + if (reset_password_token) { + const user = await query.user.find( + { + reset_password_token, + reset_password_expires: [">", utils.dateToUTC(new Date())] + } + ); + if (user) { + res.locals.token_verified = true; + } + } + + + res.render("reset_password_set_new_password", { title: "Reset password", + ...(res.locals.token_verified && { reset_password_token }), }); } @@ -124,6 +146,12 @@ async function terms(req, res) { }); } +/** +* +* PARTIALS +* +**/ + async function confirmLinkDelete(req, res) { const link = await query.link.find({ uuid: req.query.id, @@ -311,7 +339,7 @@ module.exports = { notFound, report, resetPassword, - resetPasswordResult, + resetPasswordSetNewPassword, settings, stats, terms, diff --git a/server/handlers/validators.handler.js b/server/handlers/validators.handler.js index 79d8e72..68d7989 100644 --- a/server/handlers/validators.handler.js +++ b/server/handlers/validators.handler.js @@ -469,6 +469,21 @@ const resetPassword = [ .isEmail() ]; +const newPassword = [ + body("reset_password_token", "Reset password token is invalid.") + .exists({ checkFalsy: true, checkNull: true }) + .isLength({ min: 36, max: 36 }), + body("new_password", "Password is not valid.") + .exists({ checkFalsy: true, checkNull: true }) + .isLength({ min: 8, max: 64 }) + .withMessage("Password length must be between 8 and 64."), + body("repeat_password", "Password is not valid.") + .custom((repeat_password, { req }) => { + return repeat_password === req.body.new_password; + }) + .withMessage("Passwords don't match."), +]; + const deleteUser = [ body("password", "Password is not valid.") .exists({ checkFalsy: true, checkNull: true }) @@ -607,6 +622,7 @@ module.exports = { getStats, login, malware, + newPassword, redirectProtected, removeDomain, removeDomainAdmin, diff --git a/server/routes/auth.routes.js b/server/routes/auth.routes.js index 0ed5e23..138b812 100644 --- a/server/routes/auth.routes.js +++ b/server/routes/auth.routes.js @@ -72,12 +72,22 @@ router.post( router.post( "/reset-password", - locals.viewTemplate("partials/reset_password/form"), + locals.viewTemplate("partials/reset_password/request_form"), auth.featureAccess([env.MAIL_ENABLED]), validators.resetPassword, asyncHandler(helpers.verify), helpers.rateLimit({ window: 60, limit: 3 }), - asyncHandler(auth.resetPasswordRequest) + asyncHandler(auth.resetPassword) +); + +router.post( + "/new-password", + locals.viewTemplate("partials/reset_password/new_password_form"), + locals.newPassword, + validators.newPassword, + asyncHandler(helpers.verify), + helpers.rateLimit({ window: 60, limit: 5 }), + asyncHandler(auth.newPassword) ); module.exports = router; diff --git a/server/routes/renders.routes.js b/server/routes/renders.routes.js index bf1b241..4c876e6 100644 --- a/server/routes/renders.routes.js +++ b/server/routes/renders.routes.js @@ -86,10 +86,9 @@ router.get( router.get( "/reset-password/:resetPasswordToken", - asyncHandler(auth.resetPassword), asyncHandler(auth.jwtLoosePage), asyncHandler(locals.user), - asyncHandler(renders.resetPasswordResult) + asyncHandler(renders.resetPasswordSetNewPassword) ); router.get( diff --git a/server/views/partials/reset_password/new_password_form.hbs b/server/views/partials/reset_password/new_password_form.hbs new file mode 100644 index 0000000..3f4e9a4 --- /dev/null +++ b/server/views/partials/reset_password/new_password_form.hbs @@ -0,0 +1,42 @@ +
\ No newline at end of file diff --git a/server/views/partials/reset_password/new_password_success.hbs b/server/views/partials/reset_password/new_password_success.hbs new file mode 100644 index 0000000..cbdc564 --- /dev/null +++ b/server/views/partials/reset_password/new_password_success.hbs @@ -0,0 +1,5 @@ ++ Your password is updated successfully. + You can now log in with your new password. +
+Log in → \ No newline at end of file diff --git a/server/views/partials/reset_password/form.hbs b/server/views/partials/reset_password/request_form.hbs similarity index 96% rename from server/views/partials/reset_password/form.hbs rename to server/views/partials/reset_password/request_form.hbs index 8e97bf8..cac62f3 100644 --- a/server/views/partials/reset_password/form.hbs +++ b/server/views/partials/reset_password/request_form.hbs @@ -1,5 +1,6 @@