ASP.NET MVC 1.0 Application Template CAPTCHA C# Code Example

The ASP.NET MVC 1.0 application template Captcha example project shows how to use the BotDetect CAPTCHA MvcCaptcha control in ASP.NET MVC 1.0 web applications.

First Time Here?

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

Starting with the default ASP.NET MVC 1.0 Visual Studio 2008 project template, the example includes all code required to add CAPTCHA validation to the Account controller Register action.

The example remembers when the CAPTCHA is successfully solved within a single registration, and doesn't display it again if there are errors with other form values (the username, for example).

You can get a new CAPTCHA using the Register action link in the top right corner of the page, and see the explanation in the Account controller code.

Download the BotDetect ASP.NET CAPTCHA Generator archive to run this example

→ ASP.NET MVC version:

→ .NET programming language:

Within this page, the root folder of the extracted archive is referred as the <BDC-DIR>.

This example is in the <BDC-DIR>/lgcy-on-lgcy/examples/t_api-captcha-mvc1-web.security.membership/csharp/ folder; and contains the following files:

Views\Account\Register.aspx

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage" %>

<%@ Import Namespace="BotDetect.Web.Mvc" %>
<asp:Content ID="registerTitle" ContentPlaceHolderID="TitleContent" runat="server">
  Register
</asp:Content>
<asp:Content ID="BotDetectStylesheets" ContentPlaceHolderID="includes" runat="server">
  <link href="<%= BotDetect.Web.CaptchaUrls.Absolute.LayoutStyleSheetUrl %>" 
  rel="stylesheet" type="text/css" />
</asp:Content>
<asp:Content ID="registerContent" ContentPlaceHolderID="MainContent" runat="server">
  <h2>
    Create a New Account</h2>
  <p>
    Use the form below to create a new account.
  </p>
  <p>
    Passwords are required to be a minimum of
    <%=Html.Encode(ViewData["PasswordLength"])%>
    characters in length.
  </p>
  <p>
    <%= Html.ValidationSummary("Account creation was unsuccessful. Please 
    correct the errors and try again.") %>
  </p>
  <% using (Html.BeginForm("Register", "Account"))
     { %>
  <div>
    <fieldset id="RegisterFields">
      <legend>Account Information</legend>
      <p>
        <label for="username">
          Username:</label>
        <%= Html.TextBox("username") %>
        <%= Html.ValidationMessage("username") %>
      </p>
      <p>
        <label for="email">
          Email:</label>
        <%= Html.TextBox("email") %>
        <%= Html.ValidationMessage("email") %>
      </p>
      <p>
        <label for="password">
          Password:</label>
        <%= Html.Password("password") %>
        <%= Html.ValidationMessage("password") %>
      </p>
      <p>
        <label for="confirmPassword">
          Confirm password:</label>
        <%= Html.Password("confirmPassword") %>
        <%= Html.ValidationMessage("confirmPassword") %>
      </p>
      <%  MvcCaptcha registrationCaptcha = CaptchaHelper.GetRegistrationCaptcha();
          if (!registrationCaptcha.IsSolved)
          { %>
          <%= Html.Captcha(registrationCaptcha) %>
          <p>
            <label for="captchaCode"> Please retype the characters from the picture:</label>
            <%= Html.TextBox("captchaCode")%>
            <%= Html.ValidationMessage("captchaCode")%>
          </p>
        <% } %>
      <p>
        <input type="submit" value="Register" />
      </p>
    </fieldset>
  </div>
    <% } %>
</asp:Content>

BotDetect stylesheets are included in the generated page Header using the includes content placeholder defined in the Master page.

To keep View code simple, we delegate MvcCaptcha instance creation to a helper class, and use it along with Model fields to generate the required markup using straightforward HtmlHelper calls.

Since we want to avoid re-displaying the Captcha challenge to users after they solve it, we only call the markup generation code if the IsSolved property is not set by a previous successful Captcha validation.

Views\Shared\Site.Master

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!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 runat="server">
  <title>
    <asp:ContentPlaceHolder ID="TitleContent" runat="server" />
  </title>
  <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
  <asp:ContentPlaceHolder ID="includes" runat="server">
  </asp:ContentPlaceHolder>
</head>
<body>
  <div class="page">
    <div id="header">
      <div id="title">
        <h1>
          BotDetect CAPTCHA ASP.NET MVC 1.0 Example</h1>
      </div>
      <div id="logindisplay">
        <% Html.RenderPartial("LogOnUserControl"); %>
      </div>
      <div id="menucontainer">
        <ul id="menu">
          <li>
            <%= Html.ActionLink("Home", "Index", "Home")%></li>
          <li>
            <%= Html.ActionLink("About", "About", "Home")%></li>
        </ul>
      </div>
    </div>
    <div id="main">
      <asp:ContentPlaceHolder ID="MainContent" runat="server" />
      <div id="footer">
      </div>
    </div>
  </div>
</body>
</html>

To allow easy inclusion of BotDetect stylesheets into Views which require them, the includes content placeholder should be declared in the Master page header.

Controllers\AccountController.cs

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Web.UI;

using BotDetect.Web.Mvc;

namespace AspNetMvc10CaptchaExampleCSharp.Controllers
{

  [HandleError]
  public class AccountController : Controller
  {

    // This constructor is used by the MVC framework to instantiate the controller using
    // the default forms authentication and membership providers.

    public AccountController()
      : this(null, null)
    {
    }

    // This constructor is not used by the MVC framework but is instead provided for ease
    // of unit testing this type. See the comments at the end of this file for more
    // information.
    public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
    {
      FormsAuth = formsAuth ?? new FormsAuthenticationService();
      MembershipService = service ?? new AccountMembershipService();
    }

    public IFormsAuthentication FormsAuth
    {
      get;
      private set;
    }

    public IMembershipService MembershipService
    {
      get;
      private set;
    }

    public ActionResult LogOn()
    {

      return View();
    }

    [AcceptVerbs(HttpVerbs.Post)]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054: UriParametersShouldNotBeStrings",
        Justification = "Needs to take same parameter type as Controller.Redirect()")]
    public ActionResult LogOn(string userName, string password, bool rememberMe, 
    string returnUrl)
    {

      if (!ValidateLogOn(userName, password))
      {
        return View();
      }

      FormsAuth.SignIn(userName, rememberMe);
      if (!String.IsNullOrEmpty(returnUrl))
      {
        return Redirect(returnUrl);
      }
      else
      {
        return RedirectToAction("Index", "Home");
      }
    }

    public ActionResult LogOff()
    {

      FormsAuth.SignOut();

      return RedirectToAction("Index", "Home");
    }

    public ActionResult Register()
    {

      ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

      // when the View is accessed directly and not posted, we clear any
      // remembered CAPTCHA solving state. 
      // The users only have to solve the CAPTCHA once within a single 
      // registration, but if they reload the Register page later,
      // it is shown again. 
      // Otherwise, they could register an unlimited number of accounts 
      // within a single Session after solving the CAPTCHA only once.
      MvcCaptcha.ResetCaptcha("RegistrationCaptcha");

      return View();
    }


    [AcceptVerbs(HttpVerbs.Post)]
    [CaptchaValidationActionFilter("captchaCode", "RegistrationCaptcha", 
    "Your input doesn't match displayed characters.")]
    public ActionResult Register(string userName, string email, string password, 
    string confirmPassword)
    {
      ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

      if (ValidateRegistration(userName, email, password, confirmPassword))
      {
        // Attempt to register the user
        MembershipCreateStatus createStatus = MembershipService.CreateUser(
        userName, password, email);

        if (createStatus == MembershipCreateStatus.Success)
        {
          FormsAuth.SignIn(userName, false /* createPersistentCookie */);
          return RedirectToAction("Index", "Home");
        }
        else
        {
          ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus));
        }
      }

      // If we got this far, something failed, redisplay form
      return View();
    }

    [Authorize]
    public ActionResult ChangePassword()
    {

      ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

      return View();
    }

    [Authorize]
    [AcceptVerbs(HttpVerbs.Post)]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031: DoNotCatchGeneralExceptionTypes",
        Justification = "Exceptions result in password not being changed.")]
    public ActionResult ChangePassword(string currentPassword, 
    string newPassword, string confirmPassword)
    {

      ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

      if (!ValidateChangePassword(currentPassword, newPassword, confirmPassword))
      {
        return View();
      }

      try
      {
        if (MembershipService.ChangePassword(User.Identity.Name, currentPassword,
        newPassword))
        {
          return RedirectToAction("ChangePasswordSuccess");
        }
        else
        {
          ModelState.AddModelError("_FORM", 
          "The current password is incorrect or the new password is invalid.");
          return View();
        }
      }
      catch
      {
        ModelState.AddModelError("_FORM", 
        "The current password is incorrect or the new password is invalid.");
        return View();
      }
    }

    public ActionResult ChangePasswordSuccess()
    {

      return View();
    }

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
      if (filterContext.HttpContext.User.Identity is WindowsIdentity)
      {
        throw new InvalidOperationException("Windows authentication is not supported.");
      }
    }

    #region Validation Methods

    private bool ValidateChangePassword(string currentPassword, 
    string newPassword, string confirmPassword)
    {
      if (String.IsNullOrEmpty(currentPassword))
      {
        ModelState.AddModelError("currentPassword", 
        "You must specify a current password.");
      }
      if (newPassword == null || newPassword.Length < MembershipService.MinPasswordLength)
      {
        ModelState.AddModelError("newPassword",
            String.Format(CultureInfo.CurrentCulture,
                 "You must specify a new password of {0} or more characters.",
                 MembershipService.MinPasswordLength));
      }

      if (!String.Equals(newPassword, confirmPassword, StringComparison.Ordinal))
      {
        ModelState.AddModelError("_FORM", 
        "The new password and confirmation password do not match.");
      }

      return ModelState.IsValid;
    }

    private bool ValidateLogOn(string userName, string password)
    {
      if (String.IsNullOrEmpty(userName))
      {
        ModelState.AddModelError("username", "You must specify a username.");
      }
      if (String.IsNullOrEmpty(password))
      {
        ModelState.AddModelError("password", "You must specify a password.");
      }
      if (!MembershipService.ValidateUser(userName, password))
      {
        ModelState.AddModelError("_FORM", 
        "The username or password provided is incorrect.");
      }

      return ModelState.IsValid;
    }

    private bool ValidateRegistration(string userName, string email, 
    string password, string confirmPassword)
    {
      if (String.IsNullOrEmpty(userName))
      {
        ModelState.AddModelError("username", "You must specify a username.");
      }
      if (String.IsNullOrEmpty(email))
      {
        ModelState.AddModelError("email", "You must specify an email address.");
      }
      if (password == null || password.Length < MembershipService.MinPasswordLength)
      {
        ModelState.AddModelError("password",
            String.Format(CultureInfo.CurrentCulture,
                 "You must specify a password of {0} or more characters.",
                 MembershipService.MinPasswordLength));
      }
      if (!String.Equals(password, confirmPassword, StringComparison.Ordinal))
      {
        ModelState.AddModelError("_FORM", 
        "The new password and confirmation password do not match.");
      }

      return ModelState.IsValid;
    }

    private static string ErrorCodeToString(MembershipCreateStatus createStatus)
    {
      // See http://msdn.microsoft.com/en-us/library/system.web.security.membershipcreatestatus.aspx for
      // a full list of status codes.
      switch (createStatus)
      {
        case MembershipCreateStatus.DuplicateUserName:
          return "Username already exists. Please enter a different user name.";

        case MembershipCreateStatus.DuplicateEmail:
          return "A username for that e-mail address already exists. Please 
          enter a different e-mail address.";

        case MembershipCreateStatus.InvalidPassword:
          return "The password provided is invalid. Please enter a valid 
          password value.";

        case MembershipCreateStatus.InvalidEmail:
          return "The e-mail address provided is invalid. Please check the value 
          and try again.";

        case MembershipCreateStatus.InvalidAnswer:
          return "The password retrieval answer provided is invalid. Please 
          check the value and try again.";

        case MembershipCreateStatus.InvalidQuestion:
          return "The password retrieval question provided is invalid. Please 
          check the value and try again.";

        case MembershipCreateStatus.InvalidUserName:
          return "The user name provided is invalid. Please check the value and 
          try again.";

        case MembershipCreateStatus.ProviderError:
          return "The authentication provider returned an error. Please verify 
          your entry and try again. If the problem persists, please contact your 
          system administrator.";

        case MembershipCreateStatus.UserRejected:
          return "The user creation request has been canceled. Please verify 
          your entry and try again. If the problem persists, please contact your 
          system administrator.";

        default:
          return "An unknown error occurred. Please verify your entry and try 
          again. If the problem persists, please contact your system 
          administrator.";
      }
    }
    #endregion
  }

  // The FormsAuthentication type is sealed and contains static members, so it is difficult to
  // unit test code that calls its members. The interface and helper class below demonstrate
  // how to create an abstract wrapper around such a type in order to make the AccountController
  // code unit testable.

  public interface IFormsAuthentication
  {
    void SignIn(string userName, bool createPersistentCookie);
    void SignOut();
  }

  public class FormsAuthenticationService : IFormsAuthentication
  {
    public void SignIn(string userName, bool createPersistentCookie)
    {
      FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
    }
    public void SignOut()
    {
      FormsAuthentication.SignOut();
    }
  }

  public interface IMembershipService
  {
    int MinPasswordLength { get; }

    bool ValidateUser(string userName, string password);
    MembershipCreateStatus CreateUser(string userName, string password, 
    string email);
    bool ChangePassword(string userName, string oldPassword, string newPassword);
  }

  public class AccountMembershipService : IMembershipService
  {
    private MembershipProvider _provider;

    public AccountMembershipService()
      : this(null)
    {
    }

    public AccountMembershipService(MembershipProvider provider)
    {
      _provider = provider ?? Membership.Provider;
    }

    public int MinPasswordLength
    {
      get
      {
        return _provider.MinRequiredPasswordLength;
      }
    }

    public bool ValidateUser(string userName, string password)
    {
      return _provider.ValidateUser(userName, password);
    }

    public MembershipCreateStatus CreateUser(string userName, string password, 
    string email)
    {
      MembershipCreateStatus status;
      _provider.CreateUser(userName, password, email, null, null, true, null, 
      out status);
      return status;
    }

    public bool ChangePassword(string userName, string oldPassword, 
    string newPassword)
    {
      MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */);
      return currentUser.ChangePassword(oldPassword, newPassword);
    }
  }
}

To add Captcha validation to the Register action Controller code, we perform a single line of setup in the Register action when the user GETs it, and execute the [CaptchaValidationActionFilter] filter when the user POSTs it.

The filter attribute will automatically add the appropriate ModelState error if the Captcha code input doesn't match the code displayed to the user in the Captcha picture.

CaptchaHelper.cs

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

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

public class CaptchaHelper
{
  public static MvcCaptcha GetRegistrationCaptcha()
  {
    // create the control instance
    MvcCaptcha registrationCaptcha = new MvcCaptcha("RegistrationCaptcha");
    registrationCaptcha.UserInputID = "captchaCode";

    // all Captcha properties are set in this event handler
    WebFormsCaptcha.InitializedWebCaptcha +=
        new EventHandler<InitializedWebCaptchaEventArgs>(RegistrationCaptcha_InitializedWebCaptcha);

    return registrationCaptcha;
  }

  // event handler used for Captcha control property randomization
  public static void RegistrationCaptcha_InitializedWebCaptcha(object sender,
      InitializedWebCaptchaEventArgs e)
  {
    if (e.CaptchaId != "RegistrationCaptcha")
    {
      return;
    }

    Captcha registrationCaptcha = sender as Captcha;

    // fixed Captcha settings 
    registrationCaptcha.ImageSize = new System.Drawing.Size(200, 50);
    registrationCaptcha.CodeLength = 4;

    // randomized Captcha settings
    registrationCaptcha.ImageStyle = CaptchaRandomization.GetRandomImageStyle();
    registrationCaptcha.SoundStyle = CaptchaRandomization.GetRandomSoundStyle();
  }
}

Captcha instance creation and property setting is encapsulated in this simple helper class. This separation allows application Views to stay simple regardless of the amount of Captcha customization chosen.

Global.asax.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace AspNetMvc10CaptchaExampleCSharp
{
  // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
  // visit http://go.microsoft.com/?LinkId=9394801

  public class MvcApplication : System.Web.HttpApplication
  {
    public static void RegisterRoutes(RouteCollection routes)
    {
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

      // BotDetect requests must not be routed
      routes.IgnoreRoute("{*botdetect}", new { botdetect = @"(.*)
      BotDetectCaptcha\.ashx" });


      // Set the Register action as the example project default, 
      // since it includes the Captcha control and we want to show it
      routes.MapRoute(
         "Default",
         // Route name
         "{controller}/{action}/{id}",
         // URL with parameters
         new { controller = "Account", action = "Register", id = "" } 
         // Parameter defaults
      );
    }

    protected void Application_Start()
    {
      RegisterRoutes(RouteTable.Routes);
    }
  }
}

Since all BotDetect requests are handled by the BotDetect HttpHandler, ASP.NET Url Routing must be configured to ignore BotDetect requests, which can be achieved by a single line of code in Global.asax code-behind.

Web.config

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="system.web.extensions" type="System.Web.Configuration.
    SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, 
    Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <sectionGroup name="scripting" type="System.Web.Configuration.
      ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, 
      Culture=neutral, PublicKeyToken=31BF3856AD364E35">
        <section name="scriptResourceHandler" type="System.Web.Configuration.
        ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.
        5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
        requirePermission="false" allowDefinition="MachineToApplication"/>
        <sectionGroup name="webServices" type="System.Web.Configuration.
        ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0,
        Culture=neutral, PublicKeyToken=31BF3856AD364E35">
          <section name="jsonSerialization" type="System.Web.Configuration.
          ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.
          0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
          requirePermission="false" allowDefinition="Everywhere"/>
          <section name="profileService" type="System.Web.Configuration.
          ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0,
          Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
          requirePermission="false" allowDefinition="MachineToApplication"/>
          <section name="authenticationService" type="System.Web.Configuration.
          ScriptingAuthenticationServiceSection, System.Web.Extensions, 
          Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
          requirePermission="false" allowDefinition="MachineToApplication"/>
          <section name="roleService" type="System.Web.Configuration.
          ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, 
          Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
          requirePermission="false" allowDefinition="MachineToApplication"/>
        </sectionGroup>
      </sectionGroup>
    </sectionGroup>
    <!-- Register the BotDetect configuration section -->
    <section name="botDetect" requirePermission="false" 
    type="BotDetect.Configuration.BotDetectConfigurationSection, BotDetect"/>
  </configSections>
  <connectionStrings>
    <add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;
    Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User 
    Instance=true" providerName="System.Data.SqlClient"/>
  </connectionStrings>
  <system.web>
    <compilation debug="false">
      <assemblies>
        <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral,
        PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, 
        Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Data.Linq, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=B77A5C561934E089"/>
      </assemblies>
    </compilation>
    <authentication mode="Forms">
      <forms loginUrl="~/Account/LogOn" timeout="2880"/>
    </authentication>
    <membership>
      <providers>
        <clear/>
        <add name="AspNetSqlMembershipProvider" type="System.Web.Security.
        SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, 
        PublicKeyToken=b03f5f7f11d50a3a" 
        connectionStringName="ApplicationServices" 
        enablePasswordRetrieval="false" enablePasswordReset="true" 
        requiresQuestionAndAnswer="false" requiresUniqueEmail="false" 
        passwordFormat="Hashed" maxInvalidPasswordAttempts="5" 
        minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" 
        passwordAttemptWindow="10" passwordStrengthRegularExpression="" 
        applicationName="/"/>
      </providers>
    </membership>
    <profile>
      <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider" type="System.Web.Profile.
        SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, 
        PublicKeyToken=b03f5f7f11d50a3a" 
        connectionStringName="ApplicationServices" applicationName="/"/>
      </providers>
    </profile>
    <roleManager enabled="false">
      <providers>
        <clear/>
        <add connectionStringName="ApplicationServices" applicationName="/" 
        name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider, 
        System.Web, Version=2.0.0.0, Culture=neutral, 
        PublicKeyToken=b03f5f7f11d50a3a"/>
        <add applicationName="/" name="AspNetWindowsTokenRoleProvider" 
        type="System.Web.Security.WindowsTokenRoleProvider, System.Web, 
        Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
      </providers>
    </roleManager>
    <!-- make sure Session State is enabled -->
    <pages enableSessionState="true">
      <controls>
        <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.
        Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
        <add tagPrefix="asp" namespace="System.Web.UI.WebControls" 
        assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
      </controls>
      <namespaces>
        <add namespace="System.Web.Mvc"/>
        <add namespace="System.Web.Mvc.Ajax"/>
        <add namespace="System.Web.Mvc.Html"/>
        <add namespace="System.Web.Routing"/>
        <add namespace="System.Linq"/>
        <add namespace="System.Collections.Generic"/>
        <!-- add BotDetect namespaces for coding convenience -->
        <add namespace="BotDetect"/>
        <add namespace="BotDetect.Web"/>
        <add namespace="BotDetect.Web.UI"/>
        <add namespace="BotDetect.Web.Mvc"/>
      </namespaces>
    </pages>
    <!-- configure Session State for BotDetect use -->
    <sessionState mode="InProc" cookieless="AutoDetect" timeout="20" 
    sessionIDManagerType="BotDetect.Web.CustomSessionIdManager, BotDetect"/>
    <httpHandlers>
      <remove verb="*" path="*.asmx"/>
      <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.
      Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, 
      Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.
      Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.
      0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.
      ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, 
      Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
      <add verb="*" path="*.mvc" validate="false" type="System.Web.Mvc.
      MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, 
      PublicKeyToken=31BF3856AD364E35"/>
      <!-- register HttpHandler used for BotDetect Captcha requests -->
      <add verb="GET" path="BotDetectCaptcha.ashx" type="BotDetect.Web.CaptchaHandler, BotDetect"/>
    </httpHandlers>
    <httpModules>
      <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.
      Web.Extensions, Version=3.5.0.0, Culture=neutral, 
      PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, 
      System.Web.Routing, Version=3.5.0.0, Culture=neutral, 
      PublicKeyToken=31BF3856AD364E35"/>
    </httpModules>
  </system.web>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" 
      type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, 
      Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <providerOption name="CompilerVersion" value="v3.5"/>
        <providerOption name="WarnAsError" value="false"/>
      </compiler>
      <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" 
      warningLevel="4" type="Microsoft.VisualBasic.VBCodeProvider, System, 
      Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <providerOption name="CompilerVersion" value="v3.5"/>
        <providerOption name="OptionInfer" value="true"/>
        <providerOption name="WarnAsError" value="false"/>
      </compiler>
    </compilers>
  </system.codedom>
  <system.web.extensions/>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="ScriptModule"/>
      <remove name="UrlRoutingModule"/>
      <add name="ScriptModule" preCondition="managedHandler" type="System.Web.
      Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, 
      Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, 
      System.Web.Routing, Version=3.5.0.0, Culture=neutral, 
      PublicKeyToken=31BF3856AD364E35"/>
    </modules>
    <handlers>
      <remove name="WebServiceHandlerFactory-Integrated"/>
      <remove name="ScriptHandlerFactory"/>
      <remove name="ScriptHandlerFactoryAppServices"/>
      <remove name="ScriptResource"/>
      <remove name="MvcHttpHandler"/>
      <remove name="UrlRoutingHandler"/>
      <add name="ScriptHandlerFactory" verb="*" path="*.asmx" 
      preCondition="integratedMode" type="System.Web.Script.Services.
      ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, 
      Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.
      axd" preCondition="integratedMode" type="System.Web.Script.Services.
      ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, 
      Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" 
      path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, 
      System.Web.Extensions, Version=3.5.0.0, Culture=neutral, 
      PublicKeyToken=31BF3856AD364E35"/>
      <add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.
      mvc" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, 
      Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" 
      path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, 
      Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
      <!-- register HttpHandler used for BotDetect Captcha requests -->
      <remove name="BotDetectCaptchaHandler"/>
      <add name="BotDetectCaptchaHandler" preCondition="integratedMode" 
      verb="GET" path="BotDetectCaptcha.ashx" 
      type="BotDetect.Web.CaptchaHandler, BotDetect"/>
    </handlers>
  </system.webServer>
  <botDetect helpLinkEnabled="true" helpLinkMode="image"/>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" 
        publicKeyToken="31BF3856AD364E35" culture="neutral"/>
        <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

The application's web.config file includes the standard BotDetect HttpHandler and Session state configuration elements.