FullStack implementation of reCaptcha with Vue.js and .Net
Tags: vue, javascript, csharp
Published: 2019-09-26 17:58:40

## 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.

* [Vue.js]([https://vuejs.org/v2/guide/](https://vuejs.org/v2/guide/))
* [.Net PT-BR](https://www.devmedia.com.br/introducao-ao-asp-net-web-api/25180) and [.Net EN-US](https://docs.microsoft.com/en-us/aspnet/web-api/)
* [Axios]([https://github.com/axios/axios](https://github.com/axios/axios))
* [BootstrapVue]([https://bootstrap-vue.js.org/](https://bootstrap-vue.js.org/))

## 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](https://i.imgur.com/s7gniNm.png)
  
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](https://i.imgur.com/gtCutfv.png)
  
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](https://medium.com/the-node-js-collection/making-your-node-js-work-everywhere-with-environment-variables-2da8cdf6e786)), since the **secret key** will be used on back end in nosse *web.config*

![enter image description here](https://i.imgur.com/yGXd7nd.png)

  
To learn more see full reCaptcha [documentation](https://developers.google.com/recaptcha/docs/display).

## 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](https://yarnpkg.com/en/package/vue-recaptcha)
 ### 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
```javascript
    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**:
```javascript
    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.
```javascript
    <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**:
```javascript
    <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:
```csharp
    <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**
```csharp
    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**:
```csharp
    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
  
```csharp
      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🤖