create password reset token from FE, start to add flash text
This commit is contained in:
parent
ea6d30c430
commit
5aa5032961
7 changed files with 93 additions and 21 deletions
|
@ -94,6 +94,7 @@ export const DETECT_INFINITE_LOOPS = 'DETECT_INFINITE_LOOPS';
|
||||||
export const RESET_INFINITE_LOOPS = 'RESET_INFINITE_LOOPS';
|
export const RESET_INFINITE_LOOPS = 'RESET_INFINITE_LOOPS';
|
||||||
|
|
||||||
export const RESET_PASSWORD_INITIATE = 'RESET_PASSWORD_INITIATE';
|
export const RESET_PASSWORD_INITIATE = 'RESET_PASSWORD_INITIATE';
|
||||||
|
export const RESET_PASSWORD_RESET = 'RESET_PASSWORD_RESET';
|
||||||
|
|
||||||
// eventually, handle errors more specifically and better
|
// eventually, handle errors more specifically and better
|
||||||
export const ERROR = 'ERROR';
|
export const ERROR = 'ERROR';
|
||||||
|
|
|
@ -13,12 +13,12 @@ function ResetPasswordForm(props) {
|
||||||
{...email}
|
{...email}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
<input type="submit" disabled={submitting || invalid || pristine} value="Send email to reset password" aria-label="Send email to reset password" />
|
<input type="submit" disabled={submitting || invalid || pristine} value="Send password reset email" aria-label="Send email to reset password" />
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetPassword.propTypes = {
|
ResetPasswordForm.propTypes = {
|
||||||
fields: PropTypes.shape({
|
fields: PropTypes.shape({
|
||||||
email: PropTypes.object.isRequired
|
email: PropTypes.object.isRequired
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
@ -27,6 +27,6 @@ ResetPassword.propTypes = {
|
||||||
submitting: PropTypes.bool,
|
submitting: PropTypes.bool,
|
||||||
invalid: PropTypes.bool,
|
invalid: PropTypes.bool,
|
||||||
pristine: PropTypes.bool
|
pristine: PropTypes.bool
|
||||||
}
|
};
|
||||||
|
|
||||||
export default ResetPasswordForm;
|
export default ResetPasswordForm;
|
||||||
|
|
|
@ -1,29 +1,51 @@
|
||||||
import React from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import * as UserActions from '../../User/actions';
|
import * as UserActions from '../../User/actions';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { reduxForm } from 'redux-form';
|
import { reduxForm } from 'redux-form';
|
||||||
import ResetPasswordForm from './ResetPasswordForm';
|
import ResetPasswordForm from './ResetPasswordForm';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
class ResetPasswordView extends React.Component {
|
class ResetPasswordView extends React.Component {
|
||||||
|
componentWillMount() {
|
||||||
|
this.props.resetPasswordReset();
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.refs.resetPassword.focus();
|
this.refs.resetPassword.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const resetPasswordClass = classNames({
|
||||||
|
'reset-password': true,
|
||||||
|
'reset-password--submitted': this.props.user.passwordResetInitialized
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<div className="reset-password" ref="resetPassword" tabIndex="0">
|
<div className={resetPasswordClass} ref="resetPassword" tabIndex="0">
|
||||||
<h1>Reset Your Password</h1>
|
<h1>Reset Your Password</h1>
|
||||||
<ResetPasswordForm {...this.props} />
|
<ResetPasswordForm {...this.props} />
|
||||||
|
<p className="reset-password__submitted">
|
||||||
|
Your password reset email should arrive shortly. If you don't see it, check
|
||||||
|
in your spam folder as sometimes it can end up there.
|
||||||
|
</p>
|
||||||
|
<p className="form__navigation-options">
|
||||||
<Link className="form__login-button" to="/login">Login</Link>
|
<Link className="form__login-button" to="/login">Login</Link>
|
||||||
or
|
or
|
||||||
<Link className="form__signup-button" to="/signup">Sign up</Link>
|
<Link className="form__signup-button" to="/signup">Sign up</Link>
|
||||||
|
</p>
|
||||||
<Link className="form__cancel-button" to="/">Cancel</Link>
|
<Link className="form__cancel-button" to="/">Cancel</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResetPasswordView.propTypes = {
|
||||||
|
resetPasswordReset: PropTypes.func.isRequired,
|
||||||
|
user: PropTypes.shape({
|
||||||
|
passwordResetInitialized: PropTypes.bool
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
user: state.user
|
user: state.user
|
||||||
|
@ -37,13 +59,13 @@ function mapDispatchToProps(dispatch) {
|
||||||
function validate(formProps) {
|
function validate(formProps) {
|
||||||
const errors = {};
|
const errors = {};
|
||||||
if (!formProps.email) {
|
if (!formProps.email) {
|
||||||
errors.email = 'Please enter an email'
|
errors.email = 'Please enter an email';
|
||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default reduxForm({
|
export default reduxForm({
|
||||||
form: 'reset-password',
|
form: 'reset-password',
|
||||||
fields: 'email',
|
fields: ['email'],
|
||||||
validate
|
validate
|
||||||
}, mapStateToProps, mapDispatchToProps)(ResetPasswordView);
|
}, mapStateToProps, mapDispatchToProps)(ResetPasswordView);
|
||||||
|
|
|
@ -105,15 +105,23 @@ export function logoutUser() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initiatePasswordReset(formValues) {
|
export function initiateResetPassword(formValues) {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
axios.post(`${ROOT_URL}/reset-password`, formValues, { withCredentials: true })
|
axios.post(`${ROOT_URL}/reset-password`, formValues, { withCredentials: true })
|
||||||
.then(response => {
|
.then(() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ActionTypes.RESET_PASSWORD_INITIATE
|
type: ActionTypes.RESET_PASSWORD_INITIATE
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(response => dispatch({ActionTypes.ERROR}));
|
.catch(response => dispatch({
|
||||||
}
|
type: ActionTypes.ERROR,
|
||||||
|
message: response.data
|
||||||
|
}));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resetPasswordReset() {
|
||||||
|
return {
|
||||||
|
type: ActionTypes.RESET_PASSWORD_RESET
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ const user = (state = { authenticated: false }, action) => {
|
||||||
return {
|
return {
|
||||||
authenticated: false
|
authenticated: false
|
||||||
};
|
};
|
||||||
|
case ActionTypes.RESET_PASSWORD_INITIATE:
|
||||||
|
return Object.assign(state, {}, { resetPasswordInitiate: true });
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,3 +7,20 @@
|
||||||
padding: #{20 / $base-font-size}rem;
|
padding: #{20 / $base-font-size}rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reset-password-form__email-input {
|
||||||
|
width: #{300 / $base-font-size}rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-password-form__field {
|
||||||
|
margin: #{20 / $base-font-size}rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-password__submitted {
|
||||||
|
width: #{300 / $base-font-size}rem;
|
||||||
|
display: none;
|
||||||
|
.reset-password--submitted {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import User from '../models/user';
|
import User from '../models/user';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
import async from 'async';
|
||||||
|
|
||||||
export function createUser(req, res, next) {
|
export function createUser(req, res, next) {
|
||||||
const user = new User({
|
const user = new User({
|
||||||
|
@ -74,11 +76,31 @@ export function updatePreferences(req, res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetPasswordInitiate(req, res) {
|
export function resetPasswordInitiate(req, res) {
|
||||||
|
async.waterfall([
|
||||||
|
(done) => {
|
||||||
|
crypto.randomBytes(20, function(err, buf) {
|
||||||
|
var token = buf.toString('hex');
|
||||||
|
done(err, token);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(token, done) => {
|
||||||
User.findOne({ email: req.body.email }, (err, user) => {
|
User.findOne({ email: req.body.email }, (err, user) => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return res.json({message: 'If the email is registered with the editor, an email has been sent.'});
|
return res.json({success: true, message: 'If the email is registered with the editor, an email has been sent.'});
|
||||||
}
|
}
|
||||||
user.resetPasswordToken = token;
|
user.resetPasswordToken = token;
|
||||||
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
|
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
|
||||||
|
|
||||||
|
user.save(function(err) {
|
||||||
|
done(err, token, user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
], (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return res.json({success: false});
|
||||||
|
}
|
||||||
|
return res.json({success: true, message: 'If the email is registered with the editor, an email has been sent.'});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue