Integration with Cloudflare Turnstile CAPTCHA for bot protection with React hooks and server-side verification.
React component that loads the Cloudflare Turnstile script.
/**
* Component to load Cloudflare Turnstile script
*/
const TurnstileScript: React.ComponentType;Usage Example:
import { TurnstileScript } from 'rwsdk/turnstile';
function Document({ children }) {
return (
<html>
<head>
<title>My App</title>
<TurnstileScript />
</head>
<body>{children}</body>
</html>
);
}React hook for managing Turnstile widget and challenges.
/**
* React hook for Turnstile widget
* @param siteKey - Turnstile site key from Cloudflare dashboard
* @returns Object with ref for widget container and challenge function
*/
function useTurnstile(siteKey: string): {
/** Ref to attach to the Turnstile widget container div */
ref: RefObject<HTMLDivElement>;
/** Function to trigger a challenge and get the token */
challenge: () => Promise<string>;
};
type RefObject<T> = import('react').RefObject<T>;Usage Example:
import { useTurnstile } from 'rwsdk/turnstile';
import { useState } from 'react';
function LoginForm() {
const { ref, challenge } = useTurnstile(process.env.TURNSTILE_SITE_KEY);
const [error, setError] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
// Get Turnstile token
const token = await challenge();
// Submit form with token
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: e.target.email.value,
password: e.target.password.value,
turnstileToken: token,
}),
});
if (!response.ok) {
setError('Login failed');
}
} catch (err) {
setError('Challenge failed');
}
};
return (
<form onSubmit={handleSubmit}>
<input type="email" name="email" required />
<input type="password" name="password" required />
{/* Turnstile widget container */}
<div ref={ref}></div>
{error && <p style={{ color: 'red' }}>{error}</p>}
<button type="submit">Login</button>
</form>
);
}Verifies a Turnstile token on the server-side.
/**
* Verifies a Turnstile token server-side
* @param options - Verification options
* @returns True if token is valid, false otherwise
*/
function verifyTurnstileToken(options: {
/** Turnstile token from client */
token: string;
/** Turnstile secret key from Cloudflare dashboard */
secretKey: string;
/** Optional custom fetch function (for testing) */
fetchFn?: typeof fetch;
}): Promise<boolean>;Usage Example:
import { verifyTurnstileToken } from 'rwsdk/turnstile';
// In a route handler
async function loginHandler(request: Request, env: any) {
const { email, password, turnstileToken } = await request.json();
// Verify Turnstile token
const isValidToken = await verifyTurnstileToken({
token: turnstileToken,
secretKey: env.TURNSTILE_SECRET_KEY,
});
if (!isValidToken) {
return new Response('Invalid CAPTCHA', { status: 400 });
}
// Proceed with login
const user = await authenticateUser(email, password);
if (!user) {
return new Response('Invalid credentials', { status: 401 });
}
return Response.json({ success: true, user });
}// Document component with Turnstile script
import { TurnstileScript } from 'rwsdk/turnstile';
function Document({ children }) {
return (
<html>
<head>
<title>Secure App</title>
<TurnstileScript />
</head>
<body>{children}</body>
</html>
);
}// Form with Turnstile protection
import { useTurnstile } from 'rwsdk/turnstile';
import { useState } from 'react';
function ContactForm() {
const { ref, challenge } = useTurnstile(
'0x4AAAAAAA...' // Your site key
);
const [submitting, setSubmitting] = useState(false);
const [message, setMessage] = useState('');
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setSubmitting(true);
setMessage('');
try {
const token = await challenge();
const formData = new FormData(e.currentTarget);
const response = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: formData.get('name'),
email: formData.get('email'),
message: formData.get('message'),
turnstileToken: token,
}),
});
if (response.ok) {
setMessage('Message sent successfully!');
e.currentTarget.reset();
} else {
setMessage('Failed to send message');
}
} catch (error) {
setMessage('An error occurred');
} finally {
setSubmitting(false);
}
};
return (
<form onSubmit={handleSubmit}>
<input type="text" name="name" placeholder="Name" required />
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required />
<div ref={ref} />
<button type="submit" disabled={submitting}>
{submitting ? 'Sending...' : 'Send'}
</button>
{message && <p>{message}</p>}
</form>
);
}// API endpoint with verification
import { verifyTurnstileToken } from 'rwsdk/turnstile';
import { route } from 'rwsdk/router';
const contactRoute = route('/api/contact', {
POST: async (request: Request, env: any) => {
const { name, email, message, turnstileToken } = await request.json();
// Verify Turnstile token
const isValid = await verifyTurnstileToken({
token: turnstileToken,
secretKey: env.TURNSTILE_SECRET_KEY,
});
if (!isValid) {
return new Response('CAPTCHA verification failed', { status: 400 });
}
// Process the contact form
await saveContactMessage({ name, email, message });
return Response.json({ success: true });
},
});Get Cloudflare Turnstile Keys:
Configure Environment:
# wrangler.toml
[vars]
TURNSTILE_SITE_KEY = "0x4AAAAAAA..."
# Use secrets for the secret key
# Run: wrangler secret put TURNSTILE_SECRET_KEYAdd Turnstile Script:
<TurnstileScript /> to your Document componentUse in Forms:
useTurnstile() hook in form componentsref to a div where the widget will appearchallenge() before form submissionVerify on Server:
verifyTurnstileToken() in your API handlersTurnstile supports different modes:
The mode is configured in your Cloudflare dashboard when creating the site key.
const { ref, challenge } = useTurnstile(siteKey);
try {
const token = await challenge();
// Use token
} catch (error) {
// Handle challenge failure
// - User closed the challenge
// - Network error
// - Turnstile script not loaded
console.error('Turnstile challenge failed:', error);
}