January 2018 : Instructor-led Online Course in ASP.NET Core 2.0. Conducted by Bipin Joshi. Read more...
Registration for January 2018 batch of ASP.NET Core 2.0 instructor-led online course has already started. Conducted by Bipin Joshi. Register today ! Click here for more details.

Integrate reCaptcha V2 in ASP.NET Applications (Web Forms / MVC / Core)

If you are running any website that makes use of Google's reCAPTCHA for keeping bots and automated scripts away, you are probably aware that version 1.0 is being phased out. That means you need to integrate version 2.0 into your websites. To that end this article explains how to do just that. I use ASP.NET MVC project to illustrate the integration process but the same applies even for ASP.NET Web Forms and ASP.NET Core applications.

The reCaptcha V2 basically shows the end user a checkbox. They need to check the checkbox in order to prove that a bot is not at work. Depending on some internal logic they may also be presented with additional verification checks (for example, check all cars from a set of images). This all happens on the client side. However, upon submitting the form you need to evaluate whether the reCaptcha has been successfully solved by the end user. Only if the reCaptcha has been solved you will like to invoke the intended operation (say, a database INSERT). Thus, the overall verification process can be summarized like this :

  • End user requests a page that has reCaptcha embedded on it
  • The reCaptcha is rendered on the page and is shown to the user
  • End user attempts tp solve the captcha
  • The form is posted back to the server
  • Server side code evaluates the result of reCaptcha verification process.
  • Depending on the outcome of the previous step you perform some operation or throw / show error to the user.

We will implement this whole process in the following easy steps.

Step 1 - Get an API key pair

Before you begin any development integrate reCaptcha in your application you need to generate a reCaptcha key pair. The key pair consists of two keys - Site Key and Secret Key. The former key is embedded in your HTML markup and is a public key. The latter key is a private key and is used by your server side code. You must keep this key secret (and hence the name).

To get this key pair go here and click on the Get reCAPTCHA button on the right hand side top. Then fill the key registration form and click on the Register button to generate the keys. The registration form captures details such as a list of domains where these keys will be used and a friendly name for the key pair. Once you generate the keys keep them ready with you so that you can use them in the following steps.

Step 2 - Create an ASP.NET MVC project

Now, create an ASP.NET project - Web Forms, ASP.NET MVC, or ASP.NET Core. Most of the steps discussed below remain the same irrespective of the flavor of ASP.NET you use. As an example I am going to use ASP.NET MVC project. So, go ahead and create your project.

Once the project is created, create a ReCaptchaV2 folder under the project root. This is where we will store our reCaptcha class files. For ASP.NET MVC and ASP.NET Core projects add a HomeController and Index view as you normally do in your applications. For Web Forms project add Default.aspx under the root.

 

Step 3 - Add a script reference to reCaptcha library

Open the Index view and add the following script reference in the head section. For Web Forms project open Default.aspx and do the same.

 <script src='https://www.google.com/recaptcha/api.js'></script>

This was your web page gets the reCaptcha library for further use. 

Step 4 - Mark the region where reCaptcha will be rendered

Depending on the layout of the page and the nature of the data entry form, you will need to decide where exactly you would like to display the reCaptcha to the end user. Add the following markup to the Index view.

@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
  @Html.TextBox("name")<br /><br />
  <div class="g-recaptcha" 
   data-sitekey="your site key goes here"></div>
  <input type="submit" value="Submit" />
  <strong>@Html.ValidationSummary()</strong>
}

Notice the above markup carefully. The markup consists of a <div> element with two attributes. The class attribute is set to g-recaptcha. This way the reCaptcha library can easily figure out the div for rendering the reCaptcha. The data-sitekey attribute is a custom data attribute that holds the value of your Site Key. Replace it with your actual site key before you go ahead. 

The form renders a textbox where use can enter name. There is also a Submit to submit the form. Finally, a ValidationSummary helper is used to display validation errors if any.

Step 5 - Grab the reCaptcha response on the server

If you run the page now, you will see a reCaptcha rendered at the specified location. However, no verification is yet wired. So, even if reCaptcha is unsolved the form can be posted to the server the server side code (Index POST action in this example) will get executed. This is, of course, not what we expect.

 

In order to validate the reCaptcha response you first need to capture it. reCaptcha response is basically an encrypted form field that is automatically inserted in the page's payload. This response can be retrieved on the server like this :

[HttpPost]
public ActionResult Index(string name)
{
  string captchaResponse = 
Request.Form["g-Recaptcha-Response"];
  // some code here  
  return View();
}

Notice how the reCaptcha response is retrieved using the Request's Form collection. The field through which the response is sent to the server is named g-Recaptcha-Response. You need this captchaResponse value in the next step.

For ASP.NET Core the response can be retrieved as follows :

string captchaResponse = 
HttpContext.Request.Form["g-Recaptcha-Response"];

For Web Forms project you will have similar code in the click event handler of the Submit button.

Step 6 - Validate the reCaptcha response

This is probably the most important step because this is where you decide whether the end user solved the captcha successfully or not. You have the encrypted captcha response from Step 5, you also have private Secret Key from Step 1. Now, it's time to use them to validate the captcha.

In order to complete this step you need to make a POST request to the following URL :

https://www.google.com/recaptcha/api/
siteverify?secret=key_here&response=response_here

Notice how the secret and response query string parameters need to be appended to the URL.

Upon making the POST request you will be returned a JSON result that includes the following details :

  • Success indicator flag.
  • Host name.
  • Time stamp in ISO format.
  • Error messages if any.

How do you accomplish this step in ASP.NET?

You can make use of classes such as HttpClient or WebClient to make the POST request. You will then need to grab the JSON response and convert into a C# object. This can be done using Json.Net component. The HttpClient us available to Web Forms, ASP.NET MVC, and also to ASP.NET Core. The WebClient is available only to Web Forms and ASP.NET MVC projects. So, we will use HttpClient here.

Before we discuss how these objects can be used, let's do some preliminary work. Add two classes in the ReCaptchaV2 folder you created earlier - ReCaptchaValidationResult and ReCaptchaValidator. The former class holds the result of the validation (that JSON thing I explained earlier). The latter class contains a single static method - IsValid() - that does the POSTing part explained earlier.

The ReCaptchaValidationResult class is shown below:

public class ReCaptchaValidationResult
{
    public bool Success { get; set; }
    public string HostName { get; set; }
    [JsonProperty("challenge_ts")]
    public string TimeStamp { get; set; }
    [JsonProperty("error-codes")]
    public List<string> ErrorCodes { get; set; }
}

The ReCaptchaValidationResult class consists of four properties - Success, HostName, TimeStamp, and ErrorCodes. Notice the use of [JsonProperty] attribute of Json.Net component that maps a JSON property to a C# property. Also, note that ErrorCodes is a List property since there could be more than one error messages returned during the validation process.

The skeleton of ReCaptchaValidator class is shown below :

public class ReCaptchaValidator
{
    public static ReCaptchaValidationResult 
IsValid(string captchaResponse)
    {
        //code goes here
    }
}

The IsValid() method accepts the captcha response obtained in Step 5 and returns a ReCaptchaValidationResult object to the caller.

Now, let's discuss what goes inside the IsValid() method. The following code shows how HttpClient component can be used to make the POST request. Make sure to replace your private Secret Key in this code.

public static ReCaptchaValidationResult IsValid
(string captchaResponse)
{
    if (string.IsNullOrWhiteSpace(captchaResponse))
    {
        return new ReCaptchaValidationResult() 
        { Success = false };
    }

    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("https://www.google.com");

    var values = new List<KeyValuePair<string, string>>();
    values.Add(new KeyValuePair<string, string>
    ("secret", "your_secret_key_here"));
    values.Add(new KeyValuePair<string, string>
     ("response", captchaResponse));
    FormUrlEncodedContent content = 
new FormUrlEncodedContent(values);

    HttpResponseMessage response = client.PostAsync
    ("/recaptcha/api/siteverify", content).Result;

    string verificationResponse = response.Content.
    ReadAsStringAsync().Result;

    var verificationResult = JsonConvert.DeserializeObject
    <ReCaptchaValidationResult>(verificationResponse);

    return verificationResult;
}

The code checks whether captchaResponse parameter is emptyl or otherwise. If it's empty we return a ReCaptchaValidationResult object with Success property set to false.

Then we create an instance of HttpClient and set its BaseAddress property. We need to pass secret and response values in the URL encoded form as a part of the POST request. So, we use FormUrlEncodedContent class to do just that. The FormUrlEncodedContent class basically accepts a KeyValuePair objects for the required URL encoded values.

We then call PostAsync() method to make a POST request to the specified URL. The FormUrlEncodedContent object is also passed along with the request.

The result will be a JSON string and it is read using the ReadAsStringAsync() method.

This JSON string needs to be converted to ReCaptchaValidationResult object. This is done using JsonConvert's DeserializeObject() method. The resultant ReCaptchaValidationResult object is returned to the caller.

Step 7 - Show error message to the end user (if any)

Now that you have validation code in place, let's use the IsValid() method and also make an arrangement to emit the error message (if any) onto the Index view. Modify the Index() POST version as shown below :

[HttpPost]
public ActionResult Index(string name)
{
    string captchaResponse = 
Request.Form["g-Recaptcha-Response"];

    ReCaptchaValidationResult result = 
ReCaptchaValidator.IsValid(captchaResponse);

    if(!result.Success)
    {
      foreach (string err in result.ErrorCodes)
      {
         ModelState.AddModelError("", err);
      }
    }
    return View();
}

Here, we call the IsValid() method by passing the captcha response. If the Success property of ReCaptchaValidationResult is false then we add model errors using AddModelError() method of ModelState. This way the validation summary helper on the Index view will display the error messages.

The following figure shows a sample erroneous run of the captcha.

 

That's it for now! Keep coding!!


Bipin Joshi is a software consultant, an author and a yoga mentor having 22+ years of experience in software development. He also conducts online courses in ASP.NET MVC / Core and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Meditation and Mindfulness to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 30 October 2017


Tags : ASP.NET ASP.NET Core MVC C# Visual Studio