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 (
-
-
-
+
+
+
);
};
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,
- },
});
};