DEV Community

Elif Nur Turk
Elif Nur Turk

Posted on

Google reCAPTCHA for Vue.js/Nuxt.js : App & Form Spam Shield

The Importance of reCAPTCHA in Modern Applications

In the digital age, safeguarding applications from bots and automated attacks is more critical than ever. Google’s reCAPTCHA provides a robust solution by verifying user authenticity, effectively preventing spam and protecting against brute force login attempts.

Modern reCAPTCHA versions are also much more user-friendly. Unlike older captchas that made users decipher distorted text, newer versions work silently in the background, allowing smoother user interactions while still providing strong protection.

To integrate reCAPTCHA, you need two keys: a site key and a secret key. The site key is used on the front end to display the reCAPTCHA widget, while the secret key is required on the backend to validate the token generated on frontend after user interaction.

When a user submits a form, a token will be created and sent to your backend endpoint, which communicates with Google’s API to verify the token. If the token is valid, the backend returns a confirmation (e.g., “OK”). We’ll handle only the frontend operations using the existing backend API.

vue-recaptcha-v3 is a Vue library that facilitates the integration of Google’s reCAPTCHA v3 into Vue.js applications. It enhances security by running in the background to score user interactions, effectively distinguishing between humans and bots without disrupting the user experience.

Implementing vue-recaptcha-v3 in Your Vue.js Application

Integrating vue-recaptcha-v3 into your Vue.js application involves a few straightforward steps. Here’s a guide to help you set it up, with a special focus on Nuxt.js.

Step 1: Install the Library

First, you need to install the vue-recaptcha-v3 package. Run one of the following commands in your project directory:

npm install vue-recaptcha-v3

yarn add vue-recaptcha-v3

Step 2: Obtain reCAPTCHA API Keys

Click and visit the Google reCAPTCHA admin console to register your site. You’ll receive two keys: Site Key & Secret Key. Make sure to choose reCAPTCHA v3 during registration. We will be using Site Key in the front-end.

Step 3: Create a Plugin

Next, create a new plugin file for reCAPTCHA. You can do this by creating a vue-recaptcha-v3.js file in the plugins directory:

*plugins/vue-recaptcha-v3.js*

    import { defineNuxtPlugin } from '#app'; 
    import { VueReCaptcha } from 'vue-recaptcha-v3';

    export default defineNuxtPlugin((nuxtApp) => {
      nuxtApp.vueApp.use(VueReCaptcha, {
        siteKey: 'YOUR_SITE_KEY', // Replace with your site key
        loaderOptions: {
          autoHideBadge: true, // Optional: Automatically hides the badge
          explicitRenderParameters: {
              //badge: 'bottomleft', //incase you don't want to hide it
          }
        }
    });
    });
Enter fullscreen mode Exit fullscreen mode

Badge Appearance
The reCAPTCHA badge is visible by default on all pages, but you can customize its behavior. For instance, you can choose to hide it by enabling the autoHideBadge option.

Customizing Badge Location

You can specify the position of the reCAPTCHA badge by using the loaderOptions when configuring vue-recaptcha-v3. Here are the available badge positions:
inline: The badge is shown inline (no fixed position).
bottomright: The badge appears in the bottom-right corner of the page.
bottomleft: The badge appears in the bottom-left corner of the page.
topright: The badge appears in the top-right corner of the page.
topleft: The badge appears in the top-left corner of the page.

Step 4: Register the Plugin

Add the plugin to the plugins array in your nuxt.config.js. Here’s an example configuration,

    export default defineNuxtConfig({
      devtools: { enabled: true },
       //..

        plugins:[
            {src: '~/plugins/vue-recaptcha-v3.js', mode: 'client' },
        ],

      ..//

    })
Enter fullscreen mode Exit fullscreen mode

Now we can apply it in form…

Let’s break down the implementation of reCAPTCHA in your ContactForm component step by step.

Step 1: Setup and Import

First, we import the necessary modules from vue-recaptcha-v3 and define our component:

/ContactForm.vue

    <script>
    import { VueReCaptcha, useReCaptcha } from "vue-recaptcha-v3";

    export default {
      name: "ContactForm",
      data() {
        return {
          form: {
            name: "",
          },
          submissionMessage: null,
          errorMessage: null,
        };
      },
      setup() {
        const { executeRecaptcha, recaptchaLoaded } = useReCaptcha();

        const recaptcha = async () => {
          await recaptchaLoaded(); // Wait for reCAPTCHA to load
          return await executeRecaptcha("contact"); // Create a reCAPTCHA token
        };
        return {
          recaptcha,
        };
      },
Enter fullscreen mode Exit fullscreen mode

The component imports VueReCaptcha and the useReCaptcha hook to manage reCAPTCHA.
The form object contains the user's input fields, while submissionMessage and errorMessage are used to display feedback.

Step 2: Token Creation

Next, we define the submitForm method to handle form submissions:

   methods: {
        async submitForm() {
          const token = await this.recaptcha(); // Call the recaptcha method to get the token
          console.log(token);

          // Check if the token is valid
          if (!token) {
            this.errorMessage = "Invalid reCAPTCHA. Please try again.";
            this.submissionMessage = null; // Clear previous messages
            alert(this.errorMessage);
            return; // Exit if the token is invalid
          }
Enter fullscreen mode Exit fullscreen mode

The recaptcha method is called to generate a token, which is logged to the console.
If the token is not generated (invalid), an error message is displayed, and the function exits early.

Step 3: CAPTCHA Validation

Once a valid token is created, the next step is to validate it:

    try {
            // Send the token to the CAPTCHA validation API first
            const captchaResponse = await fetch(
              "https://api.x.com/api/captcha",
              {
                method: "POST",
                headers: {
                  "Content-Type": "application/json",
                },
                body: JSON.stringify({ recaptcha_token: token }),
              }
            );

            // If the CAPTCHA validation is successful (status 200), submit the form
            if (captchaResponse.ok) {
              console.log("response is ok");
Enter fullscreen mode Exit fullscreen mode

The token is sent to your backend CAPTCHA validation endpoint using a POST request.
If the response is OK (status 200), it proceeds to prepare the form submission.

Step 4: Form Submission

If the CAPTCHA validation passes, the form data is sent:

      const formData = new FormData();
              formData.append("name", this.form.name);
              formData.append("recaptchaToken", token);

              const formSubmitResponse = await fetch(
                "https://api.x.com/api/contact",
                {
                  method: "POST",
                  body: formData,
                }
              );

              // Check if the form submission response is okay (status 200-299)
              if (formSubmitResponse.ok) {
                const result = await formSubmitResponse.json();
                this.submissionMessage = "Contact form submitted successfully!";
                alert(this.submissionMessage); // Show success alert
Enter fullscreen mode Exit fullscreen mode

A FormData object is created to hold the form inputs, including the reCAPTCHA token. The form data is sent to your form submission endpoint. If the submission is successful, a success message is displayed.

Step 5: Error Handling

If either the CAPTCHA validation or form submission fails, appropriate error messages are shown:

    } else {
                const error = await formSubmitResponse.json();
                this.errorMessage = error.message || "Error submitting form.";
                alert(this.errorMessage);
                this.submissionMessage = null; // Clear previous messages
              }
            } else {
              this.errorMessage = "CAPTCHA validation failed. Please try again.";
              alert(this.errorMessage);
              this.submissionMessage = null;
            }
          } catch (error) {
            console.error("Submission error:", error);
            this.errorMessage = "An error occurred while submitting the form.";
            this.submissionMessage = null; // Clear previous messages
            alert(this.errorMessage); // Show error alert
          }
Enter fullscreen mode Exit fullscreen mode

Error Handling: If there are issues with the CAPTCHA validation or form submission, the error messages are displayed, allowing users to understand what went wrong.

Step 6: Resetting the Form

Finally, the form can be reset after submission:

resetForm() {
this.form.name = "";
},

Here’s the complete code for the ContactForm component, integrating reCAPTCHA for secure form submissions:

/ContactForm.vue

    <script>
    import { VueReCaptcha, useReCaptcha } from "vue-recaptcha-v3";

    export default {
      name: "ContactForm",
      data() {
        return {
          form: {
            name: "",
          },
          submissionMessage: null,
          errorMessage: null,
        };
      },
      setup() {
        const { executeRecaptcha, recaptchaLoaded } = useReCaptcha();

        const recaptcha = async () => {
          await recaptchaLoaded(); // Wait for reCAPTCHA to load
          return await executeRecaptcha("contact"); // Create a reCAPTCHA token
        };
        return {
          recaptcha,
        };
      },

      methods: {
        async submitForm() {
          const token = await this.recaptcha(); // Call the recaptcha method to get the token
          console.log(token);

          // Check if the token is valid
          if (!token) {
            this.errorMessage = "Invalid reCAPTCHA. Please try again.";
            this.submissionMessage = null; // Clear previous messages
            alert(this.errorMessage);
            return;
          }

          try {
            // Send the token to the CAPTCHA validation API first
            const captchaResponse = await fetch(
              "https://api.x.com/api/captcha",
              {
                method: "POST",
                headers: {
                  "Content-Type": "application/json",
                },
                body: JSON.stringify({ recaptcha_token: token }),
              }
            );

            // If the CAPTCHA validation is successful (status 200), submit the form
            if (captchaResponse.ok) {
              console.log("response is ok");

              // Prepare form data for submission
              const formData = new FormData();
              formData.append("name", this.form.name);
              formData.append("recaptchaToken", token); // Append the reCAPTCHA token

              // Submit the form data
              const formSubmitResponse = await fetch(
                "https://api.x./api/contact",
                {
                  method: "POST",
                  body: formData,
                }
              );

              // Check if the form submission response is okay (status 200-299)
              if (formSubmitResponse.ok) {
                const result = await formSubmitResponse.json();
                this.submissionMessage = "Contact form submitted successfully!";
                this.errorMessage = null; // Clear previous error messages
                alert(this.submissionMessage); // Show success alert
              } else {
                // Handle errors from the form submission server
                const error = await formSubmitResponse.json();
                this.errorMessage = error.message || "Error submitting form.";
                alert(this.errorMessage);
                this.submissionMessage = null; // Clear previous messages
              }
            } else {
              // Handle CAPTCHA validation error
              this.errorMessage = "CAPTCHA validation failed. Please try again.";
              alert(this.errorMessage);
              this.submissionMessage = null;
            }
          } catch (error) {
            console.error("Submission error:", error);
            this.errorMessage = "An error occurred while submitting the form.";
            this.submissionMessage = null; // Clear previous messages
            alert(this.errorMessage); // Show error alert
          }
        },
        resetForm() {
          this.form.name = "";
        },
      },

     beforeUnmount() {
      this.$recaptcha.destroy();
      },
    };

    </script>

Enter fullscreen mode Exit fullscreen mode

Conclusion

By implementing this step-by-step guide, you’ve successfully integrated reCAPTCHA into your form component. This not only enhances the security of your form submissions but also ensures a smooth and transparent user experience.

“Let’s hope the bots can’t outsmart this reCAPTCHA — if they do, I’ll need to write a new article to fool them! Until then, stay secure.”

See you in next article!

Elif Nur Türk

References

Top comments (0)