diff --git a/apps/web/src/components/forms/send-confirmation-email.tsx b/apps/web/src/components/forms/send-confirmation-email.tsx index ee073d063..33247bf9f 100644 --- a/apps/web/src/components/forms/send-confirmation-email.tsx +++ b/apps/web/src/components/forms/send-confirmation-email.tsx @@ -13,6 +13,7 @@ import { FormField, FormItem, FormLabel, + FormMessage, } from '@documenso/ui/primitives/form/form'; import { Input } from '@documenso/ui/primitives/input'; import { useToast } from '@documenso/ui/primitives/use-toast'; @@ -63,31 +64,32 @@ export const SendConfirmationEmailForm = ({ className }: SendConfirmationEmailFo }; return ( -
-
- -
- ( - - Email address - - - - - )} - /> -
- -
- -
+ + + ); }; diff --git a/packages/lib/next-auth/auth-options.ts b/packages/lib/next-auth/auth-options.ts index d28506ca3..723b9cd7b 100644 --- a/packages/lib/next-auth/auth-options.ts +++ b/packages/lib/next-auth/auth-options.ts @@ -13,6 +13,7 @@ import { IdentityProvider, UserSecurityAuditLogType } from '@documenso/prisma/cl import { isTwoFactorAuthenticationEnabled } from '../server-only/2fa/is-2fa-availble'; import { validateTwoFactorAuthentication } from '../server-only/2fa/validate-2fa'; +import { getLastVerificationToken } from '../server-only/user/get-last-verification-token'; import { getUserByEmail } from '../server-only/user/get-user-by-email'; import { sendConfirmationToken } from '../server-only/user/send-confirmation-token'; import { extractNextAuthRequestMetadata } from '../universal/extract-request-metadata'; @@ -92,12 +93,19 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = { } if (!user.emailVerified) { - const totalUserVerificationTokens = user.VerificationToken.length; - const lastUserVerificationToken = user.VerificationToken[totalUserVerificationTokens - 1]; + const [lastUserVerificationToken] = await getLastVerificationToken({ userId: user.id }); + + if (!lastUserVerificationToken) { + await sendConfirmationToken({ email }); + throw new Error(ErrorCode.UNVERIFIED_EMAIL); + } + const expiredToken = DateTime.fromJSDate(lastUserVerificationToken.expires) <= DateTime.now(); + const lastSentToken = DateTime.fromJSDate(lastUserVerificationToken.createdAt); + const sentWithinLastHour = DateTime.now().minus({ hours: 1 }) <= lastSentToken; - if (totalUserVerificationTokens < 1 || expiredToken) { + if (expiredToken || !sentWithinLastHour) { await sendConfirmationToken({ email }); } diff --git a/packages/lib/server-only/user/get-last-verification-token.ts b/packages/lib/server-only/user/get-last-verification-token.ts new file mode 100644 index 000000000..279a1fcfd --- /dev/null +++ b/packages/lib/server-only/user/get-last-verification-token.ts @@ -0,0 +1,27 @@ +import { prisma } from '@documenso/prisma'; + +export interface GetLastVerificationTokenOptions { + userId: number; +} + +export const getLastVerificationToken = async ({ userId }: GetLastVerificationTokenOptions) => { + const user = await prisma.user.findFirstOrThrow({ + where: { + id: userId, + }, + include: { + VerificationToken: { + select: { + expires: true, + createdAt: true, + }, + orderBy: { + createdAt: 'desc', + }, + take: 1, + }, + }, + }); + + return user.VerificationToken; +}; diff --git a/packages/lib/server-only/user/get-user-by-email.ts b/packages/lib/server-only/user/get-user-by-email.ts index 8c61202a2..0a2ef8d16 100644 --- a/packages/lib/server-only/user/get-user-by-email.ts +++ b/packages/lib/server-only/user/get-user-by-email.ts @@ -9,8 +9,5 @@ export const getUserByEmail = async ({ email }: GetUserByEmailOptions) => { where: { email: email.toLowerCase(), }, - include: { - VerificationToken: true, - }, }); };