ASP.NET jQuery Validation CAPTCHA C# Code Sample

This sample project shows how to integrate BotDetect ASP.NET CAPTCHA validation with jQuery Validation client-side form validation.

First Time Here?

Check the BotDetect ASP.NET WebForms Captcha Quickstart for key integration steps.

Client-side validation is not secure by itself (it can be bypassed trivially by bots that don't execute JavaScript at all), so the sample shows how the protected form action must always be secured by server-side CAPTCHA validation first, and uses client-side validation only to improve the user experience.

Download the BotDetect ASP.NET CAPTCHA Component and run this sample

Visual Studio 2012 / .NET 4.5

By default, the .NET 4.5 C# version of the ASP.NET jQuery Validation Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v4.5\WebApp\AspNetJQueryValidationCaptchaSample\ CSharp

You can also run it from the BotDetect Start Menu:
Programs > Lanapsoft > BotDetect 3 CAPTCHA Component > ASP.NET > DotNET 4.5 Web Applications > Run

The Visual Studio 2012 / .NET 4.5 source has no essential differences from the Visual Studio 2010 / .NET 4.0 source.

Visual Studio 2010 / .NET 4.0

By default, the .NET 4.0 C# version of the ASP.NET jQuery Validation Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v4.0\WebApp\AspNetJQueryValidationCaptchaSample\ CSharp

You can also run it from the BotDetect Start Menu:
Programs > Lanapsoft > BotDetect 3 CAPTCHA Component > ASP.NET > DotNET 4.0 Web Applications > Run

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" 
  Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.
w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
  <title>BotDetect CAPTCHA jQuery Validation ASP.NET Sample</title>
  <link type="text/css" rel="Stylesheet" href="StyleSheet.css" />
</head>
<body>
  <form id="form1" runat="server">
  <h1>BotDetect CAPTCHA jQuery Validation ASP.NET Sample</h1>
  <fieldset>
    <legend>CAPTCHA Validation</legend>
    <p class="prompt"><label for="CaptchaCodeTextBox">Retype the 
    characters from the picture:</label></p>
    <BotDetect:Captcha ID="SampleCaptcha" runat="server" />
    <div class="validationDiv">
      <!-- add CssClass name used to set up jQuery field validation -->
      <asp:TextBox ID="CaptchaCodeTextBox" CssClass="captchaVal" 
        runat="server"></asp:TextBox>
      <asp:Button ID="ValidateCaptchaButton" runat="server" />
      <asp:Label ID="CaptchaCorrectLabel" runat="server" 
      CssClass="correct"></asp:Label>
      <asp:Label ID="CaptchaIncorrectLabel" runat="server" 
      CssClass="incorrect"></asp:Label>
    </div>
  </fieldset>
  
  <script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
  <script type="text/javascript" src="js/jquery.validate.min.js"></script>
  <script type="text/javascript" src="js/validationRules.js"></script>
  
  </form>
</body>
</html>

The input form code is almost exactly the same as in the basic Captcha sample. The only differences are that we add a custom CssClass attribute to the Captcha code input textbox, and that we must include jQuery, the jQuery.validate plugin, and the script containing our custom validation rules.

The custom Css class value is used to ensure we can reliably identify the Captcha textbox from client-side code. Since ASP.NET controls don't neccessarily have predictable client-side identifiers (depending on the ClientIDMode setting used, whether the control is placed within a naming container, a data-bound repeater etc.), a unique Css class is used instead.

js\validationRules.js

$(document).ready(function() {

  $("#form1").validate({
    // only validate fields when the form is submitted:
    // the Captcha input must only be validated when the whole code string is
    // typed in, not after each individual character (onkeyup must be false);
    // onfocusout could be left on in more complex forms, but doesn't fit
    // this sample
    onkeyup: false,
    onfocusout: false,
    // customize client-side error display elements
    errorClass: "incorrect",
    validClass: "correct",
    errorElement: "label",
    // show the client-side error label to the left of the textbox
    errorPlacement: function(error, element) {
      offset = element.offset();
      error.insertBefore(element)
    },
    // always reload the Captcha image if remote validation failed,
    // since it will not be usable any more (a failed validation attempt
    // removes the attempted code for necessary Captcha security
    showErrors: function(errorMap, errorList) {
      this.defaultShowErrors();
      for (var i=0; i<errorList.length; i++) {
        var element = errorList[i].element;
        var message = errorList[i].message;
        // check element css class and does the error message match remote
        // validation failure
        if (element.className.match(/captchaVal/) &&
            message === this.settings.messages[element.id].remote) {
              element.Captcha.ReloadImage();
        }
      }
    }
  });

  // add validation rules by CSS class, so we don't have to know the
  // exact client id of the Captcha code textbox
  $(".captchaVal").rules('add', {
    required: true,
    remote: $(".captchaVal").get(0).Captcha.ValidationUrl,
    messages: {
      required: 'Required (client)',
      remote: 'Incorrect (client)'
    }
  });

});

To set up jQuery validation of a textbox taking Captcha code input, we do the following:

  • Add validation rules specifying it as a required field, that also needs to be validated remotely when a value is entered; define error messages shown when these validation rules are triggered
  • Disable onkeyup validation of the Captcha code, since we must validate the whole code and not the individual characters
  • Reload the Captcha image whenever remote Captcha validation fails

CAPTCHA jQuery Validation Rules and Error Messages

Since we're not using the input textbox Id for jQuery validation rules, we need to add them dynamically after the default validator has been initialized; that's why they're located after the $("#form1").validate({ call.

The expression for the remote Captcha validation Url can easily be read looking at dot-separated segments from right to left:

  • The ValidationUrl property is part of the BotDetect client-side API, and is unique for each Captcha instance.
  • The client-side BotDetect object can always be accessed through the Captcha property of the Captcha code textbox. For this custom property to be assigned, the textbox must be registered during server-side Captcha object initialization, through the UserInputClientID property (as has been done in Default.aspx.cs code below).
  • We use the standard jQuery selector to access the textbox by Css class, and the .get(0) function call to get the underlying DOM element. This is needed because BotDetect adds the custom Captcha property to the DOM element directly, and the jQuery wrapper element returned by the jQuery selector doesn't include it.

Disabling Remote CAPTCHA Validation on Individual Character Input

Since BotDetect deletes the Captcha code stored on the server after failed validation of a particular Captcha instance (as explained in the BotDetect FAQ), we must avoid validating the user input before the user finished typing in the whole Captcha code. The simplest way to achieve this is to disable onkeyup validation completely (onkeyup: false).

Reloading the CAPTCHA Image When Remote Validation Fails

Due to the above (failed validation invalidates the stored code for security purposes), we must also always reload the Captcha image when remote validation fails. Otherwise the user would be trying to correct his input based on an expired image, and couldn't pass Captcha validation at all.

We do this by customizing the jQuery.validate showErrors callback: beside the regular functionality (this.defaultShowErrors), we also check that there is a Captcha validation error (element.className.match(/captchaVal/) and that the error message indicates a remote validation failure (message === this.settings.messages[element.id]. remote). If that is case, we call the ReloadImage() function on the client-side Captcha object (accessed through the textbox element as explained above).

Default.aspx.cs

using System;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_PreRender(object sender, EventArgs e)
    {
        // initial page setup
        if (!IsPostBack)
        {
            // set control text
            ValidateCaptchaButton.Text = "Validate";
            CaptchaCorrectLabel.Text = "Successful form submission (server)";
            CaptchaIncorrectLabel.Text = "Failed form submission (server)";

            // these messages are shown only after validation
            CaptchaCorrectLabel.Visible = false;
            CaptchaIncorrectLabel.Visible = false;
        }

        // setup client-side input processing
        SampleCaptcha.UserInputClientID = CaptchaCodeTextBox.ClientID;

        if (IsPostBack)
        {
            // validate the Captcha to check we're not dealing with a 
            bot
            string code = CaptchaCodeTextBox.Text.Trim().ToUpper();
            bool isHuman = SampleCaptcha.Validate(code);
            CaptchaCodeTextBox.Text = null; // clear previous user 
            input

            if (isHuman)
            {
                CaptchaCorrectLabel.Visible = true;
                CaptchaIncorrectLabel.Visible = false;
            }
            else
            {
                CaptchaCorrectLabel.Visible = false;
                CaptchaIncorrectLabel.Visible = true;
            }
        }
    }
}

The input form code is functionally identical to the basic Captcha sample, with only the Captcha validation error and success messages changed to make it easier to follow the interaction between client-side and server-side validation.

We left the server-side Captcha validation (implemented in the basic Captcha sample) in place, ensuring that any bots or users with JavaScript disabled have their input checked. For users with JavaScript enabled, errors in Captcha code input will be shown on the client without full form postback to the server.

Web.config

<?xml version="1.0"?>
<configuration xmlns="http://schemas.microsoft.com/.
  NetConfiguration/v2.0">
  <system.web>
    <httpHandlers>
      <!-- Register the HttpHandler used for BotDetect Captcha 
      requests -->
      <add verb="GET" path="BotDetectCaptcha.ashx" 
        type="BotDetect.Web.CaptchaHandler, BotDetect"/>
    </httpHandlers>
    <!-- Register a custom SessionIDManager for BotDetect Captcha 
    requests -->
    <sessionState mode="InProc" cookieless="AutoDetect" timeout="20" 
      sessionIDManagerType="BotDetect.Web.CustomSessionIdManager, 
      BotDetect"/>
    <!-- Session state is required for BotDetect storage; you can also 
    turn if off globally and only enable for BotDetect-protected pages 
    if you prefer -->
    <pages enableSessionState="true">
      <controls>
        <!-- Register the BotDetect tag prefix for easier use in all 
        pages -->
        <add assembly="BotDetect" namespace="BotDetect.Web.UI" 
          tagPrefix="BotDetect"/>
      </controls>
    </pages>
    <compilation debug="true" targetFramework="4.0">
      <assemblies>
        <add assembly="System.Design, Version=4.0.0.0, Culture=neutral,
        PublicKeyToken=B03F5F7F11D50A3A"/>
      </assemblies>
    </compilation>
    <trace enabled="false" localOnly="true"/>
    <httpCookies httpOnlyCookies="true"/>
    <trust level="Medium" originUrl=""/>
    <authentication mode="None"/>
    <customErrors mode="RemoteOnly"></customErrors>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <handlers>
      <!-- Register the HttpHandler used for BotDetect Captcha 
      requests (IIS 7.0+) -->
      <remove name="BotDetectCaptchaHandler"/>
      <add name="BotDetectCaptchaHandler" 
        preCondition="integratedMode" verb="GET" 
        path="BotDetectCaptcha.ashx" 
        type="BotDetect.Web.CaptchaHandler, BotDetect"/>
    </handlers>
  </system.webServer>
</configuration>

There are several BotDetect-related changes in the web.config file, including Captcha HttpHandler registration, ASP.NET Session state configuration, and BotDetect tag prefix registration.

Visual Studio 2008 / .NET 3.5

By default, the .NET 3.5 C# version of the ASP.NET jQuery Validation Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v3.5\WebApp\AspNetJQueryValidationCaptchaSample\ CSharp

You can also run it from the BotDetect Start Menu:
Programs > Lanapsoft > BotDetect 3 CAPTCHA Component > ASP.NET > DotNET 3.5 Web Applications > Run

The Visual Studio 2008 / .NET 3.5 source has no essential differences from the Visual Studio 2010 / .NET 4.0 source.

Visual Studio 2005 / .NET 2.0

By default, the .NET 2.0 C# version of the ASP.NET jQuery Validation Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v2.0\WebApp\AspNetJQueryValidationCaptchaSample\ CSharp

You can also run it from the BotDetect Start Menu:
Programs > Lanapsoft > BotDetect 3 CAPTCHA Component > ASP.NET > DotNET 2.0 Web Applications > Run

The Visual Studio 2005 / .NET 2.0 source has no essential differences from the Visual Studio 2010 / .NET 4.0 source.