Implement Google reCAPTCHA In ASP.NET MVC

ImpIntroduction 
 
reCAPTCHA is a free service that protects your website from spam and abuse. This blog shows how to implement reCAPTCHA version 2.0 into an ASP.NET MVC website. 
 
Google reCAPTCHA In ASP.NET MVC 

Step 1 - Get Site & Secret Key for reCAPTCHA from Google

Login to your Google account, then go to the below link.

https://www.google.com/recaptcha/admin#list

Enter your details in the register a new site section. Add a label to identify the site, choose “reCAPTCHA V2" and then add a list of domains.

Also, remember to add localhost if you intend to test it on your local machine,

Google reCAPTCHA In ASP.NET MVC
 
Click Register and you will be provided with your Site & Secret Key,

Google reCAPTCHA In ASP.NET MVC 

  • Site key - is used to display the widget in your page or code.
  • Secret key - can be used as communication between your site and Google to verify the user response whether the reCAPTCHA is valid or not.

 

Now the next step displays the reCAPTCHA widget on your website.

Step 2 - Using reCAPTCHA in our ASP.NET MVC Application

Now we create a new ASP.NET MVC empty project,

 Google reCAPTCHA In ASP.NET MVC
 
First, we add a static class to store our reCAPTCHA “Site Key & Secret Key” to use them later :
  1. public static class SiteSettings  
  2. {  
  3.     public const string GoogleRecaptchaSecretKey = "your secret key here";  
  4.     public const string GoogleRecaptchaSiteKey = "your site key here";  
  5. }  

Step 3 - We should create 2 html helpers

One to generate “reCAPTCHA” in our views that uses the above class values,

  1. public static class GoogleCaptchaHelper  
  2. {  
  3.     public static IHtmlString GoogleCaptcha(this HtmlHelper helper)  
  4.     {  
  5.         const string publicSiteKey = SiteSettings.GoogleRecaptchaSiteKey;  
  6.   
  7.         var mvcHtmlString = new TagBuilder("div")  
  8.         {  
  9.             Attributes =  
  10.             {  
  11.                 new KeyValuePair<string, string>("class""g-recaptcha"),  
  12.                 new KeyValuePair<string, string>("data-sitekey", publicSiteKey)  
  13.             }  
  14.         };  
  15.   
  16.         const string googleCaptchaScript = "<script src='https://www.google.com/recaptcha/api.js'></script>";  
  17.         var renderedCaptcha = mvcHtmlString.ToString(TagRenderMode.Normal);  
  18.   
  19.         return MvcHtmlString.Create($"{googleCaptchaScript}{renderedCaptcha}");  
  20.     }  
  21. }  

A second one to show error message if Captcha is invalid for any reason,

  1. public static class InvalidGoogleCaptchaHelper  
  2. {  
  3.     public static IHtmlString InvalidGoogleCaptchaLabel(this HtmlHelper helper, string errorText)  
  4.     {  
  5.         var invalidCaptchaObj = helper.ViewContext.Controller.TempData["InvalidCaptcha"];  
  6.   
  7.         var invalidCaptcha = invalidCaptchaObj?.ToString();  
  8.         if (string.IsNullOrWhiteSpace(invalidCaptcha)) return MvcHtmlString.Create("");  
  9.   
  10.         var buttonTag = new TagBuilder("span")  
  11.         {  
  12.             Attributes =  
  13.             {  
  14.                 new KeyValuePair<string, string>("class""text text-danger")  
  15.             },  
  16.             InnerHtml = errorText ?? invalidCaptcha  
  17.         };  
  18.   
  19.         return MvcHtmlString.Create(buttonTag.ToString(TagRenderMode.Normal));  
  20.     }  
  21. }  
Step 4
 
Now, we need to add a custom attribute to validate submitted Captcha from the form in the controller.
  1. public class ValidateGoogleCaptchaAttribute : ActionFilterAttribute  
  2. {  
  3.     public override void OnActionExecuting(ActionExecutingContext filterContext)  
  4.     {  
  5.         const string urlToPost = "https://www.google.com/recaptcha/api/siteverify";  
  6.         const string secretKey = SiteSettings.GoogleRecaptchaSecretKey;  
  7.         var captchaResponse = filterContext.HttpContext.Request.Form["g-recaptcha-response"];  
  8.   
  9.         if (string.IsNullOrWhiteSpace(captchaResponse)) AddErrorAndRedirectToGetAction(filterContext);  
  10.   
  11.         var validateResult = ValidateFromGoogle(urlToPost, secretKey, captchaResponse);  
  12.         if (!validateResult.Success) AddErrorAndRedirectToGetAction(filterContext);  
  13.   
  14.         base.OnActionExecuting(filterContext);  
  15.     }  
  16.   
  17.     private static void AddErrorAndRedirectToGetAction(ActionExecutingContext filterContext)  
  18.     {  
  19.         filterContext.Controller.TempData["InvalidCaptcha"] = "Invalid Captcha !";  
  20.         filterContext.Result = new RedirectToRouteResult(filterContext.RouteData.Values);  
  21.     }  
  22.   
  23.     private static ReCaptchaResponse ValidateFromGoogle(string urlToPost, string secretKey, string captchaResponse)  
  24.     {  
  25.         var postData = "secret=" + secretKey + "&response=" + captchaResponse;  
  26.   
  27.         var request = (HttpWebRequest)WebRequest.Create(urlToPost);  
  28.         request.Method = "POST";  
  29.         request.ContentLength = postData.Length;  
  30.         request.ContentType = "application/x-www-form-urlencoded";  
  31.   
  32.         using (var streamWriter = new StreamWriter(request.GetRequestStream()))  
  33.             streamWriter.Write(postData);  
  34.   
  35.         string result;  
  36.         using (var response = (HttpWebResponse)request.GetResponse())  
  37.         {  
  38.             using (var reader = new StreamReader(response.GetResponseStream()))  
  39.                 result = reader.ReadToEnd();  
  40.         }  
  41.   
  42.         return JsonConvert.DeserializeObject<ReCaptchaResponse>(result);  
  43.     }  
  44. }  
  45.   
  46. internal class ReCaptchaResponse  
  47. {  
  48.     [JsonProperty("success")]  
  49.     public bool Success { get; set; }  
  50.   
  51.     [JsonProperty("challenge_ts")]  
  52.     public string ValidatedDateTime { get; set; }  
  53.   
  54.     [JsonProperty("hostname")]  
  55.     public string HostName { get; set; }  
  56.   
  57.     [JsonProperty("error-codes")]  
  58.     public List<string> ErrorCodes { get; set; }  
  59. }  
Step 5
 
Now, to test our application, we are about to create a new controller with 2 actions.
  1. public class TestController : Controller  
  2. {  
  3.     [HttpGet]  
  4.     public ActionResult Create()  
  5.     {  
  6.         return View();  
  7.     }  
  8.   
  9.     [HttpPost]  
  10.     [ValidateAntiForgeryToken]  
  11.     [ValidateGoogleCaptcha]  
  12.     public ActionResult Create(string title)  
  13.     {  
  14.         // If we are here, Captcha is validated.  
  15.         return View();  
  16.     }  
  17. }  
 We validate Captcha in our POST action using [ValidateGoogleCaptcha].

Ours creates a view,

  1. @using GoogleRecaptcha.Infrastructure.HtmlHelpers  
  2.   
  3. @{  
  4.     ViewBag.Title = "Create";  
  5. }  
  6.   
  7. <h2>@ViewBag.Title</h2>  
  8. <h4>Some test form that needs reCAPTCHA !</h4>  
  9.   
  10. <hr />  
  11.   
  12. @using (Html.BeginForm("Create""Test", FormMethod.Post, new { @class = "form-horizontal" }))  
  13. {  
  14.     @Html.AntiForgeryToken()  
  15.   
  16.     <div class="form-group">  
  17.         @Html.Label("Title"new { @class = "control-label col-md-2" })  
  18.         <div class="col-md-10">  
  19.             @Html.TextBox("Title"""new { @class = "form-control" })  
  20.         </div>  
  21.     </div>  
  22.   
  23.     <div class="form-group">  
  24.         <div class="col-md-offset-2 col-md-10">  
  25.             @Html.GoogleCaptcha()  
  26.             @Html.InvalidGoogleCaptchaLabel("Captcha is not valid !")  
  27.         </div>  
  28.     </div>  
  29.   
  30.     <div class="form-group">  
  31.         <div class="col-md-offset-2 col-md-10">  
  32.             <input type="submit" value="Create" class="btn btn-primary" />  
  33.         </div>  
  34.     </div>  
  35. }  

Now that your implementation is done,, check this OUTPUT.

Google reCAPTCHA In ASP.NET MVC
 
Conclusion
 
In this blog, I explained how to implement Google ReCaptcha into our ASP.NET MVC application while generating custom Helper Class for the Google ReCaptcha.