BWBama85
BWBama85

Reputation: 72

Next.js/React Form with Cloudflare Turnstile unable to get response token from form

I'm new to Next.js/React, and I am trying to build out a simple contact form and protect it with Cloudflare Turnstile. Without Turnstile, I have it working with no issue. The form submits to the API and sends the email out.

Enviroment:

Next.js 13 using 'use client'

importing:

import { useState } from 'react';

import { Turnstile } from '@marsidev/react-turnstile';

However, now that I am trying to check the form submission with Turnstile, I can never get the input field containing the response token. I can inspect the form and see the input field with the response token filled out. Here is my code:

Form

      <form onSubmit={handleSubmit}>
        <div className="flex flex-col gap-4">
          <div className="form-control w-full max-w-md">
            <label className="label" htmlFor="name">
              <span className="label-text">Name</span>
            </label>
            <input
              type="text"
              id="name"
              placeholder="Your Name"
              required
              className="input w-full max-w-md"
              name="name"
              onChange={(e) => {
                setName(e.target.value);
              }}
            />
          </div>
          <div className="form-control w-full max-w-md">
            <label className="label" htmlFor="email">
              <span className="label-text">Email</span>
            </label>
            <input
              type="text"
              id="email"
              placeholder="Your Email"
              required
              className="input w-full max-w-md"
              name="email"
              onChange={(e) => {
                setEmail(e.target.value);
              }}
            />
          </div>
          <div className="form-control w-full max-w-md">
            <label className="label" htmlFor="subject">
              <span className="label-text">Subject</span>
            </label>
            <input
              type="text"
              id="subject"
              placeholder="Subject"
              required
              className="input w-full max-w-md"
              name="subject"
              onChange={(e) => {
                setSubject(e.target.value);
              }}
            />
          </div>
          <div className="form-control">
            <label className="label" htmlFor="message">
              <span className="label-text">Message</span>
            </label>
            <textarea
              className="textarea-bordered textarea h-48 w-full max-w-md"
              id="message"
              placeholder="Your message"
              required
              name="message"
              onChange={(e) => {
                setMessage(e.target.value);
              }}
            ></textarea>
          </div>
          <Turnstile siteKey={cfSiteKey} />
          <button type="submit" className="btn-primary btn w-full max-w-md">
            Submit
          </button>
        </div>
      </form>

Handler

  let cfSiteKey: string;

  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [subject, setSubject] = useState('');
  const [message, setMessage] = useState('');
  const [submitted, setSubmitted] = useState(false);

  if (process.env.NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY) {
    cfSiteKey= process.env.NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY;
  } else {
    throw new Error('Cloudflare Turnstile Secret Key Not Set');
  }
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const formData = new FormData();
    const token = formData.get('cf-turnstile-response');
    console.log(token);

    const data = {
      name,
      email,
      subject,
      message,
      token,
    };
    fetch('/api/contact', {
      method: 'POST',
      headers: {
        Accept: 'application/json, text/plain, */*',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    }).then((res) => {
      console.log('Response received');
      if (res.status === 200) {
        console.log('Response succeeded!');
        setSubmitted(true);
        setName('');
        setEmail('');
        setSubject('');
        setMessage('');
      }
    });
  };

Above, token always returns null when I submit the form. What am I missing or doing wrong here?

Upvotes: 0

Views: 1461

Answers (2)

Nishant Shah
Nishant Shah

Reputation: 1

In my case, I was just receiving name not the content of token in formdata.

To fix this I converted the token to string during form submission.

function registerUser(e) {
    e.preventDefault();
    const formData = new FormData(e.target);
    const token = formData.get("cf-turnstile-response").toString();
    formData.set("cf-turnstile-response", token);
}

But still I was getting null in the server side. For this header was responsible.

I was passing below mentioned header. Removing it and not adding other headers eventually worked.

 headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },

Upvotes: 0

BWBama85
BWBama85

Reputation: 72

I guess I will answer my question.

I had tried many things inside new FormData() but they did not work. So I left it blank and looked elsewhere in my code for the problem.

The answer, along with correct typing is the following inside the handler:

  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    const token = formData.get('cf-turnstile-response') as string;
    console.log(token);

Now the token is available and sent correctly to the API.

Upvotes: 2

Related Questions