Merge pull request #435 from coralproject/reset-password-redirect

redirect to the parent url when resetting your password
This commit is contained in:
Riley Davis
2017-03-23 12:59:31 -06:00
committed by GitHub
5 changed files with 34 additions and 13 deletions
+3 -1
View File
@@ -3,6 +3,7 @@ import translations from './../translations';
const lang = new I18n(translations);
import * as actions from '../constants/auth';
import coralApi, {base} from '../helpers/response';
import {pym} from 'coral-framework';
// Dialog Actions
export const showSignInDialog = (offset = 0) => ({type: actions.SHOW_SIGNIN_DIALOG, offset});
@@ -135,7 +136,8 @@ const forgotPassowordFailure = () => ({type: actions.FETCH_FORGOT_PASSWORD_FAILU
export const fetchForgotPassword = email => (dispatch) => {
dispatch(forgotPassowordRequest(email));
coralApi('/account/password/reset', {method: 'POST', body: {email}})
const redirectUri = pym.parentUrl || location.href;
coralApi('/account/password/reset', {method: 'POST', body: {email, loc: redirectUri}})
.then(() => dispatch(forgotPassowordSuccess()))
.catch(error => dispatch(forgotPassowordFailure(error)));
};
+1 -1
View File
@@ -12,7 +12,7 @@ router.get('/password-reset', (req, res) => {
// TODO: store the redirect uri in the token or something fancy.
// admins and regular users should probably be redirected to different places.
res.render('admin/password-reset', {redirectUri: process.env.TALK_ROOT_URL});
res.render('admin/password-reset');
});
router.get('*', (req, res) => {
+6 -6
View File
@@ -41,14 +41,14 @@ router.post('/email/verify', (req, res, next) => {
* if it does, create a JWT and send an email
*/
router.post('/password/reset', (req, res, next) => {
const {email} = req.body;
const {email, loc} = req.body;
if (!email) {
return next('you must submit an email when requesting a password.');
}
UsersService
.createPasswordResetToken(email)
.createPasswordResetToken(email, loc)
.then((token) => {
// Check to see if the token isn't defined.
@@ -101,11 +101,11 @@ router.put('/password/reset', (req, res, next) => {
UsersService
.verifyPasswordResetToken(token)
.then((user) => {
return UsersService.changePassword(user.id, password);
.then(([user, loc]) => {
return Promise.all([UsersService.changePassword(user.id, password), loc]);
})
.then(() => {
res.status(204).end();
.then(([ , loc]) => {
res.json({redirect: loc});
})
.catch(() => {
next(authorization.ErrNotAuthorized);
+23 -4
View File
@@ -1,4 +1,5 @@
const bcrypt = require('bcrypt');
const url = require('url');
const jwt = require('jsonwebtoken');
const Wordlist = require('./wordlist');
const errors = require('../errors');
@@ -13,6 +14,7 @@ const USER_ROLES = require('../models/user').USER_ROLES;
const RECAPTCHA_WINDOW_SECONDS = 60 * 10; // 10 minutes.
const RECAPTCHA_INCORRECT_TRIGGER = 5; // after 3 incorrect attempts, recaptcha will be required.
const SettingsService = require('./settings');
const ActionsService = require('./actions');
// In the event that the TALK_SESSION_SECRET is missing but we are testing, then
@@ -478,15 +480,18 @@ module.exports = class UsersService {
* Creates a JWT from a user email. Only works for local accounts.
* @param {String} email of the local user
*/
static createPasswordResetToken(email) {
static createPasswordResetToken(email, loc) {
if (!email || typeof email !== 'string') {
return Promise.reject('email is required when creating a JWT for resetting passord');
}
email = email.toLowerCase();
return UserModel.findOne({profiles: {$elemMatch: {id: email}}})
.then((user) => {
return Promise.all([
UserModel.findOne({profiles: {$elemMatch: {id: email}}}),
SettingsService.retrieve()
])
.then(([user, settings]) => {
if (!user) {
// Since we don't want to reveal that the email does/doesn't exist
@@ -495,9 +500,21 @@ module.exports = class UsersService {
return;
}
let redirectDomain;
try {
redirectDomain = url.parse(loc).hostname;
} catch (e) {
return Promise.reject('redirect location is invalid');
}
if (settings.domains.whitelist.indexOf(redirectDomain) === -1) {
return Promise.reject('redirect location is not on the list of acceptable domains');
}
const payload = {
jti: uuid.v4(),
email,
loc,
userId: user.id,
version: user.__v
};
@@ -542,7 +559,9 @@ module.exports = class UsersService {
})
// TODO: add search by __v as well
.then((decoded) => UsersService.findById(decoded.userId));
.then((decoded) => {
return Promise.all([UsersService.findById(decoded.userId), decoded.loc]);
});
}
/**
+1 -1
View File
@@ -126,7 +126,7 @@
},
data: JSON.stringify({password: password, token: location.hash.replace('#', '')})
}).then(function (success) {
location.href = '<%= redirectUri %>';
location.href = success.redirect;
}).catch(function (error) {
showError(error.responseText);
});