This website uses cookies

This website uses cookies to give you the best and most relevant experience. By continuing to browse this site, you are agreeing to our use of cookies. Learn More.

Implementing Google reCaptcha V2 in ASP.Net Core using Model Binding and Ajax

Google's reCaptcha is an effective tool for protecting websites against spammy bots. In most cases, valid users only have to click a checkbox to go through easily. By employing an advanced risk analysis mechanism, it provides challenges when the risk level is deemed high enough which discourages bots from engaging further with the website. This article describes the implementation of reCaptcha v2.0 in Asp.Net Core using model binding and ajax calls to retrieve and validate the user's response.

The first step to implement reCaptcha is to sign up, provide a list of domains and obtain the Site Key and Secret Key. The site key is used to display the widget on the web page while the secret key is used to validate the response with Google's servers.

The reCaptcha's javascript file must be included in the head tag.

reCaptcha's javascript file in tag
<script src='https://www.google.com/recaptcha/api.js'></script>

A <div> containing the site key as a data-sitekey attribute has to be added in the <body> tag as a placeholder wherever the reCaptcha needs to be displayed.

Div with site key
<div class="g-recaptcha" data-sitekey="YOUR SITE KEY GOES HERE"></div>

When the user solves the reCaptcha, a new field (g-recaptcha-response) will be added in the HTML. This can be retrieved as a g-recaptcha-response POST parameter when the user submits the form and used to validate against Google's servers. The api details are as follows:

  • Url: https://www.google.com/recaptcha/api/siteverify
  • Method: POST
  • Post Parameters:
Parameter Description
secret Required. The shared key between your site and reCAPTCHA.
response Required. The user's response token (g-recaptcha-response) provided by reCAPTCHA used to verify the user on your site.
remoteip Optional. The user's IP address.

Verifying User's Response in the Controller

When the form is submitted, the reCaptcha verification is done by posting the secret, g-captcha-response and optionally the caller's ip to Google's siteverify api.

Verifying reCaptcha with Google's servers
//model class - this is in a different file
//using statements omitted
public class ContactModel {
   [Required]
   [MaxLength(100)]
   public string Name { get; set; }

   [Required]      
   [MaxLength(100)]
   [EmailAddress]
   public string Email{ get; set; }

   [Required]
   [MaxLength(200)]
   public string Subject { get; set; }

   [Required]
   public string Message { get; set; }

   [Required(ErrorMessage = "Please click on the 'I\'m not a robot' box at the bottom of the page")]
   [FromForm(Name = "g-recaptcha-response")]
   public string ReCaptcha { get; set; }
}

public class CaptchaVerification {
   [JsonProperty("success")]
   public bool Success { get; set; }

   [JsonProperty("error-codes")]
   public List Errors { get; set; }
}

//--------------------------------------------------------------
//different class (controller)
//using statements omitted

public class ContactController : Controller {
   [HttpGet]
   public IActionResult Index() {
      return View();
   }

   [HttpPost]
   public async Task<IActionResult> Index([FromForm]ContactModel model) {
      if (!ModelState.IsValid) return View(model);

      var result = await VerifyCaptcha(model.ReCaptcha);
      if (!result.Success) {
         ModelState.AddModelError("", "Captcha is not valid");
         return View(model);
      }

      //carry on with processing
      //save the data... 
   }

   // verify the captcha's response with Google
   private async Task<CaptchaVerification> VerifyCaptcha(string captchaResponse) {
      string userIP = string.Empty;
      var ipAddress = Request.HttpContext.Connection.RemoteIpAddress;
      if (ipAddress != null) userIP = ipAddress.MapToIPv4().ToString();

      var captchaResponse = Request.Form["g-recaptcha-response"];
      var payload = string.Format("&secret={0}&remoteip={1}&response={2}" 
         "YOUR SECRET KEY",
         userIP,
         captchaResponse
      );

      var client = new HttpClient();
      client.BaseAddress = new Uri("https://www.google.com");
      var request = new HttpRequestMessage(HttpMethod.Post, "/recaptcha/api/siteverify");
      request.Content = new StringContent(payload, Encoding.UTF8, "application/x-www-form-urlencoded");
      var response = await client.SendAsync(request);
      return JsonConvert.DeserializeObject<CaptchaVerification>(response.Content.ReadAsStringAsync().Result);     
   }
}

The captcha's reponse retrieval is made easier by using asp.net's model binding features and adding a property to the model class. Alternatively, it can be retrieved from form variables as follows:

Retrieving response value
var captchaResponse = Request.Form["g-recaptcha-response"];

This allows the verification of the reCaptcha response against Google's servers before the form is submitted. It is important to note that calling Google's reCaptcha verify api directly from javascript may expose the secret. A better approach is to make an ajax call to an api on the host application server which would then talk to Google's servers for verification.

jQuery call to verification api on server side
$(document).ready(function () {
         $("form").submit(function (e) {                
            var recaptcha = $("#g-recaptcha-response").val();
            if (recaptcha === "") {
               alert("Please check the recaptcha");
            } else {
               $.ajax({
                  async:false,
                  url: "/api/captcha/verify",
                  method: "POST",
                  data: { "captchaResponse" : recaptcha },

                  success: function (response) {
                     if (response.success === true) {
                        alert("captcha verification was successful\n" + JSON.stringify(response));
                     } else {
                        alert("captcha verification NOT successful \n" + JSON.stringify(response));
                        e.preventDefault();
                     }
                  },

                  error: function (jqXHR, textStatus, errorThrown) {
                     alert("captcha verification NOT successful");
                     e.preventDefault();
                  }
               });
            }                
         });
     });
Api in controller class
[HttpPost("api/captcha/verify")]
public async Task Verify(string captchaResponse) {
   var result = await VerifyCaptcha(captchaResponse);
   return new JsonResult(result);
}

The example project and source code is available from my github repo.