From 3c878ea92db4e2efcb56d0f3f0dd1df255d850c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=81=E6=B2=BB=E5=B9=B3?= Date: Tue, 1 Nov 2016 04:51:25 +0800 Subject: [PATCH 1/5] Update resetPassword nonce key --- src/server/models/User.js | 4 ++-- src/server/routes/api.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/server/models/User.js b/src/server/models/User.js index e4fe0c2..9f58293 100644 --- a/src/server/models/User.js +++ b/src/server/models/User.js @@ -52,7 +52,7 @@ let UserSchema = new mongoose.Schema({ }, }, nonce: { - password: Number, + resetPassword: Number, }, lastLoggedInAt: Date, }, { @@ -83,7 +83,7 @@ UserSchema.methods.toVerifyEmailToken = function(cb) { UserSchema.methods.toResetPasswordToken = function(cb) { const user = { _id: this._id, - nonce: this.nonce.password, + nonce: this.nonce.resetPassword, }; const token = jwt.sign(user, configs.jwt.resetPassword.secret, { expiresIn: configs.jwt.resetPassword.expiresIn, diff --git a/src/server/routes/api.js b/src/server/routes/api.js index 2ff4a45..f211cdf 100644 --- a/src/server/routes/api.js +++ b/src/server/routes/api.js @@ -38,7 +38,7 @@ export default ({ app }) => { bodyParser.json, validate.form('user/ForgetPasswordForm'), validate.recaptcha, - userController.setNonce('password'), + userController.setNonce('resetPassword'), mailController.sendResetPasswordLink ); app.put('/api/users/password', @@ -47,7 +47,7 @@ export default ({ app }) => { 'resetPasswordToken', configs.jwt.resetPassword.secret ), - validate.verifyUserNonce('password'), + validate.verifyUserNonce('resetPassword'), validate.form('user/ResetPasswordForm'), userController.resetPassword ); From 8939a9edef8242b1960c01ed77d53aaa0bdac805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=81=E6=B2=BB=E5=B9=B3?= Date: Tue, 1 Nov 2016 06:15:10 +0800 Subject: [PATCH 2/5] Add nonce into email verification --- src/server/controllers/user.js | 18 +++++++++--------- src/server/models/User.js | 2 ++ src/server/routes/api.js | 1 + 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/server/controllers/user.js b/src/server/controllers/user.js index f1bba60..e702a23 100644 --- a/src/server/controllers/user.js +++ b/src/server/controllers/user.js @@ -40,6 +40,9 @@ export default { value: req.body.email, }, password: req.body.password, + nonce: { + verifyEmail: Math.random(), + }, }); user.save(handleDbError(res)((user) => { req.user = user; @@ -55,15 +58,12 @@ export default { }, verifyEmail(req, res) { - User.findById(req.decodedPayload._id, handleDbError(res)((user) => { - if (user.email.isVerified) { - return res.errors([Errors.TOKEN_REUSED]); - } - user.email.isVerified = true; - user.email.verifiedAt = new Date(); - user.save(handleDbError(res)(() => { - res.json({}); - })); + let { user } = req; + + user.email.isVerified = true; + user.email.verifiedAt = new Date(); + user.save(handleDbError(res)(() => { + res.json({}); })); }, diff --git a/src/server/models/User.js b/src/server/models/User.js index 9f58293..3154cae 100644 --- a/src/server/models/User.js +++ b/src/server/models/User.js @@ -52,6 +52,7 @@ let UserSchema = new mongoose.Schema({ }, }, nonce: { + verifyEmail: Number, resetPassword: Number, }, lastLoggedInAt: Date, @@ -73,6 +74,7 @@ UserSchema.methods.auth = function(password, cb) { UserSchema.methods.toVerifyEmailToken = function(cb) { const user = { _id: this._id, + nonce: this.nonce.verifyEmail, }; const token = jwt.sign(user, configs.jwt.verifyEmail.secret, { expiresIn: configs.jwt.verifyEmail.expiresIn, diff --git a/src/server/routes/api.js b/src/server/routes/api.js index f211cdf..1180273 100644 --- a/src/server/routes/api.js +++ b/src/server/routes/api.js @@ -31,6 +31,7 @@ export default ({ app }) => { 'verifyEmailToken', configs.jwt.verifyEmail.secret ), + validate.verifyUserNonce('verifyEmail'), userController.verifyEmail ); app.post('/api/users/login', bodyParser.json, userController.login); From f18856450f7be13b8d68310fe76e5b14330c15f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=81=E6=B2=BB=E5=B9=B3?= Date: Tue, 1 Nov 2016 06:19:18 +0800 Subject: [PATCH 3/5] Add VerifyEmailForm --- .../components/forms/user/VerifyEmailForm.js | 140 ++++++++++++++++++ src/common/constants/FormNames.js | 1 + src/server/controllers/formValidation.js | 19 +++ src/server/routes/api.js | 5 + 4 files changed, 165 insertions(+) create mode 100644 src/common/components/forms/user/VerifyEmailForm.js diff --git a/src/common/components/forms/user/VerifyEmailForm.js b/src/common/components/forms/user/VerifyEmailForm.js new file mode 100644 index 0000000..6c9e191 --- /dev/null +++ b/src/common/components/forms/user/VerifyEmailForm.js @@ -0,0 +1,140 @@ +import React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { push } from 'react-router-redux'; +import { Field, reduxForm } from 'redux-form'; +import Alert from 'react-bootstrap/lib/Alert'; +import Button from 'react-bootstrap/lib/Button'; +// import validator from 'validator'; +import FormNames from '../../../constants/FormNames'; +import { validateForm } from '../../../actions/formActions'; +import { pushErrors } from '../../../actions/errorActions'; +import { Form, FormField, FormFooter } from '../../utils/BsForm'; +import configs from '../../../../../configs/project/client'; + +export let validate = (values) => { + let errors = {}; + + // if (values.email && !validator.isEmail(values.email)) { + // errors.email = 'Not an email'; + // } + + if (!values.email) { + errors.email = 'Required'; + } + + if (configs.recaptcha && !values.recaptcha) { + errors.recaptcha = 'Required'; + } + + return errors; +}; + +let asyncValidate = (values, dispatch) => { + return dispatch(validateForm( + FormNames.USER_VERIFY_EMAIL, + 'email', + values.email + )).then((json) => { + let validationError = {}; + if (!json.isPassed) { + validationError.email = json.message; + throw validationError; + } + }); +}; + +class VerifyEmailForm extends Component { + constructor() { + super(); + this.handleSubmit = this._handleSubmit.bind(this); + this.handleCancleClick = this._handleCancleClick.bind(this); + } + + componentDidMount() { + let { email, initialize } = this.props; + + if (email) { + initialize({ email }); + } + } + + _handleSubmit(formData) { + } + + _handleCancleClick() { + let { onCancel, dispatch } = this.props; + + if (onCancel) { + onCancel(); + } else { + dispatch(push('/')); + } + } + + render() { + const { + email, + handleSubmit, + submitSucceeded, + submitFailed, + error, + pristine, + submitting, + invalid, + } = this.props; + + return ( +
+ {submitSucceeded && ( + A reset link is sent + )} + {submitFailed && error && ({error})} + + + +