Upgrading ReCaptcha to the "I'm not a robot" version
I'm picking up an issue where we are looking to upgrade to the latest version of ReCaptch (the one that gives you just a simulated check box to click on to say "I'm not a robot").
We've modified FieldType.Recaptcha.cshtml as follows (basically removing the default and adding in the code to render the ReCaptcha):
@using Umbraco.Forms.Core; @model Umbraco.Forms.Mvc.Models.FieldViewModel @{ var siteKey = Configuration.GetSetting("RecaptchaPublicKey"); if (!string.IsNullOrEmpty(siteKey)) { var theme = "clean"; var fieldSettingViewModel = Model.AdditionalSettings.FirstOrDefault(x => x.Key == "Theme"); if (fieldSettingViewModel != null) { theme = fieldSettingViewModel.Value; } <script src="https://www.google.com/recaptcha/api.js" async defer></script> <div class="g-recaptcha" data-sitekey="@siteKey" data-theme="@theme"></div> } } @*@ReCaptchaHelper.ReCaptcha(theme)*@
Seems that it's not as simple as that though, as if I make the field Mandatory I always get the validation error message (whether or not I've said I'm not a robot); and if I don't make it Mandatory I don't get blocked even if I don't complete the ReCaptcha field.
Initially just wondering if this is even feasible without some change to Contour core and how it handles the valiation of the ReCaptcha field?
Seems that it doesn't work out of the box, but with some custom validation I've managed to get it working. For background this blog post from Tim and this Code Project article was useful in setting it up.
As noted above I've amended the default form field rendering - in FieldTyle.Recaptcha.cshtml - as follows:
@using Umbraco.Forms.Core;
@model Umbraco.Forms.Mvc.Models.FieldViewModel
@{
var siteKey = Configuration.GetSetting("RecaptchaPublicKey");
if (!string.IsNullOrEmpty(siteKey))
{
var theme = "clean";
var fieldSettingViewModel = Model.AdditionalSettings.FirstOrDefault(x => x.Key == "Theme");
if (fieldSettingViewModel != null)
{
theme = fieldSettingViewModel.Value;
} } }
And then I've wired up custom validation on an application event like this:
public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
FormRenderController.FormValidate += ContourEvents.FormValidate;
}
public static class ContourEvents
{
public static void FormValidate(object sender, FormViewEventArgs e)
{
var reCaptchaField = e.Form.AllFields
.SingleOrDefault(f => f.FieldType.Name == "Recaptcha");
if (reCaptchaField != null)
{
var httpContext = HttpContext.Current;
var secretKey = Configuration.GetSetting("RecaptchaPrivateKey");
var reCaptchaResponse = httpContext.Request["g-recaptcha-response"];
var url = string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secretKey, reCaptchaResponse);
using (var client = new WebClient() {
var response = client.DownloadString(url);
var captchaResponse = JsonConvert.DeserializeObject(response);
if (!captchaResponse.Success)
{
var controller = sender as Controller;
controller.ModelState.AddModelError(reCaptchaField.Id.ToString(), "Validation not accepted");
} }
}
}
}
This looks to work as expected. If you ignore the CAPTCHA field, the response in the form is empty and so the call to Google comes back unsuccessful. If you successfully prove you aren't a robot, it'll come back with a success response.
Sorry if this is a silly question but where do you go about putting the custom validation, do you have to create a separate class project and reference it or is it just placed in the App_Code folder, can you provide a full class file structure with references as well please if possible?
Should work either way Graham. I tend to have a separate class library but it should work in App_Code just the same. Having a brain fade about exactly what project I did this in I'm afraid so can't put my hands on it now. But think you'll need a reference to Umbraco.Forms.Mvc.
Thanks Andy, if you do happen to locate the project that you did this in, I would very much welcome if you could send me the code including the customised version FieldTyle.Recaptcha.cshtml.
Have found it, it's what I have above. The only bit you're missing maybe is the using statements - you'll likely need some of:
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Forms.Core;
using Umbraco.Forms.Mvc;
using Umbraco.Forms.Mvc.Controllers;
using Umbraco.Web;
using Newtonsoft.Json;
Likely not all as I have a bit more going on in my class.
Based on Andy's excellent post, I have made a few updates/enhancements. Code files copied here for anyone who is looking for "plug-n-play".
UmbracoFormsEvents.cs
(Compiled with other custom code, or added to App_Code directory)
namespace MYSITE.Events
{
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json.Linq;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Forms.Mvc;
using Umbraco.Forms.Web.Controllers;
public class UmbracoFormsEvents : ApplicationEventHandler
{
//Requires Web.config app keys:
// <add key="RecaptchaSiteKey" value="XXXX" />
// <add key="RecaptchaSecretKey" value="XXXX" />
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
UmbracoFormsController.FormValidate += this.FormValidate;
//LogHelper.Info<UmbracoFormsEvents>("ReCaptcha FormValidate Event added to Umbraco Forms.");
}
private void FormValidate(object sender, FormValidationEventArgs e)
{
//LogHelper.Info<UmbracoFormsEvents>("FormValidate with ReCaptcha Running...");
var matchingFields = e.Form.AllFields.Where(f => f.FieldType.Name.Contains("Recaptcha"));
if (matchingFields.Any())
{
var reCaptchaField = matchingFields.FirstOrDefault();
var httpContext = HttpContext.Current;
var secretKey = System.Configuration.ConfigurationManager.AppSettings["RecaptchaSecretKey"];
if (secretKey == "")
{
LogHelper.Warn<UmbracoFormsEvents>("ERROR: ReCaptcha v.2 is missing the Secret Key - Please update the web.config to include 'key=\"RecaptchaSecretKey\"'");
}
else
{
var reCaptchaResponse = httpContext.Request["g-recaptcha-response"];
var url = string.Format(
"https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}",
secretKey,
reCaptchaResponse);
var isSuccess = false;
var errorCodes = new List<string>();
using (var client = new WebClient())
{
var response = client.DownloadString(url);
JObject responseParsed = JObject.Parse(response);
//Get Success Status
JToken sucessToken;
var sucessFound = responseParsed.TryGetValue("success", out sucessToken);
if (sucessFound)
{
isSuccess = sucessToken.Value<bool>();
}
//Get Error codes
JToken errorsToken;
var errorsFound = responseParsed.TryGetValue("error-codes", out errorsToken);
if (errorsFound)
{
var errorsChildren = errorsToken.Children();
foreach (var child in errorsChildren)
{
errorCodes.Add(child.Value<string>());
}
}
else
{
errorCodes.Add("unknown-error");
}
}
if (!isSuccess)
{
var controller = sender as Controller;
var compiledErrors = ",";
foreach (var code in errorCodes)
{
//TODO: Use Dictionary Keys to return error message text
compiledErrors += ", " + code;
}
compiledErrors = compiledErrors.Replace(",,", "");
//Add errors to Form Model
var errorMsg = string.Format("Recaptcha Verification Failed: {0}", compiledErrors);
controller.ModelState.AddModelError(reCaptchaField.Id.ToString(),errorMsg);
}
}
}
}
}
}
@using Umbraco.Core.Logging
@using Umbraco.Forms.Core;
@using MYSITE.Events
@model Umbraco.Forms.Mvc.Models.FieldViewModel
@{
//Updated Jan 2016 to use new ReCaptcha 2.0
var siteKey = System.Configuration.ConfigurationManager.AppSettings["RecaptchaSiteKey"];
if (siteKey == "")
{
LogHelper.Warn<UmbracoFormsEvents>("ERROR: ReCaptcha v.2 is missing the Site Key - Please update the web.config to include 'key=\"RecaptchaSiteKey\"'");
}
else
{
//LogHelper.Info<UmbracoFormsEvents>("ReCaptcha Site Key:" + siteKey);
if (!string.IsNullOrEmpty(siteKey))
{
var theme = "clean";
var fieldSettingViewModel = Model.AdditionalSettings.FirstOrDefault(x => x.Key == "Theme");
if (fieldSettingViewModel.Value != "")
{
theme = fieldSettingViewModel.Value;
}
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey="@siteKey" data-theme="@theme"></div>
}
}
}
I'm running Umbraco Forms 4.1.5 but I'm missing a couple of the references you have shown in UmbracoFormsEvents.cs
using Umbraco.Forms.Mvc;
using Umbraco.Forms.Web.Controllers;
Any ideas?
Update:
Looks like including a reference in the Visual Studio project to Umbraco.Forms.Web.dll provides visibility into those namespaces for compilation. I suspect others who haven't installed via Nuget and have installed via Umbraco package manager are missing those as well.
This is excellent, thanks for sharing! Do you think there is a way to read the Recaptcha keys from the /App_Plugins/UmbracoForms/UmbracoForms.config file instead of needing to duplicate them in the web.config?
var secretKey = Umbraco.Forms.Core.Configuration.GetSetting("RecaptchaPrivateKey");
var siteKey = Umbraco.Forms.Core.Configuration.GetSetting("RecaptchaPublicKey");
UPDATE:
In case anyone else has this issue, I was receiving the above error because the "api.js" for ReCaptcha was being included in my templates already.
Thanks for the code. But I see a problem that recaptcha field isn't checked for mandatoryness. I was able to submit the form without selecting the recaptcha. Anybody has found the same problem?
I decided to create a class with the above code, using @AndyButland the library compiled but when i copied to the bin directory nothing happened. I then copied @HeatherFloyd code along with copying the UmbracoForms dlls from https://our.umbraco.org/projects/developer-tools/umbraco-forms/ but had an error
The type or namespace name 'UmbracoFormsEvents' could not be found (are you missing a using directive or an assembly reference?)
Before i dive into this any further would this resolve the issue mentioned for Contour? Along with the version i am using? Or is this strictly for Umbraco Forms?
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json.Linq;
using Umbraco.Forms.Mvc;
using Umbraco.Forms.Mvc.Controllers;
Could you also post your Fieldtype.Recaptcha.cshtml as well please? I have the class compiling but changing UmbracoGlobal to the one listed in Global.asax however i am not seeing any changes or breakpoints hitting.
@using Umbraco.Forms.Core;
@using System.Configuration;
@model Umbraco.Forms.Mvc.Models.FieldViewModel
@{
var siteKey = System.Configuration.ConfigurationManager.AppSettings["RecaptchaSiteKey"];
if (string.IsNullOrEmpty(siteKey))
{
<div>ReCaptcha v.2 is missing the Site Key</div>
}
else
{
//LogHelper.Info<UmbracoFormsEvents>("ReCaptcha Site Key:" + siteKey);
if (!string.IsNullOrEmpty(siteKey))
{
var theme = "clean";
var fieldSettingViewModel = Model.AdditionalSettings.FirstOrDefault(x => x.Key == "Theme");
if (fieldSettingViewModel.Value != "")
{
theme = fieldSettingViewModel.Value;
}
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey="@siteKey" data-theme="@theme"></div>
}
}
}
UmbracoGlobal is my own class derived from Umbraco.Web.UmbracoApplication class (customised umbraco application class that holds the application events)
So its simple, you must make sure you have a new event handler - FormValidate (in my case, but it can be any method name) and add this event handler to Form Validate event of the umbraco contour forms during the application started event.
Normally, your Global.asax file on the root folder is derived from Umbraco.Web.UmbracoApplication. But I have it as ExtendedUmbracoGlobal to have this working.
My FieldType.Recaptcha.cshtml is found under Umbraco>Plugins>UmbracoContour>Views
I add a form to my homepage which has no Recaptcha (old version). In debug mode OnApplicationStarted never hits neither does FormValidate method.
When designing the form i dont see a Recpatcha 2 option either, only Recpatcha so i readd Recaptcha (i assume changing the FieldType.Recaptcha.cshtml would display version 2)?
but the new captcha never displays? I think im missing something obvious? Thanks for your help Santhosh
I've added the exact same class as yours, only difference i have is adding Umbraco.Web.UmbracoApplication instead of UmbracoGlobal, so the code is exactly the same.
I have not setup a sitekey or secret key yet as i was looking for an error (to indicate its working) but when i view the form i only see the old captcha
I have the Global file inheriting from my new class and i can now see it hitting the OnApplicationStarted code.... however the form still displays the old captcha.
Upgrading ReCaptcha to the "I'm not a robot" version
I'm picking up an issue where we are looking to upgrade to the latest version of ReCaptch (the one that gives you just a simulated check box to click on to say "I'm not a robot").
We've modified FieldType.Recaptcha.cshtml as follows (basically removing the default and adding in the code to render the ReCaptcha):
Seems that it's not as simple as that though, as if I make the field Mandatory I always get the validation error message (whether or not I've said I'm not a robot); and if I don't make it Mandatory I don't get blocked even if I don't complete the ReCaptcha field.
Initially just wondering if this is even feasible without some change to Contour core and how it handles the valiation of the ReCaptcha field?
Thanks in advance for any advice.
Andy
I am looking for the exact same thing. Just following where this is going :-)
Seems that it doesn't work out of the box, but with some custom validation I've managed to get it working. For background this blog post from Tim and this Code Project article was useful in setting it up.
As noted above I've amended the default form field rendering - in FieldTyle.Recaptcha.cshtml - as follows:
And then I've wired up custom validation on an application event like this:
This looks to work as expected. If you ignore the CAPTCHA field, the response in the form is empty and so the call to Google comes back unsuccessful. If you successfully prove you aren't a robot, it'll come back with a success response.
Hello,
Sorry if this is a silly question but where do you go about putting the custom validation, do you have to create a separate class project and reference it or is it just placed in the App_Code folder, can you provide a full class file structure with references as well please if possible?
Thanks.
Should work either way Graham. I tend to have a separate class library but it should work in App_Code just the same. Having a brain fade about exactly what project I did this in I'm afraid so can't put my hands on it now. But think you'll need a reference to Umbraco.Forms.Mvc.
Andy
Thanks Andy, if you do happen to locate the project that you did this in, I would very much welcome if you could send me the code including the customised version FieldTyle.Recaptcha.cshtml.
Have found it, it's what I have above. The only bit you're missing maybe is the using statements - you'll likely need some of:
Likely not all as I have a bit more going on in my class.
Based on Andy's excellent post, I have made a few updates/enhancements. Code files copied here for anyone who is looking for "plug-n-play".
UmbracoFormsEvents.cs
(Compiled with other custom code, or added to App_Code directory)
\Views\Partials\Forms\Fieldtypes\Fieldtype.Recaptcha.cshtml
Heather thanks for posting some code!
I'm running Umbraco Forms 4.1.5 but I'm missing a couple of the references you have shown in UmbracoFormsEvents.cs
Any ideas?
Update: Looks like including a reference in the Visual Studio project to Umbraco.Forms.Web.dll provides visibility into those namespaces for compilation. I suspect others who haven't installed via Nuget and have installed via Umbraco package manager are missing those as well.
Hi Heather, Andy,
This is excellent, thanks for sharing! Do you think there is a way to read the Recaptcha keys from the /App_Plugins/UmbracoForms/UmbracoForms.config file instead of needing to duplicate them in the web.config?
Thanks,
David
Hi Heather.
I know its an old post.
But how can i change the Theme prevalues for recaptcha?
Hi Chris,
Yep - you found it "Umbraco.Forms.Web.dll". I have a secondary compiled "code" project and did use NuGet to access the libraries.
http://www.nuget.org/packages/UmbracoForms.Core/ for anyone who is looking for the right package.
Have you seen this javascript error before? (Error copied from Chrome Console)
I'm using same empty tag you are in Fieldtype.Recaptcha.cshtml:
UPDATE: In case anyone else has this issue, I was receiving the above error because the "api.js" for ReCaptcha was being included in my templates already.
Big thanks to Heather and Andy, this solution saved me some time today.
Just a note that if you happen to have the strange requirement to have 2 forms on the page, your second form may not show the second Recaptcha.
In this case I tweaked the FieldType.Recaptcha.cshtml to allow for multiple recaptcha fields with the following:
How about this package ?
https://our.umbraco.org/projects/collaboration/recaptcha-field-for-umbraco-forms/
Hi Paulis, Add references of the following:
Umbraco.Forms.CodeFirst.dll Umbraco.Forms.Core.dll Umbraco.Forms.Core.Providers.dll Umbraco.Forms.Core.Providers.V6.dll Umbraco.Forms.Core.Providers.V7.dll Umbraco.Forms.Mvc.dll Umbraco.Forms.References.dll Umbraco.Forms.UI.dll
Hi All,
Thanks for the code. But I see a problem that recaptcha field isn't checked for mandatoryness. I was able to submit the form without selecting the recaptcha. Anybody has found the same problem?
Regards
Santhosh
Sorry it was my fault. I didnt hook the eventhandler correctly. Now its fixed.
Thanks again
Regards,
Santhosh
I decided to create a class with the above code, using @AndyButland the library compiled but when i copied to the bin directory nothing happened. I then copied @HeatherFloyd code along with copying the UmbracoForms dlls from https://our.umbraco.org/projects/developer-tools/umbraco-forms/ but had an error
The type or namespace name 'UmbracoFormsEvents' could not be found (are you missing a using directive or an assembly reference?)
Before i dive into this any further would this resolve the issue mentioned for Contour? Along with the version i am using? Or is this strictly for Umbraco Forms?
Hi @J,
"UmbracoFormsEvents" was the name of the class in my custom file:
If you named your class differently, that would explain it. :-)
Hi J,
I have done it for 7.5.14 (Web Forms) + Contour v3.0.31. So it should work.
Here is my code:
Hope it helps
Here are the using statements:
using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Net; using System.Web; using System.Web.Mvc; using Newtonsoft.Json.Linq; using Umbraco.Forms.Mvc; using Umbraco.Forms.Mvc.Controllers;
Could you also post your Fieldtype.Recaptcha.cshtml as well please? I have the class compiling but changing UmbracoGlobal to the one listed in Global.asax however i am not seeing any changes or breakpoints hitting.
Here is my FieldType.Recaptcha.cshtml:
UmbracoGlobal is my own class derived from Umbraco.Web.UmbracoApplication class (customised umbraco application class that holds the application events)
So its simple, you must make sure you have a new event handler - FormValidate (in my case, but it can be any method name) and add this event handler to Form Validate event of the umbraco contour forms during the application started event.
Normally, your Global.asax file on the root folder is derived from Umbraco.Web.UmbracoApplication. But I have it as ExtendedUmbracoGlobal to have this working.
Hope it helps!
Thanks,
Santhosh
I have the code as you have suggested.
My FieldType.Recaptcha.cshtml is found under Umbraco>Plugins>UmbracoContour>Views
I add a form to my homepage which has no Recaptcha (old version). In debug mode OnApplicationStarted never hits neither does FormValidate method.
When designing the form i dont see a Recpatcha 2 option either, only Recpatcha so i readd Recaptcha (i assume changing the FieldType.Recaptcha.cshtml would display version 2)?
but the new captcha never displays? I think im missing something obvious? Thanks for your help Santhosh
Umbraco>Plugins>UmbracoContour>Views is the correct place for the cshtml file.
You don't see any difference on your form editor with recaptcha 2. It looks like this
And you must see something like this rendered on your form when viewed:
Can you show me the class how you have added the event handler "FormValidate" to the event "FormRenderController.FormValidate"?
Also show me the Global.asax content of your?
Did you setup the RecaptchaSiteKey and RecaptchaSecretKey on your appSettings?
I've added the exact same class as yours, only difference i have is adding Umbraco.Web.UmbracoApplication instead of UmbracoGlobal, so the code is exactly the same.
I have not setup a sitekey or secret key yet as i was looking for an error (to indicate its working) but when i view the form i only see the old captcha
We are close. Can you tell me the class name where you hooked the event handler with the event? For example, in my case - ExtendedUmbracoGlobal
This is the class that you must inherit on your Global.asax.
I have the Global file inheriting from my new class and i can now see it hitting the OnApplicationStarted code.... however the form still displays the old captcha.
Anyone?
is working on a reply...