diff --git a/common/models/user.js b/common/models/user.js index 56acb92ac5..fa61aca7ab 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -587,82 +587,66 @@ module.exports = function(User) { User.prototype.requestUpdateEmail = function requestUpdateEmail(newEmail) { const currentEmail = this.email; - return Observable.defer(() => { - const isOwnEmail = isTheSame(newEmail, currentEmail); - const sameUpdate = isTheSame(newEmail, this.newEmail); - const messageOrNull = getWaitMessage(this.emailVerifyTTL); - if (isOwnEmail) { - if (this.emailVerified) { - // email is already associated and verified with this account - throw wrapHandledError( - new Error('email is already verified'), - { - type: 'info', - message: `${newEmail} is already associated with this account.` - } - ); - } else if (!this.emailVerified && messageOrNull) { - // email is associated but unverified and - // email is within time limit - throw wrapHandledError( - new Error(), - { - type: 'info', - message: messageOrNull - } - ); - } - } - if (sameUpdate && messageOrNull) { - // trying to update with the same newEmail and - // confirmation email is still valid + const isOwnEmail = isTheSame(newEmail, currentEmail); + const sameUpdate = isTheSame(newEmail, this.newEmail); + const messageOrNull = getWaitMessage(this.emailVerifyTTL); + if (isOwnEmail) { + if (this.emailVerified) { + // email is already associated and verified with this account throw wrapHandledError( - new Error(), + new Error('email is already verified'), { type: 'info', - message: dedent` - We have already sent an email confirmation request to ${newEmail}. - Please check your inbox.` + message: `${newEmail} is already associated with this account.` } ); - } - if (!isEmail('' + newEmail)) { - throw createEmailError(); - } - // newEmail is not associated with this user, and - // this attempt to change email is the first or - // previous attempts have expired - return Observable.if( - () => isOwnEmail || (sameUpdate && messageOrNull), - Observable.empty(), - // defer prevents the promise from firing prematurely (before subscribe) - Observable.defer(() => User.doesExist(null, newEmail)) - ) - .do(exists => { - if (exists) { - // newEmail is not associated with this account, - // but is associated with different account + } else if (!this.emailVerified && messageOrNull) { + // email is associated but unverified and + // email is within time limit throw wrapHandledError( - new Error('email already in use'), + new Error(), { type: 'info', - message: - `${newEmail} is already associated with another account.` + message: messageOrNull } ); } + } + if (sameUpdate && messageOrNull) { + // trying to update with the same newEmail and + // confirmation email is still valid + throw wrapHandledError( + new Error(), + { + type: 'info', + message: dedent` + We have already sent an email confirmation request to ${newEmail}. + Please check your inbox.` + } + ); + } + if (!isEmail('' + newEmail)) { + throw createEmailError(); + } + // newEmail is not associated with this user, and + // this attempt to change email is the first or + // previous attempts have expired + + if (isOwnEmail || (sameUpdate && !messageOrNull)) { + const update = { + newEmail, + emailVerified: false, + emailVerifyTTL: new Date() + }; + return this.update$(update).toPromise() + .then(() => { + Object.assign(this, update); + return; }) - .flatMap(() => { - const update = { - newEmail, - emailVerified: false, - emailVerifyTTL: new Date() - }; - return this.update$(update) - .do(() => Object.assign(this, update)) - .flatMap(() => this.requestAuthEmail(false, newEmail)); - }); - }); + .then(() => this.requestAuthEmail(false, newEmail).toPromise()); + } else { + return 'Something unexpected happened whilst updating your email.'; + } }; function requestCompletedChallenges() { diff --git a/server/boot/authentication.js b/server/boot/authentication.js index cd9db97b8d..cf108fa3ba 100644 --- a/server/boot/authentication.js +++ b/server/boot/authentication.js @@ -38,6 +38,14 @@ module.exports = function enableAuthentication(app) { ifUserRedirect, (req, res) => res.redirect(301, '/auth/auth0')); + router.get( + '/update-email', + ifNoUserRedirectHome, + (req, res) => res.render('account/update-email', { + title: 'Update your email' + }) + ); + router.get('/signout', (req, res) => { req.logout(); req.session.destroy( (err) => { diff --git a/server/boot/settings.js b/server/boot/settings.js index f2d67628f4..4ccfa2944b 100644 --- a/server/boot/settings.js +++ b/server/boot/settings.js @@ -37,13 +37,9 @@ export default function settingsController(app) { .withMessage('Email format is invalid.') ]; - function updateMyEmail(req, res, next) { + function updateMyEmail(req, res) { const { user, body: { email } } = req; - return user.requestUpdateEmail(email) - .subscribe( - message => res.json({ message }), - next - ); + return res.json({ message: user.requestUpdateEmail(email) } ); } const updateMyCurrentChallengeValidators = [ diff --git a/server/middlewares/email-not-verified-notice.js b/server/middlewares/email-not-verified-notice.js index bdc430c433..d3d6f5012d 100644 --- a/server/middlewares/email-not-verified-notice.js +++ b/server/middlewares/email-not-verified-notice.js @@ -4,7 +4,8 @@ const ALLOWED_METHODS = ['GET']; const EXCLUDED_PATHS = [ '/api/flyers/findOne', '/signout', - '/settings/update-email' + '/update-email', + '/passwordless-change' ]; export default function emailNotVerifiedNotice() { @@ -23,7 +24,7 @@ export default function emailNotVerifiedNotice() { confirm. ` ); - res.redirect('/settings/update-email'); + res.redirect('/update-email'); return next; } }