'How to use google recaptcha in nuxt?

I am using nuxt and would like to use this library: https://github.com/nuxt-community/recaptcha-module. But I don't understand how to verify that the user has passed the check. The example doesn't tell me too much (https://github.com/nuxt-community/recaptcha-module/blob/master/example/v3/pages/index.vue). Could someone show me how to do it correctly?



Solution 1:[1]

This example is only half the story. It returns a Recaptcha V3 token on the client-side.

This must then be sent to the serverside and verified using your secret key.

This is done by sending a post to this URL:

const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${token}`;

You do not want to allow this secret key on the client side.

To achieve this in Nuxt, assuming version 2.13+, you can utilise privateRuntimeConfig in your nuxt config.

This will allow you to link a .env file to be injected only on the server side.

For this use case, a privateRuntimeConfig like this should suffice:

privateRuntimeConfig: {
    secretKey: process.env.GOOGLE_SECRET
}

Once you have done this, you will be able to access these variables as part of this.$config within your Nuxt application - in this case this.$config.secretKey when calling the Recaptcha verify endpoint.

For more information check out the Nuxt blog

Solution 2:[2]

Use https://github.com/nuxt-community/recaptcha-module, in your nuxt.config.js

modules: [
  '@nuxtjs/recaptcha',
],

recaptcha: {
  hideBadge: true,
  siteKey: "ABC...", // Better would be from 'process.env.API_KEY' and with '.env' file
  version: 2, // Or 3
},

Keep in mind that modules, that's not the same as buildModules (sometimes it might confuse due to similar naming).

Solution 3:[3]

Here is a working implementation for ReCaptcha V3:

package.json

  "dependencies": {
    "@nuxtjs/axios": "^5.13.6",
    "@nuxtjs/recaptcha": "^1.0.4",
    "h3": "^0.3.9",
  },

Note the h3 version. I wasn't able to get it working with a newer version of that because the library is converted to EJS/mjs and TypeScript, which conflicts with Nuxt. Transpiling h3 didn't fix it. It may work with Nuxt V3+.

nuxt.config.js

  modules: [
    ['@nuxtjs/recaptcha', {
      siteKey: process.env.RECAPTCHA_SITE_KEY,
      version: 3,
    }],
  ],

  serverMiddleware: [
    { path: '/api/check-token', handler: '~/middleware/recaptcha' },
  ],

middleware/recaptcha.js

import { useBody } from 'h3';
import axios from 'axios';

export default async (req, res) => {
  res.setHeader('Content-Type', 'application/json');
  try {
    const { token } = await useBody(req);

    if (!token) {
      res.end(
        JSON.stringify({
          success: false,
          message: 'Invalid token'
        })
      );
      return;
    }

    axios.get(`https://www.google.com/recaptcha/api/siteverify?secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${token}`).then((answer) => {
      if (answer.status) {
        res.end(
          JSON.stringify({
            success: true,
            message: 'Token verified'
          })
        );
      } else {
        res.end(
          JSON.stringify({
            success: false,
            message: 'Invalid token'
          })
        );
      }
    });
  } catch (e) {
    console.log('ReCaptcha error:', e);
    res.end(
      JSON.stringify({
        success: false,
        message: 'Internal error'
      })
    );
  }
};

.env

RECAPTCHA_SITE_KEY=gosei478htosvei478tvoei478tvge
RECAPTCHA_SECRET_KEY=ios47eos487t6es4897gtv6es487

index.vue

async mounted() {
    try {
        await this.$recaptcha.init();
    } catch (err) {
        throw new Error(`index# Problem initializing ReCaptcha: ${err}.`);
    }
},

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

methods: {
    async submitContactForm() {
        try {
            const token = await this.$recaptcha.execute('contact')

            const formData = {
                email: this.contactForm.email,
                firstname: name.firstName,
                lastname: name.lastName,
                phone: this.contactForm.phone,
                band_name: this.contactForm.band_name,
                initial_message: this.contactForm.message,
            }

            // note: use POST request
            const recaptcha = await this.$axios.post('/api/check-token', { token });

            console.log('recaptcha', recaptcha.data);

            if (recaptcha.data.success) {
                const result = await this.$axios.post(process.env.CONTACT_FORM_API, formData);


                // cleanup logic
            } else {
                // handle error case
            }
        } catch (err) {
            // handle errors
        }
    },
},

You can read more here: https://www.npmjs.com/package/@nuxtjs/recaptcha

Note the section where it says

Server Side When you send data + token to the server, you should verify the token on the server side to make sure it does not requested from a bot. You can find out how to verify token on the server side by looking at the server middleware inside v2 example. (The server side is same for both versions)

The above server-side middleware comes from there. It is important to use the version of h3 that I suggest because you need it to access useBody(req). I tried for several hours to find another way to read the request body but it proved too difficult. Your results may vary in a newer version of Nuxt. I suggest trying the newest version of h3 and if that fails with errors when building the application, try the older version.

It is critically important to not expose the ReCaptcha secret key, and this solution keeps it secret in the server-side.

A more optimal solution might be to use your actual server and make an endpoint for validating ReCaptcha tokens. This above solution allows you to do it purely client-side, assuming you are using SSR.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Daniel Danielecki
Solution 3 agm1984