Add backend endpoint for resending confirmation email

This commit is contained in:
James Allen 2018-07-12 16:39:04 +01:00
parent 67e2f6f942
commit 33b28db061
5 changed files with 154 additions and 9 deletions

View file

@ -27,6 +27,16 @@ module.exports =
return callback(error) if error?
callback null, token
findValidTokenFromData: (use, data, callback = (error, token) ->) ->
db.tokens.findOne {
use: use,
data: data,
expiresAt: { $gt: new Date() },
usedAt: { $exists: false }
}, (error, token) ->
return callback(error) if error?
return callback null, token?.token
getValueFromTokenAndExpire: (use, token, callback = (error, data) ->)->
logger.log token_start: token.slice(0,8), "getting data from #{use} token"
now = new Date()

View file

@ -23,6 +23,17 @@ module.exports = UserEmailsConfirmationHandler =
confirmEmailUrl: "#{settings.siteUrl}/user/emails/confirm?token=#{token}"
EmailHandler.sendEmail emailTemplate, emailOptions, callback
resendConfirmationEmail: (user_id, email, callback = (error) ->) ->
OneTimeTokenHandler.findValidTokenFromData 'email_confirmation', { user_id, email }, (error, token) ->
return callback(error) if error?
if !token?
UserEmailsConfirmationHandler.sendConfirmationEmail user_id, email, callback
else
emailOptions =
to: email
confirmEmailUrl: "#{settings.siteUrl}/user/emails/confirm?token=#{token}"
EmailHandler.sendEmail 'confirmEmail', emailOptions, callback
confirmEmailFromToken: (token, callback = (error) ->) ->
logger.log {token_start: token.slice(0,8)}, 'confirming email from token'
OneTimeTokenHandler.getValueFromTokenAndExpire 'email_confirmation', token, (error, data) ->

View file

@ -61,6 +61,19 @@ module.exports = UserEmailsController =
return next(error) if error?
res.sendStatus 204
resendConfirmation: (req, res, next) ->
userId = AuthenticationController.getLoggedInUserId(req)
email = EmailHelper.parseEmail(req.body.email)
return res.sendStatus 422 unless email?
UserGetter.getUserByAnyEmail email, {_id:1}, (error, user) ->
return next(error) if error?
if !user? or user?._id?.toString() != userId
logger.log {userId, email, foundUserId: user?._id}, "email doesn't match logged in user"
return res.sendStatus 422
logger.log {userId, email}, 'resending email confirmation token'
UserEmailsConfirmationHandler.resendConfirmationEmail userId, email, (error) ->
return next(error) if error?
res.sendStatus 200
showConfirm: (req, res, next) ->
res.render 'user/confirm_email', {

View file

@ -115,6 +115,9 @@ module.exports = class Router
UserEmailsController.showConfirm
webRouter.post '/user/emails/confirm',
UserEmailsController.confirm
webRouter.post '/user/emails/resend_confirmation',
AuthenticationController.requireLogin(),
UserEmailsController.resendConfirmation
if Features.hasFeature 'affiliations'
webRouter.post '/user/emails',

View file

@ -17,7 +17,7 @@ describe "UserEmails", ->
token = null
async.series [
(cb) =>
@user.request {
@user.request {
method: 'POST',
url: '/user/emails',
json:
@ -45,7 +45,7 @@ describe "UserEmails", ->
token = tokens[0].token
cb()
(cb) =>
@user.request {
@user.request {
method: 'POST',
url: '/user/emails/confirm',
json:
@ -80,7 +80,7 @@ describe "UserEmails", ->
(cb) => @user2.login cb
(cb) =>
# Create email for first user
@user.request {
@user.request {
method: 'POST',
url: '/user/emails',
json: {@email}
@ -99,21 +99,21 @@ describe "UserEmails", ->
cb()
(cb) =>
# Delete the email from the first user
@user.request {
@user.request {
method: 'POST',
url: '/user/emails/delete',
json: {@email}
}, cb
(cb) =>
# Create email for second user
@user2.request {
@user2.request {
method: 'POST',
url: '/user/emails',
json: {@email}
}, cb
(cb) =>
# Original confirmation token should no longer work
@user.request {
@user.request {
method: 'POST',
url: '/user/emails/confirm',
json:
@ -158,7 +158,7 @@ describe "UserEmails", ->
token = null
async.series [
(cb) =>
@user.request {
@user.request {
method: 'POST',
url: '/user/emails',
json:
@ -183,12 +183,12 @@ describe "UserEmails", ->
db.tokens.update {
token: token
}, {
$set: {
$set: {
expiresAt: new Date(Date.now() - 1000000)
}
}, cb
(cb) =>
@user.request {
@user.request {
method: 'POST',
url: '/user/emails/confirm',
json:
@ -198,3 +198,111 @@ describe "UserEmails", ->
expect(response.statusCode).to.equal 404
cb()
], done
describe 'resending the confirmation', ->
it 'should resend the existing token', (done) ->
token = null
async.series [
(cb) =>
@user.request {
method: 'POST',
url: '/user/emails',
json:
email: 'reconfirmation-email@example.com'
}, (error, response, body) =>
return done(error) if error?
expect(response.statusCode).to.equal 204
cb()
(cb) =>
db.tokens.find {
use: 'email_confirmation',
'data.user_id': @user._id,
usedAt: { $exists: false }
}, (error, tokens) =>
# There should only be one confirmation token at the moment
expect(tokens.length).to.equal 1
expect(tokens[0].data.email).to.equal 'reconfirmation-email@example.com'
expect(tokens[0].data.user_id).to.equal @user._id
token = tokens[0].token
cb()
(cb) =>
@user.request {
method: 'POST',
url: '/user/emails/resend_confirmation',
json:
email: 'reconfirmation-email@example.com'
}, (error, response, body) =>
return done(error) if error?
expect(response.statusCode).to.equal 200
cb()
(cb) =>
db.tokens.find {
use: 'email_confirmation',
'data.user_id': @user._id,
usedAt: { $exists: false }
}, (error, tokens) =>
# There should still only be one confirmation token
expect(tokens.length).to.equal 1
expect(tokens[0].data.email).to.equal 'reconfirmation-email@example.com'
expect(tokens[0].data.user_id).to.equal @user._id
token = tokens[0].token
cb()
], done
it 'should create a new token if none exists', (done) ->
# This should only be for users that have sign up with their main
# emails before the confirmation system existed
token = null
async.series [
(cb) =>
db.tokens.remove {
use: 'email_confirmation',
'data.user_id': @user._id,
usedAt: { $exists: false }
}, cb
(cb) =>
@user.request {
method: 'POST',
url: '/user/emails/resend_confirmation',
json:
email: @user.email
}, (error, response, body) =>
return done(error) if error?
expect(response.statusCode).to.equal 200
cb()
(cb) =>
db.tokens.find {
use: 'email_confirmation',
'data.user_id': @user._id,
usedAt: { $exists: false }
}, (error, tokens) =>
# There should still only be one confirmation token
expect(tokens.length).to.equal 1
expect(tokens[0].data.email).to.equal @user.email
expect(tokens[0].data.user_id).to.equal @user._id
token = tokens[0].token
cb()
], done
it "should not allow reconfirmation if the email doesn't match the user", (done) ->
token = null
async.series [
(cb) =>
@user.request {
method: 'POST',
url: '/user/emails/resend_confirmation',
json:
email: 'non-matching-email@example.com'
}, (error, response, body) =>
return done(error) if error?
expect(response.statusCode).to.equal 422
cb()
(cb) =>
db.tokens.find {
use: 'email_confirmation',
'data.user_id': @user._id,
usedAt: { $exists: false }
}, (error, tokens) =>
expect(tokens.length).to.equal 0
cb()
], done