Skip to content
Snippets Groups Projects
Commit da0ffbe9 authored by Louis's avatar Louis :fire:
Browse files

Handle reset logic in API & web routers

parent e3da3823
No related branches found
No related tags found
No related merge requests found
......@@ -79,3 +79,8 @@ a:hover {
input[type=text]:disabled {
background: #FFF5F5;
}
input[type=text].error,
input[type=password].error {
border-color: red;
}
\ No newline at end of file
......@@ -4,10 +4,53 @@
const tokenField = document.getElementById('reset_token')
const newField = document.getElementById('new_password')
const confirmField = document.getElementById('confirm_password')
const messageField = document.getElementById('formmessage')
form.addEventListener('submit', function (e) {
function validateInput() {
const newValue = newField.value
const confirmValue = confirmField.value
if (newValue !== confirmValue) {
messageField.innerText = 'The passwords do not match'
confirmField.classList.toggle('error', true)
return false
} else {
messageField.innerText = ''
confirmField.classList.toggle('error', false)
return true
}
}
newField.addEventListener('input', validateInput)
confirmField.addEventListener('input', validateInput)
form.addEventListener('submit', async function (e) {
e.preventDefault()
console.log(tokenField, newField, confirmField)
if (!validateInput()) {
return
}
const payload = {
reset_token: tokenField.value,
new_password: newField.value,
confirm_password: confirmField.value,
}
const result = await fetch('/api/auth/reset-password', {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(payload),
})
if (!result.ok) {
messageField.innerText = 'There was a problem resetting your password. Please request a new password reset link and try again later.'
return
}
const data = await result.clone().json()
})
})
}())
\ No newline at end of file
const moment = require('moment')
const { User } = require('database/models')
const HttpError = require('core/errors/HttpError')
const crypto = require('core/utils/crypto')
exports.register = async ctx => {
const { email, name, password, date_of_birth } = ctx.request.body
......@@ -32,4 +35,104 @@ exports.login = async ctx => {
exports.triggerPasswordReset = async ctx => {
const { email } = ctx.request.body
}
exports.handlePasswordReset = async ctx => {
const { reset_token, new_password, confirm_password } = ctx.request.body
let token = null
if (!reset_token) {
ctx.body = {
error: {
message: 'The reset token was missing',
},
params: {
new_password,
confirm_password,
},
}
ctx.status = 400
return
}
if (!new_password || !confirm_password) {
ctx.body = {
error: {
message: 'A new password and password confirmation must be supplied',
},
params: {
new_password,
confirm_password,
},
}
ctx.status = 400
return
}
if (new_password !== confirm_password) {
ctx.body = {
error: {
message: 'The new password must match the password confirmation',
},
params: {
new_password,
confirm_password,
},
}
ctx.status = 409
return
}
try {
token = JSON.parse(await crypto.decrypt(reset_token))
} catch (e) {
ctx.body = {
error: {
message: 'The reset token was invalid or expired',
},
params: {
new_password,
confirm_password,
},
}
ctx.status = 400
return
}
const expires = moment.utc(token.expires)
if (expires.isSameOrBefore(moment.utc())) {
ctx.body = {
error: {
message: 'The reset token was invalid or expired'
},
params: {
new_password,
confirm_password,
},
}
ctx.status = 400
return
}
const user = await User.findOne({ where: { reset_token, id: token.id } })
if (!user) {
ctx.body = {
error: {
message: 'The reset token was invalid or expired'
},
params: {
new_password,
confirm_password,
},
}
ctx.status = 400
return
}
user.reset_token = null
await user.setPlaintextPassword(new_password)
await user.save()
ctx.status = 200
ctx.body = { user }
}
\ No newline at end of file
......@@ -73,4 +73,20 @@ exports.resetPassword = async ctx => {
await ctx.render('auth/reset-password', {
token,
})
}
exports.handleResetPassword = async ctx => {
const defer = require('./api/auth').handlePasswordReset
await defer(ctx)
if (ctx.status >= 400) {
const { error } = ctx.body
await ctx.render('auth/reset-password-error', {
back_link: `/reset-password?token=${ ctx.request.body.reset_token }`,
message: error.message,
})
return
}
await ctx.render('auth/reset-password-success')
}
\ No newline at end of file
......@@ -18,6 +18,7 @@ web.get('/login', ctx => {
web.post('/login', controller('auth', 'login'))
web.get('/reset-password', controller('auth', 'resetPassword'))
web.post('/reset-password', controller('auth', 'handleResetPassword'))
web.get('/auth/authorize', AuthServer.authorize)
web.post('/auth/authorize', AuthServer.authorize)
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Password Reset | Jetsam</title>
<meta name="description" content="Jetsam is a social plastics logging app that puts the power of change in your hands!">
<meta property="og:description" content="Jetsam is a social plastics logging app that puts the power of change in your hands!">
<meta property="og:image" content="https://jetsam.tech/images/card-image.png">
<meta property="og:image:type" content="image/png">
<meta name="twitter:site" content="@jetsam_tech">
<meta name="twitter:creator" content="@louisdoesdev">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" type="image/png" href="https://jetsam.tech/images/logo.png">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"
integrity="sha256-l85OmPOjvil/SOvVt3HnSSjzF1TUMyT9eV0c2BzEGzU="
crossorigin="anonymous" />
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css"
integrity="sha256-2YQRJMXD7pIAPHiXr0s+vlRWA7GYJEK0ARns7k2sbHY="
crossorigin="anonymous" />
<link rel="stylesheet" href="/css/main.css?v=2">
</head>
<body>
<main class="container" style="max-width: 600px">
<header class="header">
<img class="logo" src="https://jetsam.tech/images/logo.png" width="128px" height="128px">
<div>
<h1>Jetsam</h1>
<h3>Your World; Cleaner</h3>
</div>
</header>
<h3 class="centered">Password Reset Successful</h3>
<p class="centered">You successfully reset your password. You can log in to the Jetsam app with the password you just created. Now go and snap some pesky plastics!</p>
<div class="row centered">
<a href="https://jetsam.tech">Go to the Jetsam website</a>
</div>
</main>
</body>
</html>
......@@ -41,7 +41,7 @@
You've requested a reset of your password; please use this form to choose a new one. The token is valid for 1 hour from the time we send it to you,
so if you've gone to grab a cup of tea between requesting a new password and using this form, you may need to request another password reset!
</p>
<input id=reset_token name=reset_token required type=hidden class="u-full-width" value="{{token}}" disabled>
<input id=reset_token name=reset_token required type=hidden class="u-full-width" value="{{token}}">
<div class="row">
<div class="twelve columns">
<label for=new_password><sup class="required" title="Required">*</sup>New Password:</label>
......@@ -59,7 +59,7 @@
<div class="row centered">
<button class="button-primary four columns" type="submit" id=submit>Reset Password</button>
</div>
<div id="formmessage" class="row"></div>
<div id="formmessage" class="row" style="color: red"></div>
</form>
</main>
......@@ -75,6 +75,6 @@
})();
</script>
<script type="application/javascript" src="/js/reset-password.js"></script>
<!--<script type="application/javascript" src="/js/reset-password.js"></script>-->
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment