not - recaptcha v3 mvc




How to implement reCaptcha for ASP.NET MVC? (8)

How do I implement reCaptcha in ASP.NET MVC and C#?


Here's a version that uses the JavaScriptSerializer. Thanks Ala for the basis for this code.

WebConfig App Setting - I've added the secret key to the Web.Config in my case to allow transforms between environments. It can also be easily encrypted here if required.

<add key="Google.ReCaptcha.Secret" value="123456789012345678901234567890" />

The ReCaptcha Class - A simple class to post the response parameter along with your secret to Google and validate it. The response is deserialized using the .Net JavaScriptSerializer class and from that true or false returned.

using System.Collections.Generic;
using System.Configuration;

public class ReCaptcha
{   
    public bool Success { get; set; }
    public List<string> ErrorCodes { get; set; }

    public static bool Validate(string encodedResponse)
    {
        if (string.IsNullOrEmpty(encodedResponse)) return false;

        var client = new System.Net.WebClient();
        var secret = ConfigurationManager.AppSettings["Google.ReCaptcha.Secret"];

        if (string.IsNullOrEmpty(secret)) return false;

        var googleReply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secret, encodedResponse));

        var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();

        var reCaptcha = serializer.Deserialize<ReCaptcha>(googleReply);

        return reCaptcha.Success;
    }
}

Validate The Response - Check the validity of the g-Recaptcha-Response form parameter in your Controller (or code behind for a web form) and take appropriate action.

var encodedResponse = Request.Form["g-Recaptcha-Response"];
var isCaptchaValid = ReCaptcha.Validate(encodedResponse);

if (!isCaptchaValid)
{
    // E.g. Return to view or set an error message to visible
}   

I have added reCaptcha to a project I'm currently working on. I needed it to use the AJAX API as the reCaptcha element was loaded into the page dynamically. I couldn't find any existing controls and the API is simple so I created my own.

I'll post my code here in case anyone finds it useful.

1: Add the script tag to the master page headers

<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>

2: Add your keys to the web.config

<appSettings>
    <add key="ReCaptcha.PrivateKey" value="[key here]" />
    <add key="ReCaptcha.PublicKey" value="[key here]" />
</appSettings>

3: Create the Action Attribute and Html Helper extensions

namespace [Your chosen namespace].ReCaptcha
{
    public enum Theme { Red, White, BlackGlass, Clean }

    [Serializable]
    public class InvalidKeyException : ApplicationException
    {
        public InvalidKeyException() { }
        public InvalidKeyException(string message) : base(message) { }
        public InvalidKeyException(string message, Exception inner) : base(message, inner) { }
    }

    public class ReCaptchaAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var userIP = filterContext.RequestContext.HttpContext.Request.UserHostAddress;

            var privateKey = ConfigurationManager.AppSettings.GetString("ReCaptcha.PrivateKey", "");

            if (string.IsNullOrWhiteSpace(privateKey))
                throw new InvalidKeyException("ReCaptcha.PrivateKey missing from appSettings");

            var postData = string.Format("&privatekey={0}&remoteip={1}&challenge={2}&response={3}",
                                         privateKey,
                                         userIP,
                                         filterContext.RequestContext.HttpContext.Request.Form["recaptcha_challenge_field"],
                                         filterContext.RequestContext.HttpContext.Request.Form["recaptcha_response_field"]);

            var postDataAsBytes = Encoding.UTF8.GetBytes(postData);

            // Create web request
            var request = WebRequest.Create("http://www.google.com/recaptcha/api/verify");
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = postDataAsBytes.Length;
            var dataStream = request.GetRequestStream();
            dataStream.Write(postDataAsBytes, 0, postDataAsBytes.Length);
            dataStream.Close();

            // Get the response.
            var response = request.GetResponse();

            using (dataStream = response.GetResponseStream())
            {
                using (var reader = new StreamReader(dataStream))
                {
                    var responseFromServer = reader.ReadToEnd();

                    if (!responseFromServer.StartsWith("true"))
                        ((Controller)filterContext.Controller).ModelState.AddModelError("ReCaptcha", "Captcha words typed incorrectly");
                }
            }
        }
    }

    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString GenerateCaptcha(this HtmlHelper helper, Theme theme, string callBack = null)
        {
            const string htmlInjectString = @"<div id=""recaptcha_div""></div>
<script type=""text/javascript"">
    Recaptcha.create(""{0}"", ""recaptcha_div"", {{ theme: ""{1}"" {2}}});
</script>";

            var publicKey = ConfigurationManager.AppSettings.GetString("ReCaptcha.PublicKey", "");

            if (string.IsNullOrWhiteSpace(publicKey))
                throw new InvalidKeyException("ReCaptcha.PublicKey missing from appSettings");

            if (!string.IsNullOrWhiteSpace(callBack))
                callBack = string.Concat(", callback: ", callBack);

            var html = string.Format(htmlInjectString, publicKey, theme.ToString().ToLower(), callBack);
            return MvcHtmlString.Create(html);
        }
    }
}

4: Add the captcha to your view

@using (Html.BeginForm("MyAction", "MyController"))
{
   @Html.TextBox("EmailAddress", Model.EmailAddress)
   @Html.GenerateCaptcha(Theme.White)
   <input type="submit" value="Submit" />
}

5: Add the attribute to your action

[HttpPost]
[ReCaptcha]
public ActionResult MyAction(MyModel model)
{
   if (!ModelState.IsValid) // Will have a Model Error "ReCaptcha" if the user input is incorrect
      return Json(new { capthcaInvalid = true });

   ... other stuff ...
}

6: Note you will need to reload the captcha after each post even if it was valid and another part of the form was invalid. Use Recaptcha.reload();


I've successfully implemented ReCaptcha in the following way.
note: this is in VB, but can easily be converted

1] First grab a copy of the reCaptcha library

2] Then build a custom ReCaptcha HTML Helper

    ''# fix SO code coloring issue.
    <Extension()>
    Public Function reCaptcha(ByVal htmlHelper As HtmlHelper) As MvcHtmlString
        Dim captchaControl = New Recaptcha.RecaptchaControl With {.ID = "recaptcha",
                                                                  .Theme = "clean",
                                                                  .PublicKey = "XXXXXX",
                                                                  .PrivateKey = "XXXXXX"}
        Dim htmlWriter = New HtmlTextWriter(New IO.StringWriter)
        captchaControl.RenderControl(htmlWriter)
        Return MvcHtmlString.Create(htmlWriter.InnerWriter.ToString)
    End Function

3] From here you need a re-usable server side validator

Public Class ValidateCaptchaAttribute : Inherits ActionFilterAttribute
    Private Const CHALLENGE_FIELD_KEY As String = "recaptcha_challenge_field"
    Private Const RESPONSE_FIELD_KEY As String = "recaptcha_response_field"

    Public Overrides Sub OnActionExecuting(ByVal filterContext As ActionExecutingContext)

        If IsNothing(filterContext.HttpContext.Request.Form(CHALLENGE_FIELD_KEY)) Then
            ''# this will push the result value into a parameter in our Action
            filterContext.ActionParameters("CaptchaIsValid") = True
            Return
        End If

        Dim captchaChallengeValue = filterContext.HttpContext.Request.Form(CHALLENGE_FIELD_KEY)
        Dim captchaResponseValue = filterContext.HttpContext.Request.Form(RESPONSE_FIELD_KEY)

        Dim captchaValidtor = New RecaptchaValidator() With {.PrivateKey = "xxxxx",
                                                                       .RemoteIP = filterContext.HttpContext.Request.UserHostAddress,
                                                                       .Challenge = captchaChallengeValue,
                                                                       .Response = captchaResponseValue}

        Dim recaptchaResponse = captchaValidtor.Validate()

        ''# this will push the result value into a parameter in our Action
        filterContext.ActionParameters("CaptchaIsValid") = recaptchaResponse.IsValid

        MyBase.OnActionExecuting(filterContext)
    End Sub

above this line is reusable **ONE TIME** code


below this line is how easy it is to implement reCaptcha over and over

Now that you have your re-usable code... all you need to do is add the captcha to your View.

<%: Html.reCaptcha %>

And when you post the form to your controller...

    ''# Fix SO code coloring issues
    <ValidateCaptcha()>
    <AcceptVerbs(HttpVerbs.Post)>
    Function Add(ByVal CaptchaIsValid As Boolean, ByVal [event] As Domain.Event) As ActionResult


        If Not CaptchaIsValid Then ModelState.AddModelError("recaptcha", "*")


        '#' Validate the ModelState and submit the data.
        If ModelState.IsValid Then
            ''# Post the form
        Else
            ''# Return View([event])
        End If
    End Function

The question has already been answered. But, here's some added code that will work in ASP.NET WebForms, which enables you to make a local AJAX request to the page w/ the reCaptcha control, then do server-side captcha validation. The page's web method will return true/false.

I got this code from mindfire solutions, but added the execution of JS functions in the Ajax success callback b/c Ajax is making async callbacks.

Javascript:

<script type="text/javascript">
$(function(e) {
    $("#submit").click(function() { // my button is type=button, not type=submit
        // I'm using jQuery validation and want to make sure page is valid before making Ajax request
        if ( $("#aspnetForm").valid() ) {
            validateCaptcha();  // or validateCaptchaJson() if you want to use Json
        }   // end  If ($("#aspnetForm").valid())
    }); // end $("#submit").click()
}); // end $(function(e)


function validateCaptcha() {
    // Individual string variables storing captcha values
    var challengeField = $("input#recaptcha_challenge_field").val();
    var responseField = $("input#recaptcha_response_field").val();

    // Ajax post to page web method that will do server-side captcha validation
    $.ajax({
        type: "POST",
        url: "page.aspx/ValidateCaptcha",
        data: "recaptcha_challenge_field=" + challengeField + "&amp;recaptcha_response_field=" + responseField,
        async: false
        success: function(msg) {
            if(msg.d) { // Either true or false, true indicates CAPTCHA is validated successfully.
                // this could hide your captcha widget
                $("#recaptcha_widget_div").html(" ");
                // execute some JS function upon successful captcha validation
                goodCaptcha();
            } else {
                // execute some JS function upon failed captcha validation (like throwing up a modal indicating failed attempt)
                badCaptcha();
                // don't forget to reload/reset the captcha to try again
                Recaptcha.reload();
            }
            return false;
        }
    });
}

function validateCaptchaJson() {
    // JavaScript object storing captcha values
    var captchaInfo = {
        challengeValue: Recaptcha.get_challenge(),
        responseValue: Recaptcha.get_response()
    };

    // Ajax post to page web method that will do server-side captcha validation
    $.ajax({
        type: "POST",
        url: "page.aspx/ValidateCaptcha",
        data: JSON.stringify(captchaInfo),  // requires ref to JSON (http://www.JSON.org/json2.js)
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        success: function(msg) {
            if(msg.d) { // Either true or false, true indicates CAPTCHA is validated successfully.
                // this could hide your captcha widget
                $("#recaptcha_widget_div").html(" ");
                // execute some JS function upon successful captcha validation
                goodCaptcha();
            } else {
                // execute some JS function upon failed captcha validation (like throwing up a modal indicating failed attempt)
                badCaptcha();
                // don't forget to reload/reset the captcha to try again
                Recaptcha.reload();
            }
            return false;
        }
    });
}
</script>

Page's Web Method (VB.NET):

<WebMethod()> _
Public Shared Function ValidateCaptcha(ByVal challengeValue As String, ByVal responseValue As String) As Boolean
    ' IDEA: Get Private key of the CAPTCHA from Web.config file.
    Dim captchaValidtor As New Recaptcha.RecaptchaValidator() With { _
     .PrivateKey = "your_private_key_goes_here", _
     .RemoteIP = HttpContext.Current.Request.UserHostAddress, _
     .Challenge = challengeValue, _
     .Response = responseValue _
    }

    ' Send data about captcha validation to reCAPTCHA site.
    Dim recaptchaResponse As Recaptcha.RecaptchaResponse = captchaValidtor.Validate()
    ' Get boolean value about Captcha success / failure.
    Return recaptchaResponse.IsValid
End Function


You are getting permission error because your ajax code is trying to access a script on a different site (google) as your script. From what I know, I dont think you can do cross site Ajax calls for security reasons


Validating Recaptcha 2 (No CAPTCHA reCAPTCHA) in ASP.NET's server side

After reading many resources, I ended up with writing this class to handle the validation of the new ReCaptcha :

As mentioned Here : When a reCAPTCHA is solved by end user, a new field (g-recaptcha-response) will be populated in HTML.

We need to read this value and pass it to the class below to validate it:

In C#:

In the code behind of your page :

string EncodedResponse = Request.Form["g-Recaptcha-Response"];
bool IsCaptchaValid = (ReCaptchaClass.Validate(EncodedResponse) == "true" ? true : false);

if (IsCaptchaValid) {
    //Valid Request
}

The Class:

  using Newtonsoft.Json;

    public class ReCaptchaClass
    {
        public static string Validate(string EncodedResponse)
        {
            var client = new System.Net.WebClient();

            string PrivateKey = "6LcH-v8SerfgAPlLLffghrITSL9xM7XLrz8aeory";

            var GoogleReply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", PrivateKey, EncodedResponse));

            var captchaResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<ReCaptchaClass>(GoogleReply);

            return captchaResponse.Success;
        }

        [JsonProperty("success")]
        public string Success
        {
            get { return m_Success; }
            set { m_Success = value; }
        }

        private string m_Success;
        [JsonProperty("error-codes")]
        public List<string> ErrorCodes
        {
            get { return m_ErrorCodes; }
            set { m_ErrorCodes = value; }
        }


        private List<string> m_ErrorCodes;
    }

In VB.NET:

In the code behind of your page :

Dim EncodedResponse As String = Request.Form("g-Recaptcha-Response")
    Dim IsCaptchaValid As Boolean = IIf(ReCaptchaClass.Validate(EncodedResponse) = "True", True, False)

    If IsCaptchaValid Then
        'Valid Request
    End If

The Class:

Imports Newtonsoft.Json


Public Class ReCaptchaClass
    Public Shared Function Validate(ByVal EncodedResponse As String) As String
        Dim client = New System.Net.WebClient()

        Dim PrivateKey As String = "6dsfH-v8SerfgAPlLLffghrITSL9xM7XLrz8aeory"

        Dim GoogleReply = client.DownloadString(String.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", PrivateKey, EncodedResponse))

        Dim captchaResponse = Newtonsoft.Json.JsonConvert.DeserializeObject(Of ReCaptchaClass)(GoogleReply)

        Return captchaResponse.Success
    End Function

    <JsonProperty("success")> _
    Public Property Success() As String
        Get
            Return m_Success
        End Get
        Set(value As String)
            m_Success = value
        End Set
    End Property
    Private m_Success As String

    <JsonProperty("error-codes")> _
    Public Property ErrorCodes() As List(Of String)
        Get
            Return m_ErrorCodes
        End Get
        Set(value As List(Of String))
            m_ErrorCodes = value
        End Set
    End Property

    Private m_ErrorCodes As List(Of String)

End Class