diff --git a/apps/web/modules/signup-view.tsx b/apps/web/modules/signup-view.tsx index 9875f1717e..10f33c8a44 100644 --- a/apps/web/modules/signup-view.tsx +++ b/apps/web/modules/signup-view.tsx @@ -35,6 +35,7 @@ import { pushGTMEvent } from "@calcom/lib/gtm"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useDebounce } from "@calcom/lib/hooks/useDebounce"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { INVALID_CLOUDFLARE_TOKEN_ERROR } from "@calcom/lib/server/checkCfTurnstileToken"; import { IS_EUROPE } from "@calcom/lib/timezoneConstants"; import { signupSchema as apiSignupSchema } from "@calcom/prisma/zod-utils"; import type { inferSSRProps } from "@calcom/types/inferSSRProps"; @@ -191,6 +192,7 @@ export default function Signup({ const [usernameTaken, setUsernameTaken] = useState(false); const [isGoogleLoading, setIsGoogleLoading] = useState(false); const [displayEmailForm, setDisplayEmailForm] = useState(token); + const [turnstileKey, setTurnstileKey] = useState(0); const searchParams = useCompatSearchParams(); const { t, i18n } = useLocale(); const router = useRouter(); @@ -207,7 +209,6 @@ export default function Signup({ useEffect(() => { if (redirectUrl) { - // eslint-disable-next-line @calcom/eslint/avoid-web-storage localStorage.setItem("onBoardingRedirect", redirectUrl); } }, [redirectUrl]); @@ -306,6 +307,13 @@ export default function Signup({ }); }) .catch((err) => { + setTurnstileKey((k) => k + 1); + formMethods.setValue("cfToken", undefined); + + if (err.message === INVALID_CLOUDFLARE_TOKEN_ERROR) { + return; + } + posthog.capture("signup_form_submit_error", { has_token: !!token, is_org_invite: isOrgInviteByLink, @@ -512,10 +520,17 @@ export default function Signup({ {/* Cloudflare Turnstile Captcha */} {CLOUDFLARE_SITE_ID ? ( { formMethods.setValue("cfToken", token); }} + onExpire={() => { + formMethods.setValue("cfToken", undefined); + }} + onError={() => { + formMethods.setValue("cfToken", undefined); + }} /> ) : null} @@ -558,7 +573,6 @@ export default function Signup({ org_slug: orgSlug, }); - // eslint-disable-next-line @calcom/eslint/avoid-web-storage localStorage.setItem("username", username); const sp = new URLSearchParams(); // @NOTE: don't remove username query param as it's required right now for stripe payment page @@ -588,9 +602,7 @@ export default function Signup({ !!formMethods.formState.errors.email || !formMethods.getValues("email") || !formMethods.getValues("password") || - (CLOUDFLARE_SITE_ID && - !process.env.NEXT_PUBLIC_IS_E2E && - !formMethods.getValues("cfToken")) || + (CLOUDFLARE_SITE_ID && !process.env.NEXT_PUBLIC_IS_E2E && !watch("cfToken")) || isSubmitting || usernameTaken }> @@ -640,7 +652,6 @@ export default function Signup({ if (prepopulateFormValues?.username) { // If username is present we save it in query params to check for premium searchQueryParams.set("username", prepopulateFormValues.username); - // eslint-disable-next-line @calcom/eslint/avoid-web-storage localStorage.setItem("username", prepopulateFormValues.username); } if (token) { diff --git a/packages/lib/server/checkCfTurnstileToken.ts b/packages/lib/server/checkCfTurnstileToken.ts index 7985dfa7d9..2fb0440545 100644 --- a/packages/lib/server/checkCfTurnstileToken.ts +++ b/packages/lib/server/checkCfTurnstileToken.ts @@ -2,6 +2,8 @@ import { HttpError } from "../http-error"; const TURNSTILE_SECRET_ID = process.env.CLOUDFLARE_TURNSTILE_SECRET; +export const INVALID_CLOUDFLARE_TOKEN_ERROR = "Invalid cloudflare token"; + export async function checkCfTurnstileToken({ token, remoteIp }: { token?: string; remoteIp: string }) { // This means the instance doesn't have turnstile enabled - we skip the check and just return success. // OR the instance is running in CI so we skip these checks also @@ -28,7 +30,7 @@ export async function checkCfTurnstileToken({ token, remoteIp }: { token?: strin const data = await result.json(); if (!data["success"]) { - throw new HttpError({ statusCode: 401, message: "Invalid cloudflare token" }); + throw new HttpError({ statusCode: 401, message: INVALID_CLOUDFLARE_TOKEN_ERROR }); } return data;