From b12c1c3a82941f2767ade8f497db56933415b94d Mon Sep 17 00:00:00 2001
From: Mohamed Bassem <me@mbassem.com>
Date: Sun, 30 Nov 2025 00:01:07 +0000
Subject: feat: add support for turnstile on signup

---
 apps/web/components/signup/SignUpForm.tsx | 42 +++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

(limited to 'apps/web/components/signup/SignUpForm.tsx')

diff --git a/apps/web/components/signup/SignUpForm.tsx b/apps/web/components/signup/SignUpForm.tsx
index a1e8dadf..bd4a1cf2 100644
--- a/apps/web/components/signup/SignUpForm.tsx
+++ b/apps/web/components/signup/SignUpForm.tsx
@@ -25,6 +25,7 @@ import { Input } from "@/components/ui/input";
 import { useClientConfig } from "@/lib/clientConfig";
 import { api } from "@/lib/trpc";
 import { zodResolver } from "@hookform/resolvers/zod";
+import { Turnstile } from "@marsidev/react-turnstile";
 import { TRPCClientError } from "@trpc/client";
 import { AlertCircle, UserX } from "lucide-react";
 import { signIn } from "next-auth/react";
@@ -43,11 +44,13 @@ export default function SignUpForm() {
       name: "",
       password: "",
       confirmPassword: "",
+      turnstileToken: "",
     },
   });
   const [errorMessage, setErrorMessage] = useState("");
   const router = useRouter();
   const clientConfig = useClientConfig();
+  const turnstileSiteKey = clientConfig.turnstile?.siteKey;
 
   const createUserMutation = api.users.create.useMutation();
 
@@ -97,6 +100,14 @@ export default function SignUpForm() {
         <Form {...form}>
           <form
             onSubmit={form.handleSubmit(async (value) => {
+              if (turnstileSiteKey && !value.turnstileToken) {
+                form.setError("turnstileToken", {
+                  type: "manual",
+                  message: "Please complete the verification challenge",
+                });
+                return;
+              }
+              form.clearErrors("turnstileToken");
               try {
                 await createUserMutation.mutateAsync(value);
               } catch (e) {
@@ -205,6 +216,37 @@ export default function SignUpForm() {
               )}
             />
 
+            {turnstileSiteKey && (
+              <FormField
+                control={form.control}
+                name="turnstileToken"
+                render={({ field }) => (
+                  <FormItem>
+                    <FormLabel>Verification</FormLabel>
+                    <FormControl>
+                      <Turnstile
+                        siteKey={turnstileSiteKey}
+                        onSuccess={(token) => {
+                          field.onChange(token);
+                          form.clearErrors("turnstileToken");
+                        }}
+                        onExpire={() => field.onChange("")}
+                        onError={() => {
+                          field.onChange("");
+                          form.setError("turnstileToken", {
+                            type: "manual",
+                            message:
+                              "Verification failed, please reload the challenge",
+                          });
+                        }}
+                      />
+                    </FormControl>
+                    <FormMessage />
+                  </FormItem>
+                )}
+              />
+            )}
+
             <ActionButton
               type="submit"
               loading={
-- 
cgit v1.2.3-70-g09d2

