ASP.NET 4.5 WebForms Application Template CAPTCHA C# Code Sample (BotDetect v3.0; deprecated)

First Time Here?

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

The ASP.NET 4.5 WebForms application template Captcha sample project shows how to add BotDetect CAPTCHA protection to the registration form included in the default ASP.NET 4.5 Web Forms Application project template coming with Visual Studio.

The sample shows how to include BotDetect CAPTCHA validation in new user data validation for the sample Register form

The sample also shows how to complement server-side CAPTCHA validation with client-side Ajax CAPTCHA validation using ASP.NET 4.5 unobtrusive validation applied to all form fields.

Visual Studio 2013 / .NET 4.5.1

The Visual Studio 2013 WebForms application Captcha sample project shows how to add BotDetect CAPTCHA protection to the registration form included in the default ASP.NET 4.5.1 Web Forms Application project template coming with Visual Studio 2013 (File > New Project > Installed > Templates > Visual C# > Web > ASP.NET Web Application > Web Forms).

Since the Register form uses Account/Register.aspx.cs source code).

The sample also shows how to complement server-side CAPTCHA validation with client-side Ajax CAPTCHA validation using ASP.NET 4.5.1 unobtrusive validation applied to all form fields (Scripts/WebForms/WebUICaptchaValidation.js).

Installed Location

By default, the C# version of the ASP.NET 4.5.1 WebForms Application Template Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v4.5\WebApp\AspNetWebForms451CaptchaSample\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

Sample Code Files

Account\Register.aspx

<%@ Page Title="Register" Language="C#" MasterPageFile="~/Site.Master" 
  AutoEventWireup="true" CodeBehind="Register.aspx.cs" 
  Inherits="AspNetWebForms451CaptchaSampleCSharp.Account.Register" %>
<%@ Import Namespace="BotDetect.Web.UI" %>

<asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">
    <h2><%: Title %>.</h2>
    <p class="text-danger">
        <asp:Literal runat="server" ID="ErrorMessage" />
    </p>

    <div class="form-horizontal">
        <h4>Create a new account.</h4>
        <hr />

        <div class="col-md-6" id="form-container">

            <asp:ValidationSummary runat="server" CssClass="text-danger" />

            <div class="form-group">
                <asp:Label runat="server" AssociatedControlID="UserName" 
                  CssClass="col-md-4 control-label">User name</asp:Label>
                <div class="col-md-8">
                    <asp:TextBox runat="server" ID="UserName" CssClass="form-control"/>
                    <asp:RequiredFieldValidator runat="server" 
                      ControlToValidate="UserName" CssClass="text-danger" 
                      ErrorMessage="The user name field is required." />
                </div>
            </div>
            <div class="form-group">
                <asp:Label runat="server" AssociatedControlID="Password" 
                  CssClass="col-md-4 control-label">Password</asp:Label>
                <div class="col-md-8">
                    <asp:TextBox runat="server" ID="Password" TextMode="Password" 
                      CssClass="form-control" />
                    <asp:RequiredFieldValidator runat="server" 
                      ControlToValidate="Password" CssClass="text-danger" 
                      ErrorMessage="The password field is required." />
                </div>
            </div>
            <div class="form-group">
                <asp:Label runat="server" AssociatedControlID="ConfirmPassword" 
                  CssClass="col-md-4 control-label">Confirm password</asp:Label>
                <div class="col-md-8">
                    <asp:TextBox runat="server" ID="ConfirmPassword" 
                      TextMode="Password" CssClass="form-control" />
                    <asp:RequiredFieldValidator runat="server" 
                      ControlToValidate="ConfirmPassword" CssClass="text-danger" 
                      Display="Dynamic" ErrorMessage="The  confirm password field is 
                        required." />
                    <asp:CompareValidator runat="server" ControlToCompare="Password" 
                      ControlToValidate="ConfirmPassword" CssClass="text-danger" 
                      Display="Dynamic" ErrorMessage="The password and confirmation 
                        password do not match." />
                </div>
            </div>
            <!-- Add the controls for CAPTCHA validation to the registration form -->
            <div class="form-group">
                <div class="col-md-offset-4 col-md-8">
                    <BotDetect:Captcha ID="RegisterCaptcha" runat="server" />
                </div>
                <asp:Label runat="server" AssociatedControlID="CaptchaCode" 
                  CssClass="col-md-4 control-label">Retype code</asp:Label>
                <div class="col-md-8">
                    <asp:TextBox runat="server" ID="CaptchaCode" 
                      CssClass="form-control captchaVal" />
                    <asp:RequiredFieldValidator ID="CaptchaRequiredValidator" 
                      runat="server" ControlToValidate="CaptchaCode"
                      CssClass="text-danger" Display="Dynamic"
                      ErrorMessage="Retyping the code from the picture is required." />
                    <asp:CustomValidator runat="server" ID="CaptchaValidator" 
                      ControlToValidate="CaptchaCode"
                      CssClass="text-danger" Display="Dynamic" 
                      ErrorMessage="Incorrect code, please try again." 
                      OnServerValidate="CaptchaValidator_ServerValidate" 
                      ClientValidationFunction="ValidateCaptcha" />
                </div>
            </div>
            <div class="form-group" id="form-submit">
                <div class="col-md-offset-4 col-md-8">
                    <asp:Button runat="server" OnClick="CreateUser_Click" 
                      Text="Register" CssClass="btn btn-default" />
                </div>
            </div>
        </div>
    </div>
</asp:Content>

To show a Captcha test on the registration form, we add a <BotDetect:Captcha> control and a textbox in which the visitor will type the displayed Captcha code. We also tag this textbox with a special CSS class (captchaVal), so we can attach client-side jQuery validation to it dynamically without knowing its exact ID.

To make the Captcha code a required form field, we add a <asp:RequiredFieldValidator>. For proper Captcha validation comparing the user input to the Captcha code stored on the server during image generation, we add a <asp:CustomValidator> associated with the textbox and set its OnServerValidate property to point to a validation function in form code-behind. To allow unobtrusive Ajax Captcha validation, we set its ClientValidationFunction to point at the custom Ajax Captcha validation script we will define in client-side code.

Account\Register.aspx.cs

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using System;
using System.Linq;
using System.Web;
using System.Web.UI;
using AspNetWebForms451CaptchaSampleCSharp.Models;

using BotDetect;
using BotDetect.Web;
using BotDetect.Web.UI;

namespace AspNetWebForms451CaptchaSampleCSharp.Account
{
    public partial class Register : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // setup client-side input processing
            RegisterCaptcha.UserInputClientID = CaptchaCode.ClientID;
        }

        protected void CreateUser_Click(object sender, EventArgs e)
        { 
            if (this.IsValid)
            {
                var manager = new UserManager();
                var user = new ApplicationUser() { UserName = UserName.Text };
                IdentityResult result = manager.Create(user, Password.Text);
                if (result.Succeeded)
                {
                    IdentityHelper.SignIn(manager, user, isPersistent: false);
                    IdentityHelper.RedirectToReturnUrl(
                      Request.QueryString["ReturnUrl"], Response);
                }
                else 
                {
                    ErrorMessage.Text = result.Errors.FirstOrDefault();
                }

                Response.Redirect("~/About");
            }
            
        }

        protected void CaptchaValidator_ServerValidate(object source,
           System.Web.UI.WebControls.ServerValidateEventArgs args)
        { 
            // validate the Captcha to check we're not dealing with a bot
            args.IsValid = RegisterCaptcha.Validate(args.Value.Trim().ToUpper());

            CaptchaCode.Text = null; // clear previous user input
        }
    }
}

In the Page_Load event handler, we set the UserInputClientID property of the Captcha control required for client-side Captcha object initialization. This is needed because the custom client-side Ajax Captcha validation code will require that object instance to work.

In the CaptchaValidator_ServerValidate function registered to handle server-side validation of the <asp:CustomValidator>, we use the Captcha control's Validate() method to check user input. We also clear the Captcha code textbox after validation, since each Captcha code can only be validated once for security reasons.

Scripts\WebForms\WebUICaptchaValidation.js

// CAPTCHA client-side validation function
function ValidateCaptcha(source, arguments) {
    var captcha = $(".captchaVal").get(0).Captcha;
    var validationUrl = captcha.ValidationUrl + "&i=" + arguments.Value;
    $.getJSON(validationUrl, function (data) {
        // WebUIValidation.js status update
        source.isvalid = data;
        ValidatorUpdateDisplay(source);
        ValidatorUpdateIsValid();

        // reload the Captcha image if validation failed
        if (!data) { captcha.ReloadImage(); }
    });
}

In this file, we extend the unobtrusive client-side WebForms validation with a simple Captcha validation function.

To access the client-side Captcha object, we read it from the Captcha property of the textbox marked with the captchaVal CSS class. We can then get the ValidationUrl pointing to the BotDetect HttpHandler endpoint used for Ajax Captcha validation, append the user input as an additional &i= querystring parameter, and start the Ajax validation call using the jQuery $.getJSON() function.

When the Ajax call returns the JSON-encoded Captcha validation result, we forward it to unobtrusive client-side WebForms validation code that updates ASP.NET validator status and display. We also reload the Captcha image immediately if validation failed, since the code it is showing will have been invalidated in that case, and can't be used anymore.

App_Start\BundleConfig.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Optimization;
using System.Web.UI;

namespace AspNetWebForms451CaptchaSampleCSharp
{
    public class BundleConfig
    {
        // For more information on Bundling, visit http://go.microsoft.
        com/fwlink/?LinkID=303951
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bundles/WebFormsJs").Include(
                            "~/Scripts/WebForms/WebForms.js",
                            "~/Scripts/WebForms/WebUIValidation.js",
                            "~/Scripts/WebForms/WebUICaptchaValidation.js",
                            "~/Scripts/WebForms/MenuStandards.js",
                            "~/Scripts/WebForms/Focus.js",
                            "~/Scripts/WebForms/GridView.js",
                            "~/Scripts/WebForms/DetailsView.js",
                            "~/Scripts/WebForms/TreeView.js",
                            "~/Scripts/WebForms/WebParts.js"));

            // Order is very important for these files to work, they have explicit 
            // dependencies
            bundles.Add(new ScriptBundle("~/bundles/MsAjaxJs").Include(
                    "~/Scripts/WebForms/MsAjax/MicrosoftAjax.js",
                    "~/Scripts/WebForms/MsAjax/MicrosoftAjaxApplicationServices.js",
                    "~/Scripts/WebForms/MsAjax/MicrosoftAjaxTimer.js",
                    "~/Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js"));

            // Use the Development version of Modernizr to develop with and learn from.
            // Then, when you’re  ready for production, use the build tool 
            // at http://modernizr.com to pick only the tests you need
            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                            "~/Scripts/modernizr-*"));

            ScriptManager.ScriptResourceMapping.AddDefinition(
                "respond",
                new ScriptResourceDefinition
                {
                    Path = "~/Scripts/respond.min.js",
                    DebugPath = "~/Scripts/respond.js",
                });
        }
    }
}

To make ASP.NET WebForms unobtrusive validation load the Captcha Ajax validation function, we have to add it to the /bundles/WebFormsJs script bundle.

Web.config

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.
    microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" 
      type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, 
        EntityFramework, Version=6.0.0.0, Culture=neutral, 
        PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    <section name="botDetect" requirePermission="false" 
      type="BotDetect.Configuration.BotDetectConfigurationSection, BotDetect"/>
  </configSections>
  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;
      AttachDbFilename=|DataDirectory|\aspnet-AspNetWebForms451CaptchaSampleCSharp-
      20140226081620.mdf;Initial Catalog=aspnet-AspNetWebForms451CaptchaSampleCSharp-
      20140226081620;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.web>
    <authentication mode="None" />
    <compilation debug="true" targetFramework="4.5.1" />
    <httpRuntime targetFramework="4.5.1" />
    <pages>
      <namespaces>
        <add namespace="System.Web.Optimization" />
        <add namespace="Microsoft.AspNet.Identity" />
      </namespaces>
      <controls>
        <add assembly="Microsoft.AspNet.Web.Optimization.WebForms" 
        namespace="Microsoft.AspNet.Web.Optimization.WebForms" tagPrefix="webopt" />
        <!-- Register the BotDetect tag prefix for easier use in all pages -->
        <add assembly="BotDetect" namespace="BotDetect.Web.UI" tagPrefix="BotDetect"/>
      </controls>
    </pages>
    <membership>
      <providers>
        <!--
        ASP.NET Membership is disabled in this template. Please visit the following 
        link http://go.microsoft.com/fwlink/?LinkId=301889 to learn about the ASP.NET 
        Membership support in this template
        -->
        <clear />
      </providers>
    </membership>
    <profile>
      <providers>
        <!--
        ASP.NET Membership Profile is disabled in this template. Please visit the 
        following link http://go.microsoft.com/fwlink/?LinkId=301889 to learn about 
        the ASP.NET Membership support in this template
        -->
        <clear />
      </providers>
    </profile>
    <roleManager>
      <!--
          ASP.NET Membership Role is disabled in this template. Please visit the 
          following link http://go.microsoft.com/fwlink/?LinkId=301889 to learn about 
          the ASP.NET Membership support in this template
        -->
      <providers>
        <clear />
      </providers>
    </roleManager>
    <!--
            If you are deploying to a cloud environment that has multiple web server 
            instances,
            you should change session state mode from "InProc" to "Custom". In 
            addition,
            change the connection string named "DefaultConnection" to connect to an 
            instance
            of SQL Server (including SQL Azure and SQL  Compact) instead of to SQL 
            Server Express.
      -->
    <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" customProvider="DefaultSessionProvider" 
        sessionIDManagerType="BotDetect.Web.CustomSessionIdManager, BotDetect">
      <providers>
        <add name="DefaultSessionProvider" 
          type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, 
            Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
        connectionStringName="DefaultConnection" />
      </providers>
    </sessionState>
  </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>
    <modules>
      <remove name="FormsAuthenticationModule" />
    </modules>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="WebGrease" culture="neutral" 
          publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="EntityFramework" culture="neutral" 
          publicKeyToken="b77a5c561934e089" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <entityFramework>
    <defaultConnectionFactory 
      type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, 
        EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" 
        type="System.Data.Entity.SqlServer.SqlProviderServices, 
          EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
  <botDetect>
    <captchaImage>
      <helpLink enabled="true" mode="image" />
    </captchaImage>
  </botDetect>
</configuration>

To allow the application to use BotDetect Captcha protection, we must enable and configure sessionState (required for BotDetect), and register the BotDetect HttpHandler in both <system.web><httpHandlers> and <system.webServer><handlers> configuration sections.

Visual Studio 2012 / .NET 4.5

The Visual Studio 2012 WebForms application template Captcha sample project shows how to add BotDetect CAPTCHA protection to the registration form included in the default ASP.NET 4.5 Web Forms Application project template coming with Visual Studio 2012 (File > New Project > Installed > Templates > Visual C# > Web > ASP.NET Web Forms Application).

Since the Register form uses a CreateUserWizard, the sample shows how to include BotDetect CAPTCHA validation in new user data validation (see Account/Register.aspx and Account/Register.aspx.cs source code).

The sample also shows how to complement server-side CAPTCHA validation with client-side Ajax CAPTCHA validation using ASP.NET 4.5 unobtrusive validation applied to all form fields (Scripts/WebForms/WebUICaptchaValidation.js).

Installed Location

By default, the C# version of the ASP.NET 4.5 WebForms Application Template Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v4.5\WebApp\AspNetWebForms45CaptchaSample\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

Sample Code Files

Account\Register.aspx

<%@ Page Title="Register" Language="C#" MasterPageFile="~/Site.Master" 
  AutoEventWireup="true" CodeBehind="Register.aspx.cs" 
  Inherits="AspNetWebForms45CaptchaSampleCSharp.Account.Register" %>

<asp:Content runat="server" ID="BodyContent" 
  ContentPlaceHolderID="MainContent">
  
  <hgroup class="title">
    <h1><%: Title %>.</h1>
    <h2>Use the form below to create a new account.</h2>
  </hgroup>

  <div class="register">

    <asp:CreateUserWizard runat="server" ID="RegisterUser" 
      ViewStateMode="Disabled" OnCreatedUser="RegisterUser_CreatedUser">
      
      <LayoutTemplate>
        <asp:PlaceHolder runat="server" ID="wizardStepPlaceholder" />
        <asp:PlaceHolder runat="server" ID="navigationPlaceholder" />
      </LayoutTemplate>
      
      <WizardSteps>
        <asp:CreateUserWizardStep runat="server" ID="RegisterUserWizardStep">
          
          <ContentTemplate>
            <p class="message-info">
              Passwords are required to be a minimum of <%: Membership.
              MinRequiredPasswordLength %> characters in length.
            </p>

            <p class="validation-summary-errors">
              <asp:Literal runat="server" ID="ErrorMessage" />
            </p>

            <fieldset>
              <legend>Registration Form</legend>
              <ol>
                <li>
                  <asp:Label runat="server" AssociatedControlID="UserName">
                    User name</asp:Label>
                  <asp:TextBox runat="server" ID="UserName" />
                  <asp:RequiredFieldValidator runat="server" 
                    ControlToValidate="UserName"
                    CssClass="field-validation-error" Display="Dynamic" 
                    ErrorMessage="The user name field is required." />
                </li>
                <li>
                  <asp:Label runat="server" AssociatedControlID="Email">Email 
                    address</asp:Label>
                  <asp:TextBox runat="server" ID="Email" TextMode="Email" />
                  <asp:RequiredFieldValidator runat="server" 
                    ControlToValidate="Email"
                    CssClass="field-validation-error" Display="Dynamic" 
                    ErrorMessage="The email address field is required." />
                </li>
                <li>
                  <asp:Label runat="server" AssociatedControlID="Password">
                    Password</asp:Label>
                  <asp:TextBox runat="server" ID="Password" 
                    TextMode="Password" />
                  <asp:RequiredFieldValidator runat="server" 
                    ControlToValidate="Password"
                    CssClass="field-validation-error" Display="Dynamic" 
                    ErrorMessage="The password field is required." />
                </li>
                <li>
                  <asp:Label runat="server" 
                    AssociatedControlID="ConfirmPassword">Confirm password
                  </asp:Label>
                  <asp:TextBox runat="server" ID="ConfirmPassword" 
                    TextMode="Password" />
                  <asp:RequiredFieldValidator runat="server" 
                    ControlToValidate="ConfirmPassword"
                    CssClass="field-validation-error" Display="Dynamic" 
                    ErrorMessage="The confirm password field is required." />
                  <asp:CompareValidator runat="server" 
                    ControlToCompare="Password" 
                    ControlToValidate="ConfirmPassword"
                    CssClass="field-validation-error" Display="Dynamic" 
                    ErrorMessage="The password and confirmation password do 
                      not match." />
                </li>
                <!-- Add the controls for CAPTCHA validation to the 
                  registration form -->
                <li>
                  <BotDetect:Captcha ID="RegisterCaptcha"  runat="server" />
                  <asp:Label ID="CaptchaLabel" runat="server" 
                    AssociatedControlID="CaptchaCode">Retype code</asp:Label>
                  <asp:TextBox runat="server" ID="CaptchaCode" 
                    CssClass="captchaVal" />
                  <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
                    runat="server" ControlToValidate="CaptchaCode"
                    CssClass="field-validation-error" Display="Dynamic" 
                    ErrorMessage="Retyping the code from the picture is 
                      required." />
                  <asp:CustomValidator runat="server" ID="CaptchaValidator" 
                    ControlToValidate="CaptchaCode"
                    CssClass="field-validation-error" Display="Dynamic" 
                    ErrorMessage="Incorrect code, please try again." 
                    OnServerValidate="CaptchaValidator_ServerValidate" 
                    ClientValidationFunction="ValidateCaptcha" />
                </li>
              </ol>
              <asp:Button runat="server" CommandName="MoveNext" 
                Text="Register" />
            </fieldset>
          </ContentTemplate>
          <CustomNavigationTemplate />
        </asp:CreateUserWizardStep>
      </WizardSteps>
    </asp:CreateUserWizard>

</asp:Content>

To show a Captcha test on the registration form, we add a <BotDetect:Captcha> control and a textbox in which the visitor will type the displayed Captcha code. We also tag this textbox with a special CSS class (captchaVal), so we can attach client-side jQuery validation to it dynamically without knowing its exact ID.

To make the Captcha code a required form field, we add a <asp:RequiredFieldValidator>. For proper Captcha validation comparing the user input to the Captcha code stored on the server during image generation, we add a <asp:CustomValidator> associated with the textbox and set its OnServerValidate property to point to a validation function in form code-behind. To allow unobtrusive Ajax Captcha validation, we set its ClientValidationFunction to point at the custom Ajax Captcha validation script we will define in client-side code.

Account\Register.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.AspNet.Membership.OpenAuth;

using BotDetect;
using BotDetect.Web;
using BotDetect.Web.UI;

namespace AspNetWebForms45CaptchaSampleCSharp.Account
{
    public partial class Register : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            RegisterUser.ContinueDestinationPageUrl = 
              Request.QueryString["ReturnUrl"];

            // get control references; have to use FindControl() calls since 
            // controls are inside the CreateUserWizard template
            Captcha RegisterCaptcha = RegisterUser.CreateUserStep.
              ContentTemplateContainer.FindControl("RegisterCaptcha") 
                as Captcha;
            
            TextBox CaptchaCodeTextBox = RegisterUser.CreateUserStep.
              ContentTemplateContainer.FindControl("CaptchaCode") as TextBox;

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

        // before sending user data to the Membership provider to create a new 
        // account, we first check the CAPTCHA input to ensure bots can't 
        // access the Membership provider at all
        protected void CaptchaValidator_ServerValidate(object source, 
          ServerValidateEventArgs args)
        {
            // get control references; have to use FindControl() calls since 
            // controls are inside the CreateUserWizard template
            Captcha RegisterCaptcha = RegisterUser.CreateUserStep.
              ContentTemplateContainer.FindControl("RegisterCaptcha") 
                as Captcha;
                
            TextBox CaptchaCodeTextBox = RegisterUser.CreateUserStep.
              ContentTemplateContainer.FindControl("CaptchaCode") as TextBox;

            // validate the Captcha to check we're not dealing with a bot
            args.IsValid = 
              RegisterCaptcha.Validate(args.Value.Trim().ToUpper());

            CaptchaCodeTextBox.Text = null; // clear previous user input
        }

        protected void RegisterUser_CreatedUser(object sender, EventArgs e)
        {
            FormsAuthentication.SetAuthCookie(RegisterUser.UserName, 
              createPersistentCookie: false);

            string continueUrl = RegisterUser.ContinueDestinationPageUrl;
            if (!OpenAuth.IsLocalUrl(continueUrl))
            {
                continueUrl = "~/";
            }
            Response.Redirect(continueUrl);
        }
    }
}

To access the RegisterCaptcha and CaptchaCode controls in registration form code-behind, we can't use their IDs directly because they are within a ContentTemplate. Instead, we have to use RegisterUser.CreateUserStep.ContentTemplateContainer.FindControl() calls.

In the Page_Load event handler, we set the UserInputClientID property of the Captcha control required for client-side Captcha object initialization. This is needed because the custom client-side Ajax Captcha validation code will require that object instance to work.

In the CaptchaValidator_ServerValidate function registered to handle server-side validation of the <asp:CustomValidator>, we use the Captcha control's Validate() method to check user input. We also clear the Captcha code textbox after validation, since each Captcha code can only be validated once for security reasons.

Scripts\WebForms\WebUICaptchaValidation.js

// CAPTCHA client-side validation function
function ValidateCaptcha(source, arguments) {
    var captcha = $(".captchaVal").get(0).Captcha;
    var validationUrl = captcha.ValidationUrl + "&i=" + arguments.Value;
    $.getJSON(validationUrl, function (data) {
        // WebUIValidation.js status update
        source.isvalid = data;
        ValidatorUpdateDisplay(source);
        ValidatorUpdateIsValid();

        // reload the Captcha image if validation failed
        if (!data) { captcha.ReloadImage(); }
    });
}

In this file, we extend the unobtrusive client-side WebForms validation with a simple Captcha validation function.

To access the client-side Captcha object, we read it from the Captcha property of the textbox marked with the captchaVal CSS class. We can then get the ValidationUrl pointing to the BotDetect HttpHandler endpoint used for Ajax Captcha validation, append the user input as an additional &i= querystring parameter, and start the Ajax validation call using the jQuery $.getJSON() function.

When the Ajax call returns the JSON-encoded Captcha validation result, we forward it to unobtrusive client-side WebForms validation code that updates ASP.NET validator status and display. We also reload the Captcha image immediately if validation failed, since the code it is showing will have been invalidated in that case, and can't be used anymore.

App_Start\BundleConfig.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Optimization;

namespace AspNetWebForms45CaptchaSampleCSharp
{
    public class BundleConfig
    {
        // For more information on Bundling, visit 
        // http://go.microsoft.com/fwlink/?LinkId=254726
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bundles/WebFormsJs").Include(
                  "~/Scripts/WebForms/WebForms.js",
                  "~/Scripts/WebForms/WebUIValidation.js",
                  "~/Scripts/WebForms/WebUICaptchaValidation.js",
                  "~/Scripts/WebForms/MenuStandards.js",
                  "~/Scripts/WebForms/Focus.js",
                  "~/Scripts/WebForms/GridView.js",
                  "~/Scripts/WebForms/DetailsView.js",
                  "~/Scripts/WebForms/TreeView.js",
                  "~/Scripts/WebForms/WebParts.js"));

            bundles.Add(new ScriptBundle("~/bundles/MsAjaxJs").Include(
                "~/Scripts/WebForms/MsAjax/MicrosoftAjax.js",
                "~/Scripts/WebForms/MsAjax/MicrosoftAjaxApplicationServices.js",
                "~/Scripts/WebForms/MsAjax/MicrosoftAjaxTimer.js",
                "~/Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js"));

            // Use the Development version of Modernizr to develop with and 
            // learn from. Then, when you're
            // ready for production, use the build tool at 
            // http://modernizr.com to pick only the tests you need
            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                "~/Scripts/modernizr-*"));
        }
    }
}

To make ASP.NET WebForms unobtrusive validation load the Captcha Ajax validation function, we have to add it to the /bundles/WebFormsJs script bundle.

Web.config

<?xml version="1.0" encoding="utf-8"?>

<!--
  For more information on how to configure your ASP.NET application, please 
  visit http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <configSections>
    <section name="botDetect" requirePermission="false" 
      type="BotDetect.Configuration.BotDetectConfigurationSection, BotDetect"/>
  </configSections>
  <system.web>
    <compilation debug="false" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
    <httpHandlers>
      <!-- Register the HttpHandler used for BotDetect Captcha requests -->
      <add verb="GET" path="BotDetectCaptcha.ashx" 
        type="BotDetect.Web.CaptchaHandler, BotDetect"/>
    </httpHandlers>
    <pages>
      <namespaces>
        <add namespace="System.Web.Optimization"/>
      </namespaces>
      <controls>
        <add assembly="Microsoft.AspNet.Web.Optimization.WebForms" 
          namespace="Microsoft.AspNet.Web.Optimization.WebForms" 
          tagPrefix="webopt"/>
        <!-- Register the BotDetect tag prefix for easier use in all pages -->
        <add assembly="BotDetect" namespace="BotDetect.Web.UI" 
          tagPrefix="BotDetect"/>
      </controls>
    </pages>
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login.aspx" timeout="2880"/>
    </authentication>
    <profile defaultProvider="DefaultProfileProvider">
      <providers>
        <add name="DefaultProfileProvider" type="System.Web.Providers.
            DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, 
            Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
          connectionStringName="DefaultConnection" applicationName="/"/>
      </providers>
    </profile>
    <membership defaultProvider="MockMembershipProvider">
      <providers>
        <add name="MockMembershipProvider" applicationName="/" 
          type="AspNetWebForms45CaptchaSampleCSharp.MockMembershipProvider, 
            AspNetWebForms45CaptchaSampleCSharp"/>
      </providers>
    </membership>
    <roleManager defaultProvider="DefaultRoleProvider">
      <providers>
        <add name="DefaultRoleProvider" type="System.Web.Providers.
            DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, 
            Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
          connectionStringName="DefaultConnection" applicationName="/"/>
      </providers>
    </roleManager>
    <!-- Register a custom SessionIDManager for BotDetect Captcha requests -->
    <sessionState mode="InProc" customProvider="DefaultSessionProvider" 
      cookieless="AutoDetect" timeout="20" 
      sessionIDManagerType="BotDetect.Web.CustomSessionIdManager, BotDetect">
      <providers>
        <add name="DefaultSessionProvider" type="System.Web.Providers.
            DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, 
            Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
          connectionStringName="DefaultConnection"/>
      </providers>
    </sessionState>
  </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>
  <botDetect>
    <captchaImage>
      <helpLink enabled="true" mode="image" />
    </captchaImage>
  </botDetect>
</configuration>

To allow the application to use BotDetect Captcha protection, we must enable and configure sessionState (required for BotDetect), and register the BotDetect HttpHandler in both <system.web><httpHandlers> and <system.webServer><handlers> configuration sections.


Please Note

The information on this page is out of date and applies to a deprecated version of BotDetect™ CAPTCHA (v3.0).

An up-to-date equivalent page for the latest BotDetect Captcha release (v4) is BotDetect v4 Captcha documentation index.

General information about the major improvements in the current BotDetect release can be found at the What's New in BotDetect v4.0 page.