Securing a Next.js Contact Form using Google reCAPTCHA

Example Subheading

July 31, 2023

By Anju Thomas

Integrating Google reCAPTCHA with a Next.js Form

In this blog, we will learn how to integrate Google reCAPTCHA with a Next.js contact form to prevent spam submissions and enhance the security of the form.

Google reCAPTCHA is a free service provided by Google that helps protect websites from abusive activities by bots. By adding reCAPTCHA to our contact form, we can ensure that only human users can submit the form while reducing the chances of receiving spam.

Please refer to my previous blog on building a Contact Us form in Sitecore using Nextjs and Nodemailer.

Prerequisites

Before proceeding, ensure that you have the following set up:

  1. A working Next.js project with a contact form and corresponding json rendering in sitecore implemented.
  2. A Google reCAPTCHA API key. If you don't have one, you can get it by registering your website with the Google reCAPTCHA service.

Create a Google reCAPTCHA Account

  1. Go to the Google reCAPTCHA website (https://www.google.com/recaptcha) and log in with your Google account.
  2. Click on the "Admin Console" button to create a new reCAPTCHA project.
  3. Fill in the required information, such as the label (name) of your project and the domains where the reCAPTCHA will be used.
  4. Choose the reCAPTCHA type. For this tutorial, we'll use "reCAPTCHA v2" with the "Checkbox" option.
  5. Add the domain(s) where you'll implement the reCAPTCHA and accept the terms of service.
  6. After successfully registering your site, you'll receive the Site Key and Secret Key. Keep these keys secure, as the Secret Key should never be exposed on the client-side.

Install react-google-recaptcha Package

To use the ReCAPTCHA component, we need to install the react-google-recaptcha package. Run the following command in your project directory:

npm install react-google-recaptcha

Adding reCAPTCHA to the Contact Form

In the component where you have implemented the contact form, add the reCAPTCHA widget to the form. Update your ContactUsForm.tsx component as follows:

//...(Existing imports)
import ReCAPTCHA from 'react-google-recaptcha';

//...(Sitecore fields) const FORM_DEFAULT: { [key: string]: string; } = { firstName: '', lastName: '', message: '', captchaToken: '', };

const ContactUsForm = ({ fields }: ContactUsFormProps): JSX.Element => { const [formData, setFormData] = useState(FORM_DEFAULT); const [successMsgCss, setSuccesssMsg] = useState(false); // Create a ref for the reCAPTCHA widget const recaptcha: RefObject<ReCAPTCHA> = useRef(null); // ... (Existing functions)

const formSubmit = async (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); formData.recipient = fields.emailRecipient.value; formData.emailSubject = fields.emailSubject.value; fetch('/api/contact', { method: 'POST', headers: { Accept: 'application/json, text/plain, /', 'Content-Type': 'application/json', }, body: JSON.stringify(formData), }) .then((response) => { console.log('response recieved'); if (response.status === 200) { console.log('response succeeded'); setSuccesssMsg(true); setFormData(FORM_DEFAULT); recaptcha?.current?.reset();// reset recaptcha after submission } }) .catch((error) => { console.error(error); }); } };

const onCaptchaChange = (token: string | null) => { // Set the captcha token when the user completes the reCAPTCHA if (token) { formData.captchaToken = token; } }; return ( {/* ... (existing TSX markup) /} <div className="pr-15px pl-25px pt-50px pb-25px mx-auto max-w-1200px w-full"> <div> {/ ... (existing form fields) /} {/ Add the reCAPTCHA widget /} <div className="pb-20px"> <ReCAPTCHA size="normal" sitekey="YOUR_RECAPTCHA_SITE_KEY" onChange={onCaptchaChange} ref={recaptcha} /> </div> {/ ... (existing submit button) */} </form> </div>

Server-Side Verification

Update your API route (/pages/api/contact.ts) to validate the reCAPTCHA token received from the frontend:

//...existing imports
import axios from 'axios';

export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> { // ... (existing transporter setup and mailData) // Validate the reCAPTCHA token on the server-side

try { const response = await axios.post( https://www.google.com/recaptcha/api/siteverify?secret=<span class="hljs-subst">${process.env.RECAPTCHA_SECRET_KEY}</span>&amp;response=<span class="hljs-subst">${req.body.captchaToken}</span> ); if (response.data.success) { //reCaptcha verification successfull transporter.sendMail(mailData, function (err, info) { if (err) { console.log(err); res.status(500).send('Internal Server Error'); } else { console.log('successful'); console.log(info); res.status(200).end(); } }); } else { // reCAPTCHA verification failed res.status(400).send('reCAPTCHA verification failed.'); } } catch (error) { console.error(error); res.status(500).send('Internal server error'); } }

In this above code, we use axios.post to send a POST request to the Google reCAPTCHA API endpoint (https://www.google.com/recaptcha/api/siteverify). We pass the reCAPTCHA token and the reCAPTCHA secret key in the request body. The response contains the verification result, which we check to ensure the reCAPTCHA was successful. If the reCAPTCHA verification succeeds, we proceed with sending the email; otherwise, we return an error response.

Conclusion

In this blog post, we learned how to add Google reCAPTCHA to your existing Next.js contact form. By integrating reCAPTCHA, you can enhance the security of your form and prevent automated spam submissions. Implementing reCAPTCHA is a straightforward process and provides an added layer of protection for your website's forms.

Anju Headshot

Anju Thomas

Sitecore Web Developer

Anju is a Sitecore Developer with a Bachelor's Degree in Computer Science and over 4 years of experience in Sitecore development. She loves solving Sudoku puzzles, watching movies, eating delicious food and exploring the world.