Using Google’s reCAPTCHA Version 2.0 on ASP.NET MVC with AJAX

So, you want to add the latest reCAPTCHA Version 2.0 to your existing MVC website, you want to make it secure with server-side validation, and you want to make it asynchronous and have a rich user experience with AJAX? No problem. Start by adding div’s into the forms you need. Their ID’s will indicate where we want to place CAPTCHA elements. Note for all intents and purposes I am using two different CAPTCHA elements on the same page, just to illustrate it can be done, and done nicely:

<form id="register-form">
    <div id="register-captcha"></div>
    <button id="register-button" type="button" class="btn btn-default">Register</button>
</form>
<form id="contact-form">
    <div id="contact-captcha"></div>
    <button id="contact-button" type="button" class="btn btn-default">Send</button>
</form>

Then wire these up. Take note of how the reCAPTCHA API allows us to tell it what loading method to use and what type of rendering we want (explicit, in this case). This is very important, as it allows us to control the CAPTCHA in JavaScript. Also, make sure to include your site key in the rendering calls:

@section Scripts
{
    <script type='text/javascript'>
        // CAPTCHA loading methods.
        var registrationCaptchaID = null;
        var contactCaptchaID = null;
        var loadCaptcha = function () {
            registrationCaptchaID = grecaptcha.render('register-captcha', {
                'sitekey': '1234567890123456789012345678901234567890',
                'callback': function (response) {
                    registrationCaptchaResponse = response;
                }
            });
            contactCaptchaID = grecaptcha.render('contact-captcha', {
                'sitekey': '1234567890123456789012345678901234567890',
                'callback': function (response) {
                    contactCaptchaResponse = response;
                }
            });
        };
        // Register CAPTCHA section.
        var registrationCaptchaResponse = null;
        $('#register-button').click(function () {
            if (registrationCaptchaResponse == null)
                return;
            $.ajax({
                url: '@Url.Action("Register", "Home")',
                type: 'POST',
                data: {
                    recaptchaResponse: registrationCaptchaResponse
                },
                success: function (data, textStatus, jqXHR) {
                    registrationCaptchaResponse = null;
                    grecaptcha.reset(registrationCaptchaID);
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    registrationCaptchaResponse = null;
                    grecaptcha.reset(registrationCaptchaID);
                }
            });
        });
        // Contact CAPTCHA section.
        var contactCaptchaResponse = null;
        $('#contact-button').click(function () {
            if (contactCaptchaResponse == null)
                return;
            $.ajax({
                url: '@Url.Action("Contact", "Home")',
                type: 'POST',
                data: {
                    recaptchaResponse: contactCaptchaResponse
                },
                success: function (data, textStatus, jqXHR) {
                    contactCaptchaResponse = null;
                    grecaptcha.reset(contactCaptchaID);
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    contactCaptchaResponse = null;
                    grecaptcha.reset(contactCaptchaID);
                }
            });
        });
    </script>
    <script src="https://www.google.com/recaptcha/api.js?onload=loadCaptcha&render=explicit" async defer></script>
}

To validate this on the server-side, first set up the secret key in your configuration’s application settings section so you can quickly modify on the fly later on when you need to:

<configuration>
  <appSettings>
    <add key="reCAPTCHASecret" value="1234567890123456789012345678901234567890" />
  </appSettings>
</configuration>

Make use of it on an action post, and call the reCAPTCHA service to manually verify the user-obtained results:

public class reCAPTCHAResponse
{
    public bool Success { get; set; }
}

[HttpPost]
public ActionResult Register(string recaptchaResponse)
{
    if (String.IsNullOrEmpty(recaptchaResponse))
        throw new InvalidOperationException("Missing CAPTCHA response.");
    var client = new WebClient();
    var secret = ConfigurationManager.AppSettings["reCAPTCHASecret"];
    if (String.IsNullOrWhiteSpace(secret))
        throw new InvalidOperationException("Missing CAPTCHA configuration.");
    var response = client.DownloadString(String.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secret, recaptchaResponse));
    var serializer = new JavaScriptSerializer();
    var reCAPTCHA = serializer.Deserialize<reCAPTCHAResponse>(response);
    if (!reCAPTCHA.Success)
        throw new InvalidOperationException("Incorrect CAPTCHA response.");
    return Json(true); // At this point, the request is valid, and you can do whatever you wish.
}

That’s another case closed.