diff --git a/api/server/services/AuthService.js b/api/server/services/AuthService.js index 49c3c4a17d..680a506602 100644 --- a/api/server/services/AuthService.js +++ b/api/server/services/AuthService.js @@ -108,31 +108,46 @@ const sendVerificationEmail = async (user) => { */ const verifyEmail = async (req) => { const { email, token } = req.body; - let emailVerificationData = await findToken({ email: decodeURIComponent(email) }); + const decodedEmail = decodeURIComponent(email); + + const user = await findUser({ email: decodedEmail }, 'email _id emailVerified'); + + if (!user) { + logger.warn(`[verifyEmail] [User not found] [Email: ${decodedEmail}]`); + return new Error('User not found'); + } + + if (user.emailVerified) { + logger.info(`[verifyEmail] Email already verified [Email: ${decodedEmail}]`); + return { message: 'Email already verified', status: 'success' }; + } + + let emailVerificationData = await findToken({ email: decodedEmail }); if (!emailVerificationData) { - logger.warn(`[verifyEmail] [No email verification data found] [Email: ${email}]`); + logger.warn(`[verifyEmail] [No email verification data found] [Email: ${decodedEmail}]`); return new Error('Invalid or expired password reset token'); } const isValid = bcrypt.compareSync(token, emailVerificationData.token); if (!isValid) { - logger.warn(`[verifyEmail] [Invalid or expired email verification token] [Email: ${email}]`); + logger.warn( + `[verifyEmail] [Invalid or expired email verification token] [Email: ${decodedEmail}]`, + ); return new Error('Invalid or expired email verification token'); } const updatedUser = await updateUser(emailVerificationData.userId, { emailVerified: true }); if (!updatedUser) { - logger.warn(`[verifyEmail] [User not found] [Email: ${email}]`); - return new Error('User not found'); + logger.warn(`[verifyEmail] [User update failed] [Email: ${decodedEmail}]`); + return new Error('Failed to update user verification status'); } await deleteTokens({ token: emailVerificationData.token }); - logger.info(`[verifyEmail] Email verification successful. [Email: ${email}]`); - return { message: 'Email verification was successful' }; + logger.info(`[verifyEmail] Email verification successful [Email: ${decodedEmail}]`); + return { message: 'Email verification was successful', status: 'success' }; }; - /** * Register a new user. * @param {MongoUser} user diff --git a/client/src/components/Auth/VerifyEmail.tsx b/client/src/components/Auth/VerifyEmail.tsx index acb5abe5ad..0af2f41959 100644 --- a/client/src/components/Auth/VerifyEmail.tsx +++ b/client/src/components/Auth/VerifyEmail.tsx @@ -14,7 +14,6 @@ function RequestPasswordReset() { const [headerText, setHeaderText] = useState(''); const [showResendLink, setShowResendLink] = useState(false); const [verificationStatus, setVerificationStatus] = useState(false); - const token = useMemo(() => params.get('token') || '', [params]); const email = useMemo(() => params.get('email') || '', [params]); @@ -26,9 +25,8 @@ function RequestPasswordReset() { clearInterval(timer); navigate('/c/new', { replace: true }); return 0; - } else { - return prevCountdown - 1; } + return prevCountdown - 1; }); }, 1000); }, [navigate]); @@ -39,11 +37,10 @@ function RequestPasswordReset() { setVerificationStatus(true); countdownRedirect(); }, - onError: () => { + onError: (error: unknown) => { + setHeaderText(localize('com_auth_email_verification_failed') + ' ๐Ÿ˜ข'); setShowResendLink(true); setVerificationStatus(true); - setHeaderText(localize('com_auth_email_verification_failed') + ' ๐Ÿ˜ข'); - setCountdown(0); }, }); @@ -54,7 +51,6 @@ function RequestPasswordReset() { }, onError: () => { setHeaderText(localize('com_auth_email_resent_failed') + ' ๐Ÿ˜ข'); - countdownRedirect(); }, onMutate: () => setShowResendLink(false), }); @@ -64,26 +60,22 @@ function RequestPasswordReset() { }; useEffect(() => { - if (verifyEmailMutation.isLoading || verificationStatus) { + if (verificationStatus || verifyEmailMutation.isLoading) { return; } if (token && email) { - verifyEmailMutation.mutate({ - email, - token, - }); - return; - } else if (email) { - setHeaderText(localize('com_auth_email_verification_failed_token_missing') + ' ๐Ÿ˜ข'); + verifyEmailMutation.mutate({ email, token }); } else { - setHeaderText(localize('com_auth_email_verification_invalid') + ' ๐Ÿคจ'); + if (email) { + setHeaderText(localize('com_auth_email_verification_failed_token_missing') + ' ๐Ÿ˜ข'); + } else { + setHeaderText(localize('com_auth_email_verification_invalid') + ' ๐Ÿคจ'); + } + setShowResendLink(true); + setVerificationStatus(true); } - - setShowResendLink(true); - setVerificationStatus(true); - setCountdown(0); - }, [localize, token, email, verificationStatus, verifyEmailMutation]); + }, [token, email, verificationStatus, verifyEmailMutation]); const VerificationSuccess = () => (