How to Implement Recaptcha for ASP.NET MVC

How to implement reCaptcha for ASP.NET MVC?

There are a few great examples:

  • MVC reCaptcha - making reCaptcha more MVC'ish.
  • ReCaptcha Webhelper in ASP.NET MVC 3
  • ReCaptcha Control for ASP.NET MVC from Google Code.

This has also been covered before in this Stack Overflow question.

NuGet Google reCAPTCHA V2 for MVC 4 and 5

  • NuGet Package
  • Demo And Document

Using reCAPTCHA with ASP.NET MVC

Some code here

You add the attribute like this:

[CaptchaValidator]  
[AcceptVerbs( HttpVerbs.Post )]
public ActionResult SubmitForm( Int32 id, bool captchaValid )
{
.. Do something here
}

You render the captcha in your view:

<%= Html.GenerateCaptcha() %> 

which is something like this:

public static string GenerateCaptcha( this HtmlHelper helper )  
{
var captchaControl = new Recaptcha.RecaptchaControl
{
ID = "recaptcha",
Theme = "blackglass",
PublicKey = -- Put Public Key Here --,
PrivateKey = -- Put Private Key Here --
};

var htmlWriter = new HtmlTextWriter( new StringWriter() );
captchaControl.RenderControl(htmlWriter);
return htmlWriter.InnerWriter.ToString();
}

How to implement Google reCaptcha in an MVC3 application?

I use the Google ReCaptcha and it works very well and is very simple to implement.

Note that if you are using Https be sure you have the current version of the dll (1.0.5.0 at this time)

You need to create an account on the Google Recaptcha site and get a set of public and private keys. Add the keys to your web project main web.config file:

<appSettings>
<add key="webpages:Version" value="1.0.0.0"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
<add key="ReCaptchaPrivateKey" value="put your private key value here" />
<add key="ReCaptchaPublicKey" value="put your public key value here" />
</appSettings>

Now use NuGet and install the reCAPTCHA plugin for .NET

Then, go to your web.config file inside of your VIEWS folder. Add this line:

<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="Recaptcha"/>
</namespaces>

Then, in your view that you want to show the captcha, add the using statement at the top of your file

@using Recaptcha;

then add this to your view:

<div class="editor-label">
Are you a human?
</div>
<div class="editor-field">
@Html.Raw(Html.GenerateCaptcha("captcha", "clean"))
@Html.ValidationMessage("captcha")
</div>

In your controller action you will need to modify the signature to accept the captcha results:

[HttpPost]
[RecaptchaControlMvc.CaptchaValidator]
public ActionResult ForgotPassword(CheckUsernameViewModel model, bool captchaValid, string captchaErrorMessage) {
if (!Membership.EnablePasswordReset)
throw new Exception("Password reset is not allowed\r\n");
if(ModelState.IsValid) {
if(captchaValid) {
return RedirectToAction("AnswerSecurityQuestion", new { username = model.Username });
}
ModelState.AddModelError("", captchaErrorMessage);
}
return View(model);
}

Following those steps have allowed me to implement captcha on several pages and it works smoothly. Note that the parameter names on the controller action MUST BE NAMED CORRECTLY:

bool captchaValid, string captchaErrorMessage

If you changed these parameter names you WILL get an error at runtime when your form posts back to the controller action.

How to implement reCaptcha V3 in ASP.NET

The accepted answer isn't following the Google's spec for sending the response and checking the action. Its Http requests will exhaust the number of sockets also. This is my implementation.

Browser

// Could be called from an event or another piece of code.
function FunctionToCall(term) {

// Google reCaptcha check
grecaptcha.ready(function() {
grecaptcha.execute(reCaptchaSiteKey, {action: "search"}).then(function(token) {

// You can take the response token Google returns, check it server side using
// the GoogleReCaptcha class and respond with a pass or fail. If a pass, run a block of code client side.
// { ... block of code ... }

// Or if you want to secure an endpoint that your sending request too.
// Send the response token with the request to your endpoint and check the response token server side and respond with a pass or fail.
// Use the repsonse to show a message or redirect site, etc

});
});

}

Server

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

public class GoogleReCaptcha
{
public class ReCaptchaResponse
{
public bool success { get; set; }
public double score { get; set; }
public string action { get; set; }
public DateTime challenge_ts { get; set; }
public string hostname { get; set; }
[JsonProperty("error-codes")]
public List<string> error_codes { get; set; }
}

public static async Task<(ReCaptchaResponse Response, bool HasPassed)> ReCaptchaPassed(string secretKey, string gRecaptchaToken, string expected_action)
{
try
{
// validate
if (string.IsNullOrWhiteSpace(secretKey) || string.IsNullOrWhiteSpace(gRecaptchaToken) || string.IsNullOrWhiteSpace(expected_action))
return (null, false);

// we use HttpClientFactory to avoid exhausting number of sockets available
var httpClient = HttpClientFactory.Create();

var verifyUrl = "https://www.google.com/recaptcha/api/siteverify";
var parameters = new Dictionary<string, string>
{
{"secret", secretKey},
{"response", gRecaptchaToken}
//{"remoteip", "ip" } <= this is optional
};
using (HttpContent formContent = new FormUrlEncodedContent(parameters))
{
using (var response = await httpClient.PostAsync(verifyUrl, formContent).ConfigureAwait(false))
{
// check HTTP response code
if (response.StatusCode != HttpStatusCode.OK)
return (null, false);

// get reCaptcha response
string gRecaptchaJsonresult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(gRecaptchaJsonresult))
return (null, false);

// check reCaptcha response is successful
var recaptcha_response = JsonConvert.DeserializeObject<ReCaptchaResponse>(gRecaptchaJsonresult);
if (recaptcha_response == null)
{
//Logging.Log(new Logging.LogItem { Msg = $"Google RecCaptcha response is null" }, DefaultLogValues);
return (recaptcha_response, false);
}

if (!recaptcha_response.success)
{
var errors = string.Join(",", recaptcha_response.error_codes);
//Logging.Log(new Logging.LogItem { Msg = $"Google RecCaptcha error codes:\n{errors}" }, DefaultLogValues);
return (recaptcha_response, false);
}

// check reCaptcha response action
if (recaptcha_response.action.ToUpper() != expected_action.ToUpper())
{
//Logging.Log(new Logging.LogItem { Msg = $"Google RecCaptcha action doesn't match:\nExpected action: {expected_action} Given action: {recaptcha_response.action}" }, DefaultLogValues);
return (recaptcha_response, false);
}

// response score
// anything less than 0.5 is a bot
if (recaptcha_response.score < 0.5)
return (recaptcha_response, false);
else
return (recaptcha_response, true);
}
}
}
catch (Exception ex)
{
//Logging.Log(ex, DefaultLogValues);

// default to false
return (null, false);
}
}
}

You would call it like so..

var reCaptchaTask = GoogleReCaptcha.ReCaptchaPassed(Settings.GoogleReCaptcha.secret_key, SearchReq.gRecaptchaToken, "search");

Make sure to put your keys in a settings file and not in the code.

Inside my asp.net core MVC web application how i can make the recaptcha a required field

You can determine whether clicks recaptcha before submit by triggering the data-callback method of recaptcha, and then adding a hidden control.

After clicking recaptcha,assign a value to hidden control in data-callback method, and then judge the hidden value in the form submit method to determine whether this form can be submitted.

<form method="post"> 
<div class="form-group">
<div class="col-md-2"></div>
<div class="col-md-10">
<div class="g-recaptcha" data-sitekey="@ViewData["ReCaptchaKey"]" data-callback="recaptchaCallback"></div>
<input type="hidden" value="" id="isClickCaptcha" />
<span class="text-danger" id="validationText"></span>
</div>
</div>
<input id="Submit1" type="submit" value="submit" />
</form>
<script src="https://www.google.com/recaptcha/api.js?hl=en-US"></script>
<script src="https://code.jquery.com/jquery-3.5.0.js"></script>
<script>
var recaptchaCallback = function () {
$("#isClickCaptcha").val("yes");
};

$("form").submit(function () {
if ($("#isClickCaptcha").val() == "") {
event.preventDefault();
$("#validationText").text("Please enter CAPTCHA!");
return false;
}

})
</script>

Here is the test result:
Sample Image

How to implement recaptcha in MVC 4 project?

Okay, I got it figured out. This tutorial is very helpful since the one I mentioned in my initial post did not do a good job explaining. Below are my cording codes:

ContactController:

[CaptchaValidator]
[HttpPost]
public ActionResult ContactForm(ContactModels model, bool captchaValid)
{
if (!captchaValid)
{
ModelState.AddModelError("captcha", "You did not type the verification word correctly. Please try again.");
}
else if(ModelState.IsValid)
{
MailMessage netMessage = new MailMessage();
SmtpClient mailClient = new SmtpClient();
try
{
netMessage.From = new MailAddress("contact@myComp.com");
netMessage.To.Add(new MailAddress(model.email));

netMessage.IsBodyHtml = true;
netMessage.Priority = MailPriority.High;
netMessage.Subject = "Subject: " + model.subject;
netMessage.Body = getMailBody(model.fstName, model.lstName, model.subject, model.phone, model.email, model.address, model.apartment, model.city, model.state, model.zipcode, model.country, model.comments);
mailClient.Send(netMessage);
}
catch (Exception error)
{
Response.Write("Error sending email: " + error.Message + "<br /> StackTrace: " + error.StackTrace);
}
finally
{
netMessage.Dispose();
netMessage = null;
mailClient = null;
}

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

And here's my View:

@using MvcReCaptcha.Helpers;

@{
ViewBag.Title = "Contact";
Layout = "~/Views/Shared/_FullPage.cshtml";
}

<h2>Contact Us</h2>
@using (Html.BeginForm())
{
@Html.LabelFor(model => model.fstName)
@Html.TextBoxFor(model => model.fstName)
@Html.ValidationMessageFor(model => model.fstName)

@Html.LabelFor(model => model.lstName)
@Html.TextBoxFor(model => model.lstName)
@Html.ValidationMessageFor(model => model.lstName)

@Html.LabelFor(model => model.phone)
@Html.TextBoxFor(model => model.phone)
@Html.LabelFor(model => model.email)
@Html.TextBoxFor(model => model.email)
@Html.LabelFor(model => model.address)
@Html.TextBoxFor(model => model.address)
@Html.LabelFor(model => model.apartment)
@Html.TextBoxFor(model => model.apartment)
@Html.LabelFor(model => model.city)
@Html.TextBoxFor(model => model.city)
@Html.LabelFor(model => model.state)
@Html.TextBoxFor(model => model.state)
@Html.LabelFor(model => model.zipcode)
@Html.TextBoxFor(model => model.zipcode)
@Html.LabelFor(model => model.country)
@Html.TextBoxFor(model => model.country)
@Html.LabelFor(model => model.subject)
@Html.TextBoxFor(model => model.subject)
@Html.LabelFor(model => model.comments)
@Html.TextAreaFor(model => model.comments)
@Html.Raw(Html.GenerateCaptcha())
@Html.ValidationMessage("captcha")
<br />
<button type="submit">Submit</button>
}

Noticed the parameter in the @html.ValidationMessage matches the one in the ModelStae.AddModelError. I hope this may help someone who may have the same problem as I did.

How to add captcha in ASP.NET project

Below is the full implementation from one of my latest projects - it simply follows Google's Recaptcha guidance with some additional useful code - please check the latest Google documentation for the latest updates:

Add 2 new keys in your web.config file - one for the site key and the other for the secret key - you get these values when you register your website from the Google Recaptcha portal.

  <add key="RECAPTCHA:SITE_KEY" value="Your site key that you generate from " />
<add key="RECAPTCHA:SECRET_KEY" value="Your secret key" />

Create a public static Config class to reach your web.config key values easily as follows;

public static class Config
{
private static NameValueCollection _appSettings;

private static NameValueCollection AppSettings => _appSettings ?? (_appSettings = ConfigurationManager.AppSettings);

public static string RecaptchaSiteKey => GetStringValueOrDefault("RECAPTCHA:SITE_KEY", "");

public static string RecaptchaSecretKey => GetStringValueOrDefault("RECAPTCHA:SECRET_KEY", "");

private static string GetStringValueOrDefault(string key, string defaultValue)
{
return string.IsNullOrWhiteSpace(GetStringValueFromAppSettings(key))
? defaultValue
: GetStringValueFromAppSettings(key);
}

private static string GetStringValueFromAppSettings(string key)
{
return string.IsNullOrEmpty(AppSettings[key]) ? string.Empty : AppSettings[key];
}
}

Add the g-recaptcha section to your view, i.e.

        if (ViewData.ModelState.Keys.Contains("OverallError"))
{
<div class="form__row">
@Html.ValidationMessage("OverallError", new { @class = "form__error" }).Raw()
</div>
}
<div class="form__row">
<div class="form__controls">
<div class="g-recaptcha"
data-sitekey="@YourProject.Config.RecaptchaSiteKey"
data-callback="reCaptchaResponse">
</div>
@Html.HiddenFor(m => m.RecaptchaToken)
</div>
</div>
<div class="form__row form__row--final">
<div class="form__controls">
<button type="submit" class="button button--primary" id="createAccountSubmit">Create account</button>
</div>
</div>

Script section on the same view;

@section Scripts {
<script>
$(document).ready(function () {
createAccountSubmit.disabled = true;
});

var createAccountSubmit = document.getElementById("createAccountSubmit");

function reCaptchaResponse(token) {
$('#RecaptchaToken').val(token);
createAccountSubmit.disabled = false;
}
</script>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
}

Add RecaptchaToken to your viewmodel;

 public string RecaptchaToken { get; set; }

And here is your post action and how you can validate your recapcha token and add some custom validation errors:

    if (ModelState.IsValid)
{
if (!await model.RecaptchaToken.ValidateAsRecapchaToken())
{
ModelState.AddModelError("OverallError", "CAPTCHA validation failed. Please try again.");
}

// Always clear the token as it is no longer valid. Any subsequent re-posts will need a new token.
ModelState.SetModelValue(nameof(model.RecaptchaToken), new ValueProviderResult("", "", CultureInfo.InvariantCulture));
}

StringExtension to validate the recapcha token:

 public static class StringExtensions
{
public static async Task<bool> ValidateAsRecapchaToken(this string token)
{
using (var client = new HttpClient())
{
var secret = Config.RecaptchaSecretKey;
var url = $"https://www.google.com/recaptcha/api/siteverify?secret={secret}&response={token}";
var response = await client.PostAsync(url, new StringContent(""));
var responseModel = await response.Content.ReadAsAsync<RecaptchaResponseModel>();

return responseModel.Success;
}
}
}

This is how things look like:

Sample Image



Related Topics



Leave a reply



Submit