From d95d6480125c898bda3f7acbd28a8bb0eaf8e4f4 Mon Sep 17 00:00:00 2001 From: Luke Vella Date: Mon, 23 Oct 2023 18:54:39 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20magic=20link=20consumed=20?= =?UTF-8?q?before=20user=20gets=20it=20(#909)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 Fix magic link consumed before user gets it * Improve abstraction * Remove extra comment --- apps/web/src/utils/auth.ts | 4 +- .../src/utils/auth/custom-prisma-adapter.ts | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/utils/auth/custom-prisma-adapter.ts diff --git a/apps/web/src/utils/auth.ts b/apps/web/src/utils/auth.ts index 23ed3ae08..f0817318b 100644 --- a/apps/web/src/utils/auth.ts +++ b/apps/web/src/utils/auth.ts @@ -1,4 +1,3 @@ -import { PrismaAdapter } from "@auth/prisma-adapter"; import { RegistrationTokenPayload } from "@rallly/backend"; import { decryptToken } from "@rallly/backend/session"; import { generateOtp, randomid } from "@rallly/backend/utils/nanoid"; @@ -17,13 +16,14 @@ import NextAuth, { import CredentialsProvider from "next-auth/providers/credentials"; import EmailProvider from "next-auth/providers/email"; +import { CustomPrismaAdapter } from "@/utils/auth/custom-prisma-adapter"; import { LegacyTokenProvider } from "@/utils/auth/legacy-token-provider"; import { mergeGuestsIntoUser } from "@/utils/auth/merge-user"; import { emailClient } from "@/utils/emails"; const getAuthOptions = (...args: GetServerSessionParams) => ({ - adapter: PrismaAdapter(prisma), + adapter: CustomPrismaAdapter(prisma), secret: process.env.SECRET_PASSWORD, session: { strategy: "jwt", diff --git a/apps/web/src/utils/auth/custom-prisma-adapter.ts b/apps/web/src/utils/auth/custom-prisma-adapter.ts new file mode 100644 index 000000000..71a52551c --- /dev/null +++ b/apps/web/src/utils/auth/custom-prisma-adapter.ts @@ -0,0 +1,38 @@ +import { PrismaAdapter } from "@auth/prisma-adapter"; +import { Prisma, PrismaClient } from "@prisma/client"; +import { Adapter } from "next-auth/adapters"; + +export function CustomPrismaAdapter(prisma: PrismaClient): Adapter { + const adapter = PrismaAdapter(prisma); + return { + ...adapter, + // NOTE: Some users have inboxes with spam filters that check all links before they are delivered. + // This means the verification link will be used before the user gets it. To get around this, we + // avoid deleting the verification token when it is used. Instead we delete all verification tokens + // for an email address when a new verification token is created. + async createVerificationToken(data) { + await prisma.verificationToken.deleteMany({ + where: { identifier: data.identifier }, + }); + + const verificationToken = await prisma.verificationToken.create({ + data, + }); + + return verificationToken; + }, + async useVerificationToken(identifier_token) { + try { + const verificationToken = await prisma.verificationToken.findUnique({ + where: { identifier_token }, + }); + return verificationToken; + } catch (error) { + // https://www.prisma.io/docs/reference/api-reference/error-reference#p2025 + if ((error as Prisma.PrismaClientKnownRequestError).code === "P2025") + return null; + throw error; + } + }, + }; +}