diff --git a/models/user.js b/models/user.js index 336486a54..8c478fbb4 100644 --- a/models/user.js +++ b/models/user.js @@ -2,6 +2,7 @@ const mongoose = require('../mongoose'); const uuid = require('uuid'); const bcrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); +const debug = require('debug')('talk:users'); // SALT_ROUNDS is the number of rounds that the bcrypt algorithm will run // through during the salting process. @@ -435,23 +436,35 @@ UserService.findPublicByIdArray = (ids) => { */ UserService.createPasswordResetToken = function (email) { if (!email || typeof email !== 'string') { + debug('the email was missing to createPasswordResetToken. you sent %s', email); return Promise.reject('email is required when creating a JWT for resetting passord'); } + email = email.toLowerCase(); + debug('about to look up user by email %s', email); + return UserModel.findOne({profiles: {$elemMatch: {id: email}}}) .then(user => { if (user === null) { + debug('user was null on createPasswordResetToken lookup'); // since we don't want to reveal that the email does/doesn't exist // just go ahead and resolve the Promise with null and check in the endpoint return Promise.resolve(null); } + debug('found the user! %s going to create the token', JSON.stringify(user, null 2)); + const payload = {email, jti: uuid.v4(), userId: user.id, version: user.__v}; + + debug('created payload %s', JSON.stringify(payload)); + const token = jwt.sign(payload, process.env.TALK_SESSION_SECRET, {expiresIn: '1d'}); + debug('successfully created the token %s', token); + return token; }); }; diff --git a/routes/api/user/index.js b/routes/api/user/index.js index 1765809ad..05ebf7b61 100644 --- a/routes/api/user/index.js +++ b/routes/api/user/index.js @@ -8,6 +8,7 @@ const path = require('path'); const resetEmailFile = fs.readFileSync(path.resolve(__dirname, '../../../views/password-reset-email.ejs')); const resetEmailTemplate = ejs.compile(resetEmailFile.toString()); const authorization = require('../../../middleware/authorization'); +const debug = require('debug')('talk:users'); router.get('/', authorization.needed('admin'), (req, res, next) => { const { @@ -104,6 +105,8 @@ router.post('/update-password', (req, res, next) => { router.post('/request-password-reset', (req, res, next) => { const {email} = req.body; + debug('/request-password-reset body %s', JSON.stringify(req.body, null, 2)); + if (!email) { return next('you must submit an email when requesting a password.'); } @@ -112,6 +115,7 @@ router.post('/request-password-reset', (req, res, next) => { .createPasswordResetToken(email) .then(token => { if (token === null) { + debug('back in the route. the token was null for some reason %s', token); return Promise.resolve('the email was not found in the db.'); } @@ -125,10 +129,15 @@ router.post('/request-password-reset', (req, res, next) => { rootURL: process.env.TALK_ROOT_URL }) }; + + debug('about to send a simple email with the options %s', JSON.stringify(options, null, 2)); + return mailer.sendSimple(options); }) .then(() => { + debug('password reset email sent successfully'); + // we want to send a 204 regardless of the user being found in the db // if we fail on missing emails, it would reveal if people are registered or not. res.status(204).end(); @@ -136,6 +145,8 @@ router.post('/request-password-reset', (req, res, next) => { .catch(error => { const errorMsg = typeof error === 'string' ? error : error.message; + debug('there was an error sending your email %s', error); + res.status(500).json({error: errorMsg}); }); });