Don't send plaintext reset passwords via email (#8457)

* start work to avoid sending reset password in plaintext via email

* start checking parameters

* fix new password reset email

* render error if password reset code is missing or invalid

* implement POST route, conversion to bcrypt and messages

* add auth.local.passwordResetCode field

* add failing tests, move reset code validation func to lib, fixes, remove old tests

* fix unit tests

* fix page rendering and add integration tests

* fix password reset page

* add integration test

* fix string

* fix tests url
This commit is contained in:
Matteo Pagliazzi
2017-02-14 18:08:31 +01:00
committed by GitHub
parent c6c6632405
commit d30e7b9251
12 changed files with 690 additions and 44 deletions

View File

@@ -1,6 +1,9 @@
// Utilities for working with passwords
import crypto from 'crypto';
import bcrypt from 'bcrypt';
import { decrypt } from './encryption';
import moment from 'moment';
import { model as User } from '../models/user';
const BCRYPT_SALT_ROUNDS = 10;
@@ -63,3 +66,37 @@ export async function convertToBcrypt (user, plainTextPassword) {
user.auth.local.passwordHashMethod = 'bcrypt';
user.auth.local.hashed_password = await bcryptHash(plainTextPassword); // eslint-disable-line camelcase
}
// Returns the user if a valid password reset code is supplied, otherwise false
export async function validatePasswordResetCodeAndFindUser (code) {
let isCodeValid = true;
let userId;
let user;
let decryptedPasswordResetCode;
// wrapping the code in a try to be able to handle the error here
try {
decryptedPasswordResetCode = JSON.parse(decrypt(code || 'invalid')); // also catches missing code
userId = decryptedPasswordResetCode.userId;
let expiresAt = decryptedPasswordResetCode.expiresAt;
if (moment(expiresAt).isBefore(moment())) throw new Error();
} catch (err) {
isCodeValid = false;
}
if (isCodeValid) {
user = await User.findById(userId).exec();
// check if user is found and if it's an email & password account
if (!user || !user.auth || !user.auth.local || !user.auth.local.email) {
isCodeValid = false;
} else if (code !== user.auth.local.passwordResetCode) {
// Make sure only the last code can be used
isCodeValid = false;
}
}
return isCodeValid ? user : false;
}