diff --git a/apps/web/pages/api/auth/reset-password.ts b/apps/web/pages/api/auth/reset-password.ts
index 9cd81f2c7a..25dd1a2a35 100644
--- a/apps/web/pages/api/auth/reset-password.ts
+++ b/apps/web/pages/api/auth/reset-password.ts
@@ -1,6 +1,7 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { hashPassword } from "@calcom/features/auth/lib/hashPassword";
+import { validPassword } from "@calcom/features/auth/lib/validPassword";
import prisma from "@calcom/prisma";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -36,6 +37,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return res.status(400).json({ message: "Couldn't find an account for this email" });
}
+ if (!validPassword(rawPassword)) {
+ return res.status(400).json({ message: "Password does not meet the requirements" });
+ }
+
const hashedPassword = await hashPassword(rawPassword);
await prisma.user.update({
@@ -47,9 +52,23 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
},
});
+ await expireResetPasswordRequest(rawRequestId);
+
return res.status(201).json({ message: "Password reset." });
} catch (reason) {
console.error(reason);
return res.status(500).json({ message: "Unable to create password reset request" });
}
}
+
+async function expireResetPasswordRequest(rawRequestId: string) {
+ await prisma.resetPasswordRequest.update({
+ where: {
+ id: rawRequestId,
+ },
+ data: {
+ // We set the expiry to now to invalidate the request
+ expires: new Date(),
+ },
+ });
+}
diff --git a/apps/web/pages/auth/forgot-password/[id].tsx b/apps/web/pages/auth/forgot-password/[id].tsx
index d4549a5f0d..b76d52efeb 100644
--- a/apps/web/pages/auth/forgot-password/[id].tsx
+++ b/apps/web/pages/auth/forgot-password/[id].tsx
@@ -1,16 +1,16 @@
import type { ResetPasswordRequest } from "@prisma/client";
-import { debounce } from "lodash";
import type { GetServerSidePropsContext } from "next";
import { getCsrfToken } from "next-auth/react";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import Link from "next/link";
import type { CSSProperties } from "react";
import React, { useMemo } from "react";
+import { useForm } from "react-hook-form";
import dayjs from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import prisma from "@calcom/prisma";
-import { Button, TextField } from "@calcom/ui";
+import { Button, PasswordField, Form } from "@calcom/ui";
import PageWrapper from "@components/PageWrapper";
import AuthContainer from "@components/ui/AuthContainer";
@@ -23,40 +23,22 @@ type Props = {
export default function Page({ resetPasswordRequest, csrfToken }: Props) {
const { t } = useLocale();
- const [loading, setLoading] = React.useState(false);
- const [, setError] = React.useState<{ message: string } | null>(null);
- const [success, setSuccess] = React.useState(false);
-
- const [password, setPassword] = React.useState("");
+ const formMethods = useForm<{ new_password: string }>();
+ const success = formMethods.formState.isSubmitSuccessful;
+ const loading = formMethods.formState.isSubmitting;
const submitChangePassword = async ({ password, requestId }: { password: string; requestId: string }) => {
- try {
- const res = await fetch("/api/auth/reset-password", {
- method: "POST",
- body: JSON.stringify({ requestId: requestId, password: password }),
- headers: {
- "Content-Type": "application/json",
- },
- });
-
- const json = await res.json();
-
- if (!res.ok) {
- setError(json);
- } else {
- setSuccess(true);
- }
-
- return json;
- } catch (reason) {
- setError({ message: t("unexpected_error_try_again") });
- } finally {
- setLoading(false);
- }
+ const res = await fetch("/api/auth/reset-password", {
+ method: "POST",
+ body: JSON.stringify({ requestId, password }),
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ const json = await res.json();
+ if (!res.ok) return formMethods.setError("new_password", { type: "server", message: json.message });
};
- const debouncedChangePassword = debounce(submitChangePassword, 250);
-
const Success = () => {
return (
<>
@@ -109,8 +91,9 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
{isRequestExpired &&