diff --git a/errors.js b/errors.js index 65f0a0d2b..53e8bf351 100644 --- a/errors.js +++ b/errors.js @@ -97,6 +97,16 @@ const ErrEmailVerificationToken = new APIError('token is required', { status: 400, }); +// ErrEmailAlreadyVerified is returned when the user tries to verify an email +// address that has already been verified. +const ErrEmailAlreadyVerified = new APIError( + 'email address is already verified', + { + translation_key: 'EMAIL_ALREADY_VERIFIED', + status: 409, + } +); + // ErrPasswordResetToken is returned in the event that the password reset is requested // without a token. const ErrPasswordResetToken = new APIError('token is required', { @@ -284,6 +294,7 @@ module.exports = { ErrCommentTooShort, ErrContainsProfanity, ErrEditWindowHasEnded, + ErrEmailAlreadyVerified, ErrEmailTaken, ErrEmailVerificationToken, ErrInstallLock, diff --git a/locales/en.yml b/locales/en.yml index 59689d1b1..08d64ddc8 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -206,6 +206,7 @@ en: error: COMMENT_PARENT_NOT_VISIBLE: "The comment that you're replying to has been removed or doesn't exist." EMAIL_VERIFICATION_TOKEN_INVALID: "Email verification token is invalid." + EMAIL_ALREADY_VERIFIED: "Email address already verified." PASSWORD_RESET_TOKEN_INVALID: "Your password reset link is invalid." COMMENT_TOO_SHORT: "Comments should be more than one character, please revise your comment and try again." NOT_AUTHORIZED: "You are not authorized to perform this action." diff --git a/routes/api/v1/account.js b/routes/api/v1/account.js index f7d8ea126..561134885 100644 --- a/routes/api/v1/account.js +++ b/routes/api/v1/account.js @@ -17,7 +17,11 @@ router.get('/', authorization.needed(), (req, res, next) => { * @param {Function} verifier the function used to verify the token, will throw on error * @param {Object} error the error object to send back in the event an error is found */ -const tokenCheck = (verifier, error) => async (req, res, next) => { +const tokenCheck = (verifier, error, ...whitelistedErrors) => async ( + req, + res, + next +) => { const { token = null, check = false } = req.body; if (check) { @@ -26,6 +30,10 @@ const tokenCheck = (verifier, error) => async (req, res, next) => { // Verify the token. await verifier(token); } catch (err) { + if (whitelistedErrors.includes(err)) { + return next(err); + } + // Log out the error, slurp it and send out the predefined error to the // error handler. console.error(err); @@ -48,7 +56,8 @@ router.post( '/email/verify', tokenCheck( UsersService.verifyEmailConfirmationToken, - errors.ErrEmailVerificationToken + errors.ErrEmailVerificationToken, + errors.ErrEmailAlreadyVerified ), async (req, res, next) => { const { token } = req.body; diff --git a/services/users.js b/services/users.js index af91cacc7..0617d5158 100644 --- a/services/users.js +++ b/services/users.js @@ -837,7 +837,7 @@ class UsersService { // Ensure that the user email hasn't already been verified. if (profile && profile.metadata && profile.metadata.confirmed_at) { - throw new Error('email address already confirmed'); + throw errors.ErrEmailAlreadyVerified; } return JWT_SECRET.sign( @@ -884,7 +884,7 @@ class UsersService { } if (profile.metadata && profile.metadata.confirmed_at !== null) { - throw errors.ErrEmailVerificationToken; + throw errors.ErrEmailAlreadyVerified; } return decoded;