Captcha Protection for your API Routes
Learn how to set up captcha protection for your API routes.
For captcha protection, we use Cloudflare Turnstile.
How to set up captcha protection for your API routes
Learn how to set up captcha protection for your API routes
Setting up the environment variables
To enable it, you need to set the following environment variables:
CAPTCHA_SECRET_TOKEN=NEXT_PUBLIC_CAPTCHA_SITE_KEY=You can find the CAPTCHA_SECRET_TOKEN in the Turnstile configuration. The NEXT_PUBLIC_CAPTCHA_SITE_KEY is public and safe to share. Instead, the CAPTCHA_SECRET_TOKEN should be kept secret.
This guide assumes you have correctly set up your Turnstile configuration. If you haven't, please refer to the https://developers.cloudflare.com/turnstile.
Enabling the captcha protection
When you set the token in the environment variables, the kit will automatically protect your API routes with captcha.
NB: you also need to set the token in the Supabase Dashboard!
Using Captcha in Your Components
The kit provides two clean APIs for captcha integration depending on your use case.
Option 1: Using the useCaptcha Hook
For auth containers and simple forms, use the useCaptcha hook for zero-boilerplate captcha integration:
import { useCaptcha } from '@kit/auth/captcha/client';function MyComponent({ captchaSiteKey }) { const captcha = useCaptcha({ siteKey: captchaSiteKey }); const handleSubmit = async (data) => { try { await myServerAction({ ...data, captchaToken: captcha.token, }); } finally { // Always reset after submission captcha.reset(); } }; return ( <form onSubmit={handleSubmit}> {captcha.field} <button type="submit">Submit</button> </form> );}The useCaptcha hook returns:
token- The current captcha tokenreset()- Function to reset the captcha widgetfield- The captcha component to render
Option 2: React Hook Form Integration
For forms using react-hook-form, use the CaptchaField component with automatic form integration:
import { useForm } from 'react-hook-form';import { CaptchaField } from '@kit/auth/captcha/client';function MyForm() { const form = useForm({ defaultValues: { message: '', captchaToken: '', }, }); const handleSubmit = async (data) => { try { await myServerAction(data); form.reset(); // Automatically resets captcha too } catch (error) { // Handle error } }; return ( <Form {...form}> <form onSubmit={form.handleSubmit(handleSubmit)}> {/* Your form fields */} <CaptchaField siteKey={config.captchaSiteKey} control={form.control} name="captchaToken" /> <button type="submit">Submit</button> </form> </Form> );}When using React Hook Form integration:
- The captcha token is automatically set in the form state
- Calling form.reset() automatically resets the captcha
- No manual state management needed
Using with Server Actions
Define your server action schema to include the captchaToken:
import { z } from 'zod';import { enhanceAction } from '@kit/next/actions';const MySchema = z.object({ message: z.string(), captchaToken: z.string(),});export const myServerAction = enhanceAction( async (data) => { // Your action code - captcha is automatically verified console.log(data.message); }, { captcha: true, schema: MySchema, });When captcha: true is set, enhanceAction automatically:
- Extracts the
captchaTokenfrom the data - Verifies it with Cloudflare Turnstile
- Throws an error if verification fails
Important Notes
- Token Validity: A captcha token is valid for one request only
- Always Reset: Always call captcha.reset() (or form.reset() with RHF) after submission, whether successful or not
- Automatic Renewal: The library automatically renews tokens when needed, but you must reset after consumption
Verifying the Token Manually
If you need to verify the captcha token manually server-side (e.g., in API routes), use:
import { verifyCaptchaToken } from '@kit/auth/captcha/server';async function myApiHandler(request: Request) { const token = request.headers.get('x-captcha-token'); // Throws an error if invalid await verifyCaptchaToken(token); // Your API logic}Note: If you use enhanceAction or enhanceRouteHandler with captcha: true, verification is automatic and you don't need to call verifyCaptchaToken manually.
Migration from old API (prior to v2.18.3)
If you're migrating from the old useCaptchaToken hook:
Before:
import { useCaptchaToken } from '@kit/auth/captcha/client';const { captchaToken, resetCaptchaToken } = useCaptchaToken();// Manual state management requiredAfter:
import { useCaptcha } from '@kit/auth/captcha/client';const captcha = useCaptcha({ siteKey: captchaSiteKey });