Enable Submit when reCaptcha V2 checked 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.

I managed to find a couple of ways of preventing this, you can either enable the submit button when the reCaptcha is checked or validate the reCaptcha on the client side using JavaScript. I chose the quicker to implement option of enabling the submit button when the reCaptcha is checked, so in this blog post that is what I am going to show you how to do. To find out how to add client side validation using javaScript, take a look at the blog post: Adding Client Side Validation on reCaptcha V2 in Umbraco 7

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.

The full code for my view is below:

@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")               
            </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 prevent it from posting the form if the reCaptcha has not been checked. So here is ho to do it.

In the form we need to add callback parameters to "@Html.Recaptcha()", the code that adds the reCaptcha to the form, see the line below:

  
@Html.Recaptcha(callback: "enableSubmit")

We alo need to disable the submit button by adding disabled="disabled" to it.

  
<button id="ContactSubmit" disabled="disabled">Submit</button>

The callback function will be called "enableSubmit". I created the following Javascript function for the callback at the bottom of the form. My submit button has an id of "ContactSubmit", so that is what getElementById uses to enable the button by setting disabled to false:

<script>     
function enableSubmit() {            
    document.getElementById("ContactSubmit").disabled = false;
}
</script>

Below is the amended form view to include the callback parameters, disabled submit button and the JavaScript callback function:

@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(callback: "enableSubmit")
                @Html.ValidationMessage("ReCaptcha")               
            </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" disabled="disabled">Submit</button>
            </div>
        </div>
    </div>
}
    
<script>
function enableSubmit() {            
    document.getElementById("ContactSubmit").disabled = false;   
}
</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, an easy way to stop the form being submitted if a reCaptcha version 2 has not been checked.

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