Skip to content

Commit 3b915ff

Browse files
Implement password reset request and tests
1 parent 357da35 commit 3b915ff

File tree

14 files changed

+475
-30
lines changed

14 files changed

+475
-30
lines changed

default-templates/emails/reset-password.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ If you did not mean to reset your password, ignore this email, your password wil
3939
4040
<p>To reset your password, click on the following link:</p>
4141
42-
<p>${data.resetUrl}</p>
42+
<p><a href="${data.resetUrl}">${data.resetUrl}</a></p>
4343
4444
<p>If you did not mean to reset your password, ignore this email, your password will not change.</p>
4545
`

default-views/account/register.hbs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,20 @@
4545
</div>
4646
</div>
4747
<input type="hidden" name="returnToUrl" value="{{returnToUrl}}" />
48-
4948
</div>
50-
<button type="submit" class="btn btn-primary" id="register">Register</button>
5149

52-
<div>Already have an account?
53-
<a href="/login?returnToUrl={{{returnToUrl}}}">Log In</a>
50+
<div class="form-group">
51+
<div class="row">
52+
<div class="col-md-2">
53+
<button type="submit" class="btn btn-primary" id="register">Register</button>
54+
</div>
55+
56+
<div class="col-md-10">
57+
<div>Already have an account?
58+
<a href="/login?returnToUrl={{{returnToUrl}}}">Log In</a>
59+
</div>
60+
</div>
61+
</div>
5462
</div>
5563
</form>
5664
</div>

default-views/auth/login.hbs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,27 @@
3232
<input type="password" class="form-control" name="password" id="password" />
3333
</div>
3434
</div>
35+
</div>
36+
37+
<div class="form-group">
38+
<div class="row">
39+
<div class="col-md-2">
40+
<button type="submit" class="btn btn-primary" id="login">Login</button>
41+
</div>
42+
43+
<div class="col-md-4">
44+
<div>Don't have an account?
45+
<a href="/register?returnToUrl={{{postRegisterUrl}}}">Register</a>
46+
</div>
47+
</div>
48+
49+
<div class="col-md-6">
50+
<div>Forgot password?
51+
<a href="/account/password/reset?returnToUrl={{{postRegisterUrl}}}">Reset password</a>
52+
</div>
53+
</div>
54+
</div>
55+
3556
<input type="hidden" name="response_type" id="response_type" value="{{response_type}}" />
3657
<input type="hidden" name="display" id="display" value="{{display}}" />
3758
<input type="hidden" name="scope" id="scope" value="{{scope}}" />
@@ -40,11 +61,6 @@
4061
<input type="hidden" name="state" id="state" value="{{state}}" />
4162
<input type="hidden" name="nonce" id="nonce" value="{{nonce}}" />
4263
</div>
43-
<button type="submit" class="btn btn-primary" id="login">Login</button>
44-
45-
<div>Don't have an account?
46-
<a href="/register?returnToUrl={{{postRegisterUrl}}}">Register</a>
47-
</div>
4864
</form>
4965
</div>
5066
</body>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>Reset Link Sent</title>
7+
<link rel="stylesheet" href="/common/css/bootstrap.min.css">
8+
</head>
9+
<body>
10+
<div class="container">
11+
<h4>Reset Link Sent</h4>
12+
</div>
13+
<div class="container">
14+
A Reset Password link has been sent to your email.
15+
</div>
16+
</body>
17+
</html>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>Reset Password</title>
7+
<link rel="stylesheet" href="/common/css/bootstrap.min.css">
8+
</head>
9+
<body>
10+
<div class="container">
11+
<h4>Reset Password</h4>
12+
</div>
13+
<div class="container">
14+
<form method="post" action="/account/password/reset">
15+
<div class="form-group">
16+
{{#if error}}
17+
<div class="row">
18+
<div class="col-md-12">
19+
<p class="text-danger"><strong>{{error}}</strong></p>
20+
</div>
21+
</div>
22+
{{/if}}
23+
<div class="row">
24+
<div class="col-md-12">
25+
{{#if multiUser}}
26+
<p>Please enter your account name. A password reset link will be
27+
emailed to the address you provided during account registration.</p>
28+
29+
<label for="username">Account Name:</label>
30+
<input type="text" class="form-control" name="username" id="username"
31+
placeholder="alice" />
32+
{{else}}
33+
<p>A password reset link will be
34+
emailed to the address you provided during account registration.</p>
35+
{{/if}}
36+
</div>
37+
</div>
38+
</div>
39+
40+
<div class="form-group">
41+
<div class="row">
42+
<div class="col-md-2">
43+
<button type="submit" class="btn btn-primary">Send Reset Link</button>
44+
</div>
45+
46+
<div class="col-md-4">
47+
<div>Don't have an account?
48+
<a href="/register?returnToUrl={{{returnToUrl}}}">Register</a>
49+
</div>
50+
</div>
51+
</div>
52+
53+
<input type="hidden" name="returnToUrl" value="{{{returnToUrl}}}" />
54+
</div>
55+
</form>
56+
</div>
57+
</body>
58+
</html>

lib/api/authn/webid-oidc.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const bodyParser = require('body-parser').urlencoded({ extended: false })
88

99
const { LoginByPasswordRequest } = require('../../requests/login-request')
1010

11+
const PasswordResetEmailRequest = require('../../requests/password-reset-email-request')
12+
1113
const {
1214
AuthCallbackRequest,
1315
LogoutRequest,
@@ -33,6 +35,9 @@ function middleware (oidc) {
3335
router.get(['/login', '/signin'], LoginByPasswordRequest.get)
3436
router.post(['/login', '/signin'], bodyParser, LoginByPasswordRequest.post)
3537

38+
router.get('/account/password/reset', PasswordResetEmailRequest.get)
39+
router.post('/account/password/reset', bodyParser, PasswordResetEmailRequest.post)
40+
3641
router.get('/logout', LogoutRequest.handle)
3742

3843
router.get('/goodbye', (req, res) => { res.render('auth/goodbye') })

lib/models/account-manager.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,11 +391,9 @@ class AccountManager {
391391
* @return {string}
392392
*/
393393
passwordResetUrl (token, returnToUrl) {
394-
let encodedReturnTo = encodeURIComponent(returnToUrl)
395-
396394
let resetUrl = url.resolve(this.host.serverUri,
397-
`/api/password/validateReset?token=${token}` +
398-
`&returnToUrl=${encodedReturnTo}`)
395+
`/account/password/change?token=${token}` +
396+
`&returnToUrl=${returnToUrl}`)
399397

400398
return resetUrl
401399
}
@@ -445,6 +443,8 @@ class AccountManager {
445443
.then(resetToken => {
446444
let resetUrl = this.passwordResetUrl(resetToken, returnToUrl)
447445

446+
debug('Reset URL:', resetUrl)
447+
448448
let emailData = {
449449
to: userAccount.email,
450450
webId: userAccount.webId,

lib/models/email-service.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class EmailService {
119119
emailFromTemplate (templateName, data) {
120120
let template = this.readTemplate(templateName)
121121

122-
return template.render(data)
122+
return Object.assign({}, template.render(data), data)
123123
}
124124

125125
/**

lib/requests/auth-request.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict'
2+
3+
class AuthRequest {
4+
static parseParameter (req, parameter) {
5+
let query = req.query || {}
6+
let body = req.body || {}
7+
let params = req.params || {}
8+
9+
return query[parameter] || body[parameter] || params[parameter] || null
10+
}
11+
}
12+
13+
module.exports = AuthRequest

lib/requests/create-account-request.js

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict'
22

3+
const AuthRequest = require('./auth-request')
34
const WebIdTlsCertificate = require('../models/webid-tls-certificate')
45
const debug = require('../debug').accounts
56

@@ -16,7 +17,7 @@ const debug = require('../debug').accounts
1617
*
1718
* @class CreateAccountRequest
1819
*/
19-
class CreateAccountRequest {
20+
class CreateAccountRequest extends AuthRequest {
2021
/**
2122
* @param [options={}] {Object}
2223
* @param [options.accountManager] {AccountManager}
@@ -27,6 +28,7 @@ class CreateAccountRequest {
2728
* this url on successful account creation
2829
*/
2930
constructor (options) {
31+
super()
3032
this.accountManager = options.accountManager
3133
this.userAccount = options.userAccount
3234
this.session = options.session
@@ -55,7 +57,7 @@ class CreateAccountRequest {
5557
let locals = req.app.locals
5658
let accountManager = locals.accountManager
5759
let authMethod = locals.authMethod
58-
let returnToUrl = CreateAccountRequest.parseReturnUrl(req)
60+
let returnToUrl = this.parseParameter(req, 'returnToUrl')
5961
let userAccount = accountManager.userAccountFrom(req.body)
6062

6163
let options = {
@@ -90,15 +92,6 @@ class CreateAccountRequest {
9092
response.render('account/register', params)
9193
}
9294

93-
static parseReturnUrl (req) {
94-
let body = req.body || {}
95-
if (body.returnToUrl) { return req.body.returnToUrl }
96-
97-
if (req.query && req.query.returnToUrl) { return req.query.returnToUrl }
98-
99-
return null
100-
}
101-
10295
static post (req, res) {
10396
let request
10497
let returnToUrl = req.body.returnToUrl

0 commit comments

Comments
 (0)