Adding Client Side Validation on reCaptcha V2 in Umbraco 7 - 28th October 2020

I needed to add a reCaptcha to a contact form on a large website I recently built. Shortly after the website went live, there was a massive amount of spam coming from the contact form, so the obvious thing to do was to add a reCaptcha to it.

I added a reCaptcha and it worked great, but the ony issue I had was that it couldn't validate if the reCaptcha had been checked on the client side. To do this required the form to be posted back to the server side. As the other form fields were validated on the client side, it looked strange that you could submit the form without checking the reCaptcha and only then show a message to say the reCaptcha needed checking. But I managed to find a way of doing it using JavaScript.

Adding the reCaptcha

There ara a few ways to add a reCaptcha to an Umbraco website project. I chose to add it using NuGet package installer, as it is the easiest and quickest way. There are several reCaptcha packages available on NuGet, I chose reCaptcha.MVC by Bhaumik Patel, you can install using the NuGet Package Manager for Solutions or by using the NuGet Package Manager Console, documentation and installation instuctions can be found here: http://recaptchamvc.apphb.com/Home/Document.

Once installed you will need to get reCaptcha Version 2 public and private keys from Google https://www.google.com/recaptcha/about. You will then need to add them to your web.config.

<appSettings>
    <add key="reCaptchaPublicKey" value="**your public key**"/>
    <add key="reCaptchaPrivateKey" value="**your private key**"/>
</appSettings>

The next step is to add the reCaptcha to your contact form view, I'm using Bootstrap in my project, so my view reflects this:

@inherits UmbracoViewPage<Project1.Models.ContactModel>
@using reCAPTCHA.MVC

@using (Html.BeginUmbracoForm("SubmitForm", "Contact", FormMethod.Post))
{
    @Html.AntiForgeryToken()

       <div id="contact-form">
        <div class="row">
            <div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
                @Html.LabelFor(m => m.FirstName)<br>
                @Html.TextBoxFor(m => m.FirstName, new Dictionary<string, Object> { { "required", "" }, { "class", "width-100pc" } })
                @Html.ValidationMessageFor(m => m.FirstName)
            </div>
            <div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
                @Html.LabelFor(m => m.LastName)<br>
                @Html.TextBoxFor(m => m.LastName, new Dictionary<string, Object> { { "required", "" }, { "class", "width-100pc" } })
                @Html.ValidationMessageFor(m => m.LastName)
            </div>
        </div>
        <div class="row">
            <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
                @Html.LabelFor(m => m.EmailAddress)<br>
                @Html.TextBoxFor(m => m.EmailAddress, new Dictionary<string, Object> { { "required", "" }, { "class", "width-100pc" } })
                @Html.ValidationMessageFor(m => m.EmailAddress)
            </div>
        </div>        
        <div class="row">
            <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
                @Html.LabelFor(m => m.Message)<br>
                @Html.TextAreaFor(m => m.Message, new Dictionary<string, Object> { { "required", "" }, { "rows", "5" }, { "class", "width-100pc" } })
                @Html.ValidationMessageFor(m => m.Message)
            </div>
        </div>       
        <div class="row">
            <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">              
                @Html.Recaptcha()
                @Html.ValidationMessage("ReCaptcha")
                <p id="recaptchaMessage" class="validation-summary hide">Please complete the reCAPTCHA.</p>
            </div>
        </div>
        @if (!ViewData.ModelState.IsValid)
        {
            <div class="row">
                <div class="col-lg-12 col-md-12 col-sm-6 col-xs-12">
                    <span class="validation-summary">
                        @Html.ValidationSummary(true, "Something is missing! Please check everything is selected or filled in correctly and try again.")
                    </span>
                </div>
            </div>
        }
        <div class="row">
            <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
                <button id="ContactSubmit">Submit</button>
            </div>
        </div>
    </div>
}

Now you can add server side validation in your form controller. I added a RecaptchaVerificationHelper to handle the server side validation and I also added a required message. The required message is an overload on the CaptchaValidator. You also need to add a bool for a valid captcha on the ActionResult.

[CaptchaValidator(RequiredMessage="Please check I'm not a robot.")]
public ActionResult SubmitForm(ContactModel model, bool captchaValid)  

Now we can add a RecaptchaVerificationHelper and use it to validate the reCaptcha on the server side.

RecaptchaVerificationHelper recaptchaHelper = this.GetRecaptchaVerificationHelper();
if (string.IsNullOrEmpty(recaptchaHelper.Response))
{
    ModelState.AddModelError("reCAPTCHA", "Please complete the reCAPTCHA");
    return CurrentUmbracoPage();
}
else
{
    RecaptchaVerificationResult recaptchaResult = recaptchaHelper.VerifyRecaptchaResponse();
    if (recaptchaResult != RecaptchaVerificationResult.Success)
    {
        ModelState.AddModelError("reCAPTCHA", "The reCAPTCHA is incorrect");
        return CurrentUmbracoPage();
    }
}

You can see the full code below for the HttpPost section of the controller:

[HttpPost]
[CaptchaValidator(RequiredMessage="Please check I'm not a robot.")]
[ValidateAntiForgeryToken]
public ActionResult SubmitForm(ContactModel model, bool captchaValid)
{
    RecaptchaVerificationHelper recaptchaHelper = this.GetRecaptchaVerificationHelper();
    if (string.IsNullOrEmpty(recaptchaHelper.Response))
    {
        ModelState.AddModelError("reCAPTCHA", "Please complete the reCAPTCHA");
        return CurrentUmbracoPage();
    }
    else
    {
        RecaptchaVerificationResult recaptchaResult = recaptchaHelper.VerifyRecaptchaResponse();
        if (recaptchaResult != RecaptchaVerificationResult.Success)
        {
	        ModelState.AddModelError("reCAPTCHA", "The reCAPTCHA is incorrect");
	        return CurrentUmbracoPage();
        }
    }
    
    if (ModelState.IsValid)
    {		
	    return RedirectToCurrentUmbracoPage();
    }    
    else
    {
	    return CurrentUmbracoPage();
    }
}

Ok, this is great, but it only validates on the server side and I want to validate if the reCaptcha has been checked or not on the client side. After quite a bit of searching the web and reading lots of articles and plenty of Stack Overflow posts, I stumbled upon this post from Mahatma Aladdin https://stackoverflow.com/questions/27902539/how-can-i-validate-google-recaptcha-v2-using-javascript-jquery. The answer by Palak Tanejaa was exactly what was needed, so here's how I implemented it:

In the form I added an onsubmit overload to the Html.BeginUmbracoForm: new { onsubmit = "return submitContact();" }, see the full line below:

@using (Html.BeginUmbracoForm("SubmitForm", "Contact", FormMethod.Post, new { onsubmit = "return submitContact();" }))
I then used the suggested code from Palak Tanejaa's answer on Stackoverflow to create the following Javascript at the bottom of the form:
<script>
    function submitContact() {
        var response = grecaptcha.getResponse();

        if (response.length == 0) {
           //show reCaptcha required message                         
            return false;
        }
        else
        {
            //hide reCaptcha required message 
            return true;
        }           
    }
</script>

There are a few ways you can show the required message, you can use JavaScript/JQuery to show/hide the message, you can also use it to add/remove the Bootstrap hide class and there are probably other ways. I'll let you decide which way you want to do it.

Below is the amended form view to include the onsubmit and the JavaScript code:

@inherits UmbracoViewPage<Project1.Models.ContactModel>
@using reCAPTCHA.MVC

@using (Html.BeginUmbracoForm("SubmitForm", "Contact", FormMethod.Post, new { onsubmit = "return submitContact();" }))
{
    @Html.AntiForgeryToken()

       <div id="contact-form">
        <div class="row">
            <div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
                @Html.LabelFor(m => m.FirstName)<br>
                @Html.TextBoxFor(m => m.FirstName, new Dictionary<string, Object> { { "required", "" }, { "class", "width-100pc" } })
                @Html.ValidationMessageFor(m => m.FirstName)
            </div>
            <div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
                @Html.LabelFor(m => m.LastName)<br>
                @Html.TextBoxFor(m => m.LastName, new Dictionary<string, Object> { { "required", "" }, { "class", "width-100pc" } })
                @Html.ValidationMessageFor(m => m.LastName)
            </div>
        </div>
        <div class="row">
            <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
                @Html.LabelFor(m => m.EmailAddress)<br>
                @Html.TextBoxFor(m => m.EmailAddress, new Dictionary<string, Object> { { "required", "" }, { "class", "width-100pc" } })
                @Html.ValidationMessageFor(m => m.EmailAddress)
            </div>
        </div>        
        <div class="row">
            <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
                @Html.LabelFor(m => m.Message)<br>
                @Html.TextAreaFor(m => m.Message, new Dictionary<string, Object> { { "required", "" }, { "rows", "5" }, { "class", "width-100pc" } })
                @Html.ValidationMessageFor(m => m.Message)
            </div>
        </div>       
        <div class="row">
            <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">              
                @Html.Recaptcha()
                @Html.ValidationMessage("ReCaptcha")
                <p id="recaptchaMessage" class="validation-summary hide">Please complete the reCAPTCHA.</p>
            </div>
        </div>
        @if (!ViewData.ModelState.IsValid)
        {
            <div class="row">
                <div class="col-lg-12 col-md-12 col-sm-6 col-xs-12">
                    <span class="validation-summary">
                        @Html.ValidationSummary(true, "Something is missing! Please check everything is selected or filled in correctly and try again.")
                    </span>
                </div>
            </div>
        }
        <div class="row">
            <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
                <button id="ContactSubmit">Submit</button>
            </div>
        </div>
    </div>
}
    
<script>
    function submitContact() {
        var response = grecaptcha.getResponse();

        if (response.length == 0) {
           //show reCaptcha required message                       
            return false;
        }
        else
        {
            //hide reCaptcha required message    
            return true;
        }           
    }
</script>

You can move the JavaScript to a separate js file if you want, but I left it here to show how it's done.

So there you are, a sinple way of validating if a reCaptcha version 2 has been checked on the client side.

Sources:

About Paul Jacques

I’m Paul Jacques a Bradford, West Yorkshire based freelance web designer and developer. I aim to help you get the most from your website by providing affordable, search engine optimised, fast loading, mobile friendly and most importantly, secure websites.

Find out more about how I can help you

Back to Blog