loading...
Cover image for FullStack implementation of reCaptcha with Vue.js and .Net

FullStack implementation of reCaptcha with Vue.js and .Net

wakeupmh profile image Marcos Henrique Updated on ・5 min read

Introduction 😊

This article will cover how to implement google reCaptcha v2 (I tried to implement v3 but in my case the manipulation of the score through an admin console was not interesting) using Vue.js (with axios and BootstrapVue) on the front end and .Net on the back- end, I intend to pursue this issue with other technologies for the same approach. So, let's get to work, I'll take into consideration that you already have a prior knowledge of both technologies (vue and C #), but if not, I will leave these documentation links as a guide.

reCaptcha account 🔄

To use reCaptcha you first need to create an account, for that you will need a google account, just fill in this screen below, as in our example we are using reCaptcha v2 we will select it in the reCAPTCHA type field in the first step, as we are testing and developing, the domain will be set to localhost, but when going to production be sure to modify this field with your domain.
google recaptcha

In this example I will use the reCapathca "I'm not a robot" Checkbox type as seen in the image below:
enter image description here

Once that is done, you will have your keys available, the site key should be used on the front end, and to keep it safe we ​​will use environment variables (to learn more about why to use them click here), since the secret key will be used on back end in nosse web.config

enter image description here

To learn more see full reCaptcha documentation.

Front-End 💻

To use reCaptcha I followed the documentation steps, but with the help of a package from yarn to vue, vue-recaptcha, which provides me with a basically ready component, just implement it and capture the event from validation you can see more about it here
### Instalation 🎁
yarn add vue-recaptcha
yarn add axios

In my dev.env.js where I store my environment variables I will set the site key and url of my api, which in this case will be running on localhost

    module.exports = {
      NODE_ENV: '"development"',
      RECAPTCHA_PUBLIC_KEY:'"6__________yourkey________________c"',
      process.env.API_URL: '"http://localhost:55348/api'" 
    })   

I'll start by creating the post service on my route with the axios that will post to google api and return me an object validating whether or not it was successful
services/recaptchaValidate.js:

    import axios from 'axios'

    export default {
      validate (params) {
        return new Promise((resolve, reject) => {
          axios.post(`${process.env.API_URL}/recaptcha/validate`, params)
            .then(response => {
              if (response.data.hasErrors) {
                reject(response.data.message)
              } else {
                resolve(response.data)
              }
            })
            .catch(error => {
              if (error.response.data.hasOwnProperty('hasErrors')) {
                reject(error.response.data.message)
              } else {
                reject(error.message)
              }
            })
        })
      }
    }

This done I created a component for recaptcha in my project named Recaptcha.vue, which will issue my validation to the parent component that will receive my validation to enable or not the forward button.

    <template>
         <VueRecaptcha :sitekey="this.sitekey" :loadRecaptchaScript="true" @verify="validate"/>
     </template>
     <script>
        import VueRecaptcha from 'vue-recaptcha'
        import Validation from '@/services/recaptchaValidate'
        export default {
          components: {VueRecaptcha},
          data () {
            return {
              sitekey: process.env.RECAPTCHA_PUBLIC_KEY
            }
          },
          methods: {
            validate (response) {
              Validation.validate({Response: response}).then(result => {
                this.$emit('validate', result.objectResult.success)
              }).catch(error => console.log(error))
            }
          }
        }
      </script>

The Parent component:

    <template>
     <b-row>
      <b-col class="p-5 mb-2">
        <div class="text-center justify-center align-center">
          <Recaptcha v-if="!logged" @validate="validate"/>
        </div>
      </b-col>
    </b-row>
    <b-row no-gutters>
      <b-col class="p-0">
        <div class="text-center">
          <a @click="checkFrom('next')" :class="this.validateRecaptcha ? '' : 'disabled'" class="btn btn-primary text-white m-1">Forward</a>
        </div>
      </b-col>
    </b-row>
    </template>
    import Recaptcha from '@/components/recaptcha/Recaptcha'
    export default {
        components: {Recaptcha},
        data () {
            return {
                validateRecaptcha: false
            }
        },
        methods: {
            validate (success) {
            this.validateRecaptcha = success
        }
    }

Back-End 🌐

Web.config to store my secret key and the url to google reCaptcha API:

    <configuration>
        <appSettings>
            <add key="RECAPTCHA_SECRET_KEY" value="6______yourkey________o"/>
            <add key="RECAPTCHA_GOOGLE_URL" value="https://www.google.com/recaptcha/api/siteverify"/>
        </appSettings>
    </configuration>

I am creating a model to handle the request that will be received by my route named RecaptchaRequest.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;

    namespace Recaptcha.Models.Requests
    {
        public class RecaptchaRequest
        {
            public string Response { get; set; }
        }
    }
I am creating a model to handle the response of google API named **RecaptchaResponse.cs**:

    using System;
    using System.Collections.Generic;
    using System.Web;

    namespace Recaptcha.Models.Responses
    {
        public class RecaptchaResponse
        {
            public bool Success { get; set; }
            public string Hostname { get; set; }
        }
    }

I will not go into the details of how my BaseController is made, but it is a set of methods where I manipulate the responses to return the consumer of my route, feel free to do it your own way, because the goal is just show how to access the google API, so below follows my controller named RecaptchaController.cs:

    using Recaptcha.Business;
    using Recaptcha.Models.Requests;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;

    namespace Recaptcha.Controllers
    {
        [RoutePrefix("api/recaptcha")]
        public class RecaptchaController : BaseController
        {
            [HttpPost]
            [Route("validate")]
            public async System.Threading.Tasks.Task<HttpResponseMessage> ValidateAsync(RecaptchaRequest recaptchaRequest)
            {
                if (!Token.IsValid)
                    return GetResponseFromInvalidToken();

                var response = await RecaptchaBusiness.RetrieveResponse(recaptchaRequest);
                return GetResponseFromResults(HttpStatusCode.OK, "", response);
            }
        }
    }

And finally my Business named RecaptchaBusiness.cs where I make the request via post to google api and get the json that I send to my front, informing if who accessed the checkbox is or is not a robot

      using Recaptcha.Models.Requests;
        using Recaptcha.Models.Responses;
        using System.Collections.Generic;
        using System.Configuration;
        using System.Net.Http;

        namespace Recaptcha.Business
        {
            public class RecaptchaBusiness
            {
                private static readonly HttpClient client = new HttpClient();
                public static async System.Threading.Tasks.Task<RecaptchaResponse> RetrieveResponse(RecaptchaRequest recaptchaRequest)
                {
                    var values = new Dictionary<string, string>
                    {
                        { "secret", ConfigurationManager.AppSettings["RECAPTCHA_SECRET_KEY"].ToString()},
                        { "response", recaptchaRequest.Response}
                    };

                    var content = new FormUrlEncodedContent(values);

                    var response = await client.PostAsync(ConfigurationManager.AppSettings["RECAPTCHA_GOOGLE_URL"].ToString(), content);

                    var recaptchaResponse = new RecaptchaResponse();
                    return await response.Content.ReadAsAsync<RecaptchaResponse>();

                }
            }
        }

Remembering that validation cannot be done by the client (front) if you try this, you get that hellish CORS error 😅

Thank you for your attention and your devoting your time to this short read, now you are able to avoid the robot s🤖

Posted on by:

wakeupmh profile

Marcos Henrique

@wakeupmh

"Programming isn't about what you know; it's about what you can figure out.”

Discussion

markdown guide