From 6e2b05f8354eecc2a679240a6fd85d838d67c205 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Mon, 5 Jun 2023 14:36:20 +0000 Subject: [PATCH] Change password in database to new reset password --- apps/web/components/reset-password.tsx | 37 ++++++++++++++ apps/web/pages/api/auth/forgot-password.ts | 3 +- apps/web/pages/api/auth/reset-password.ts | 49 ++++++++++++------- .../auth/{reset.tsx => reset/[token].tsx} | 2 +- packages/lib/mail/sendResetPassword.ts | 5 +- 5 files changed, 71 insertions(+), 25 deletions(-) rename apps/web/pages/auth/{reset.tsx => reset/[token].tsx} (91%) diff --git a/apps/web/components/reset-password.tsx b/apps/web/components/reset-password.tsx index 88f3cb4d0..27e214574 100644 --- a/apps/web/components/reset-password.tsx +++ b/apps/web/components/reset-password.tsx @@ -1,4 +1,5 @@ import Link from "next/link"; +import { useRouter } from "next/router"; import { Button } from "@documenso/ui"; import Logo from "./logo"; import { ArrowLeftIcon } from "@heroicons/react/24/outline"; @@ -11,14 +12,50 @@ interface IResetPassword { } export default function ResetPassword(props: any) { + const router = useRouter(); + const { token } = router.query; + const methods = useForm(); const { register, formState, watch } = methods; const password = watch("password", ""); const onSubmit = async (values: IResetPassword) => { + await toast.promise( + fetch(`/api/auth/reset-password`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ password: values.password, token }), + }), + { + loading: "Resetting...", + success: `Reset password successful`, + error: "Could not reset password :/", + } + ); + console.log(values); }; + if (!token) { + return ( +
+
+
+ +

+ Reset Password +

+

+ The token you provided is invalid. Please try again. +

+
+
+
+ ); + } + return ( <>
diff --git a/apps/web/pages/api/auth/forgot-password.ts b/apps/web/pages/api/auth/forgot-password.ts index 221be3d1c..ef2b218d7 100644 --- a/apps/web/pages/api/auth/forgot-password.ts +++ b/apps/web/pages/api/auth/forgot-password.ts @@ -24,7 +24,6 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) { } const token = crypto.randomBytes(64).toString("hex"); - const passwordResetToken = await prisma.passwordResetToken.create({ data: { token, @@ -34,7 +33,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) { await sendResetPassword(user, passwordResetToken.token); - res.status(201).end(); + res.status(200).json({ message: "Password reset email sent." }); } export default defaultHandler({ diff --git a/apps/web/pages/api/auth/reset-password.ts b/apps/web/pages/api/auth/reset-password.ts index 221be3d1c..ad6ef7a49 100644 --- a/apps/web/pages/api/auth/reset-password.ts +++ b/apps/web/pages/api/auth/reset-password.ts @@ -1,40 +1,53 @@ import { NextApiRequest, NextApiResponse } from "next"; +import { hashPassword } from "@documenso/lib/auth"; import { sendResetPassword } from "@documenso/lib/mail"; import { defaultHandler, defaultResponder } from "@documenso/lib/server"; import prisma from "@documenso/prisma"; -import crypto from "crypto"; async function postHandler(req: NextApiRequest, res: NextApiResponse) { - const { email } = req.body; - const cleanEmail = email.toLowerCase(); + const { token, password } = req.body; - if (!cleanEmail || !cleanEmail.includes("@")) { - res.status(422).json({ message: "Invalid email" }); + if (!token) { + res.status(422).json({ message: "Invalid token" }); return; } - const user = await prisma.user.findFirst({ + const foundToken = await prisma.passwordResetToken.findUnique({ where: { - email: cleanEmail, + token, + }, + include: { + User: true, }, }); - if (!user) { - return res.status(400).json({ message: "No user found with this email." }); + if (!foundToken) { + return res.status(400).json({ message: "Invalid token." }); } - const token = crypto.randomBytes(64).toString("hex"); + const hashedPassword = await hashPassword(password); - const passwordResetToken = await prisma.passwordResetToken.create({ - data: { - token, - userId: user.id, - }, - }); + const transaction = await prisma.$transaction([ + prisma.user.update({ + where: { + id: foundToken.userId, + }, + data: { + password: hashedPassword, + }, + }), + prisma.passwordResetToken.delete({ + where: { + token, + }, + }), + ]); - await sendResetPassword(user, passwordResetToken.token); + if (!transaction) { + return res.status(500).json({ message: "Error resetting password." }); + } - res.status(201).end(); + res.status(200).json({ message: "Password reset successful." }); } export default defaultHandler({ diff --git a/apps/web/pages/auth/reset.tsx b/apps/web/pages/auth/reset/[token].tsx similarity index 91% rename from apps/web/pages/auth/reset.tsx rename to apps/web/pages/auth/reset/[token].tsx index efbb4372b..fffe7c3c7 100644 --- a/apps/web/pages/auth/reset.tsx +++ b/apps/web/pages/auth/reset/[token].tsx @@ -1,6 +1,6 @@ import Head from "next/head"; import { getUserFromToken } from "@documenso/lib/server"; -import ResetPassword from "../../components/reset-password"; +import ResetPassword from "../../../components/reset-password"; export default function ResetPasswordPage(props: any) { return ( diff --git a/packages/lib/mail/sendResetPassword.ts b/packages/lib/mail/sendResetPassword.ts index fd918b470..32e098a4c 100644 --- a/packages/lib/mail/sendResetPassword.ts +++ b/packages/lib/mail/sendResetPassword.ts @@ -7,10 +7,7 @@ export const sendResetPassword = async (user: User, token: string) => { await sendMail( user.email, "Forgot password?", - resetPasswordTemplate( - `${NEXT_PUBLIC_WEBAPP_URL}/api/auth/reset/${token}`, - "Reset Your Password" - ) + resetPasswordTemplate(`${NEXT_PUBLIC_WEBAPP_URL}/auth/reset/${token}`, "Reset Your Password") ).catch((err) => { throw err; });