Step-by-Step Guide to Google reCAPTCHA v3 Integration in a Sitecore Next.js Headless App
In today's digital landscape, protecting your web applications from spam and abuse is more important than ever. Google reCAPTCHA v3 offers a powerful solution to differentiate real users from bots. This guide walks you through integrating reCAPTCHA v3 with a Next.js Sitecore app to boost security without impacting user experience. Unlike older versions, reCAPTCHA v3 works silently in the background, assigning a risk score to each request. This score helps developers block suspicious activity or take further action, ensuring flexible, user-friendly protection for your site.
Please follow the following steps for integration score-based Google reCAPTCHA v3 with the Next.js Sitecore application.
Create Google reCAPTCHA keys
To create Google reCAPTCHA v3 keys in the Google Admin Console, follow these steps:
Step 1: Sign in to Google reCAPTCHA Admin Console
- Visit the Google reCAPTCHA Admin Console: https://www.google.com/recaptcha/admin/create
- Sign in using your Google account credentials.
Step 2: Register a New Site
-
In the registration form, enter your Label. This is a name that helps you identify the reCAPTCHA for your website.
Step 3: Choose reCAPTCHA v3
-
Under the reCAPTCHA type, select Score based (v3).
Step 4: Add Domain(s)
-
Enter the domain(s) where you want to use reCAPTCHA v3. You can add multiple domains.
Step 6: Submit & Get Keys
- Click Submit to generate the keys.
- You'll now see the Site Key and Secret Key. Copy these, as you will need them for integrating reCAPTCHA v3 into your Next.js Sitecore app.
- Copy these keys. They will be used in the code.
Implementation
- In the .env file, add the two keys generated above as below.
#Google recaptcha key
NEXT_PUBLIC_RECAPTCHA_SITE_KEY= your-site-key-value
RECAPTCHA_SECRET_KEY= your-secret-key-value
- Install npm package react-google-recaptcha-v3 using the below command.
npm install react-google-recaptcha-v3
-
Client Side
-
In the Form.tsx (the Form.tsx file is taken for example, you can include this code in your .tsx file) file add the following Google reCAPTCHA code.
import { useGoogleReCaptcha } from 'react-google-recaptcha-v3'; const Form = (): JSX.Element => { const { executeRecaptcha } = useGoogleReCaptcha(); async function googleReCaptchaVerficiation(): Promise<boolean> { //GoogleRecaptcha not available yet if (!executeRecaptcha) { console.log('Execute recaptcha not available yet'); setError('ReCaptcha not available.'); return false; } //GoogleRecaptcha token let gReCaptchaToken: string; try { //The 'Form' is used for labeing. You can add your form name. gReCaptchaToken = await executeRecaptcha('Form'); } catch (error) { console.error('Failed to get ReCaptcha token. Error : ' + error); setError('ReCaptcha validation failed. Please try again.'); } //GoogleRecaptcha API verficaiton call let captchaVerifyResponse: Response; try { captchaVerifyResponse = await fetch('/api/recaptchaVerify', { method: 'POST', headers: { Accept: 'application/json, text/plain, */*', 'Content-Type': 'application/json', }, body: JSON.stringify({ gReCaptchaToken: gReCaptchaToken }), }); } catch (error) { console.error('Request failed', error); setError('Network error. Please try again.'); } // Check the response status from recaptcha verification if (captchaVerifyResponse.status === 200) { if (score > 0.5 && success) { return true; } else { console.error('ReCaptcha verification failed: ', captchaVerifyResponse); setError('ReCaptcha verification failed. Please try again.'); return false; } } else { setError('ReCaptcha verification failed. Please try again.'); return false; } } }
-
This code has to be enclosed in GoogleReCaptchaProvider. So create a FormWrapper for the above component. You can add the FormWrapper as the component name in Sitecore as below.
-
Add the below code for the FormWrapper.tsx file. Replace with your form component.
import React from 'react'; import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3'; import Form from './Form'; function FormWrapper() { const recaptchaKey: string | undefined = process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY; return ( <GoogleReCaptchaProvider reCaptchaKey={recaptchaKey ?? 'NOT DEFINED'} scriptProps={{ async: false, defer: false, appendTo: 'head', nonce: undefined, }} > <Form /> </GoogleReCaptchaProvider> ); } export default FormWrapper;
-
- Server Side
- Implement recaptchaVerify.ts like below. Update the path in fetch('/api/recaptchaVerify' in Form.tsx file above to point to the location of this file.
import type { NextApiRequest, NextApiResponse } from 'next';
interface RecaptchaResponseData {
success: boolean;
statusCode: string;
score: number;
error?: string;
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<RecaptchaResponseData>
) {
// Ensure the method is POST, enhancing security by rejecting unwanted request methods.
if (req.method !== 'POST') {
return res
.status(405)
.json({ success: false, statusCode: '405', score: 0, error: 'Method not allowed' });
}
// Retrieve the secret key from environment variables for the ReCaptcha verification.
const secretKey = process.env.RECAPTCHA_SECRET_KEY;
if (!secretKey) {
// If the secret key is not found, log an error and return an appropriate response.
console.error('RECAPTCHA_SECRET_KEY is not set in environment variables.');
return res
.status(500)
.json({ success: false, statusCode: '500', score: 0, error: 'Server configuration error. Missing recaptcha secret key' });
}
const gRecaptchaToken = req.body['gReCaptchaToken'];
if (!gRecaptchaToken) {
return res
.status(400)
.json({ success: false, statusCode: '400', score: 0, error: 'Bad request: Missing gReCaptchaToken' });
}
// Define the form data for the POST request to the ReCaptcha API.
const formData = `secret=${secretKey}&response=${gRecaptchaToken}`;
try {
// Make a POST request to the Google ReCaptcha verify API using fetch.
const response = await fetch('https://www.google.com/recaptcha/api/siteverify', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData, // formData contains the necessary URL-encoded data
});
// Parse the response JSON.
let data;
try {
data = await response.json(); // Try to parse the response JSON.
} catch (jsonError) {
// Handle the case where there's no JSON data in the response.
console.error('Failed to parse JSON:', jsonError);
return res.status(500).json({
success: false,
statusCode: '500',
score: 0,
error: 'Failed to parse response from ReCaptcha API',
});
}
// Check if the response status not 200.
if (response.status !== 200) {
console.error('ReCaptcha verification failed: No data returned from server.');
return res.status(response.status).json({
success: false,
statusCode: response.statusText + ' ' + response.status,
score: 0,
error: 'ReCaptcha verification failed: No data returned from the server.' + data.error,
});
} else {
// Check the ReCaptcha response for success and a score above a certain threshold.
if (data.success && data.score > 0.5) {
console.log('ReCaptcha verification passed with score:', data.score);
// Return a success response if the verification passes.
return res.status(response.status).json({
success: true,
statusCode: response.statusText + ' ' + response.status,
score: data.score,
});
} else if (!data.success && data.score < 0.5) {
// Log the failure and return a response indicating the verification did not pass.
console.error('ReCaptcha verification failed with a low score error : Data', data);
return res.status(response.status).json({
success: false,
statusCode: response.statusText + ' ' + response.status,
score: data.score,
error: 'ReCaptcha verification failed with a low score error: ' + data.error,
});
} else {
console.error('ReCaptcha verification failed : No Data', data);
return res.status(response.status).json({
success: false,
statusCode: response.statusText + ' ' + response.status,
score: data.score,
error: 'ReCaptcha verification failed with no data and error: ' + data.error,
});
}
}
} catch (error) {
// Handle any errors that occur during the API request.
console.error('Error during ReCaptcha verification (no response or network issue):', error);
return res
.status(500)
.json({
success: false,
statusCode: '500',
score: 0,
error: 'Internal server error or no response from ReCaptcha API' + error,
});
}
}
- Browse the page where this form is present. You should be able to see the google reCAPTCHA icon in the bottom corner of the page like below. Score-based reCAPTCHA does not present the user with any challenges. Instead, it calculates a score based on user behavior for the decision making.
By integrating Google reCAPTCHA v3 into your Sitecore Next.js headless app, you've added an important layer of security against bots and malicious activity. This implementation ensures a seamless user experience while safeguarding your application's forms and interactions. Regularly monitoring reCAPTCHA scores and fine-tuning its settings will help maintain an optimal balance between security and user convenience. With these steps in place, your app is now better equipped to handle the challenges of modern web security.