After some hours spent searching the web for implementation of Google reCAPTCHA Enterprise with ASP.NET CORE 3.1, I must, unfortunately, admit that I was not able to find anything I could use in my project. I've read the docs following the official site, but in the end, I'm still stucking for a clean implementation.
In ASP.NET Monsters there is an example, but targeting reCAPTCHA V3 and not reCAPTCHA enterprise.
There is also a nice post here Google ReCaptcha v3 server-side validation using ASP.NET Core 5.0, but again on reCAPTCHA V3.
Any help is appreciated.
So for me i needed to implement google recapthca with dotnet 5 using an angular front end. I am sure you can replace the angular front end with the native javascript instead, but this took me hours of investigating so hopefully it will help people.
First i had to enable reCAPTCHA Enterprise, to do this i went to https://cloud.google.com/recaptcha-enterprise/ and then clicked on the "go to console" button. This took me to my Google Cloud Platform. From here i needed to create a key, fill in the options and save. This key will be referred to as your SITE_KEY.
-- IF YOU ARE USING ANGULAR, READ THIS, ELSE SKIP THIS STEP AND IMPLEMENT IT YOURSELF
On the client i used ng-recaptcha, you can find it here
To implement this component, i added this import to my app.module.ts
import { RECAPTCHA_V3_SITE_KEY } from 'ng-recaptcha';
and this to the providers section
{
provide: RECAPTCHA_V3_SITE_KEY,
useValue: SITE_KEY_GOES_HERE
}
On my component, when the submit button is pressed, i used the ReCaptchaV3Service
from the library above. My code looks like this
this.recaptchaV3Service.execute(YOUR_ACTION_NAME).subscribe((recaptchaResponse) => {
// now call your api on the server and make sure you pass the recaptchaResponse string to your method
});
The text YOUR_ACTION_NAME
is the name of the action you are doing. In my case i passed 'forgotPassword'
as this parameter.
-- END OF ANGULAR PART
Now on the server, first i included this into my project
<PackageReference Include="Google.Cloud.RecaptchaEnterprise.V1" Version="1.2.0" />
Once included in my service, i found it easier to create a service in my code, which is then injected. I also created a basic options class, which is injected into my service, it can be injected into other places if needed.
RecaptchaOptions.cs
public class RecaptchaOptions
{
public string Type { get; set; }
public string ProjectId { get; set; }
public string PrivateKeyId { get; set; }
public string PrivateKey { get; set; }
public string ClientEmail { get; set; }
public string ClientId { get; set; }
public string SiteKey { get { return YOUR_SITE_KEY; } }
/// <summary>
/// 0.1 is worst (probably a bot), 0.9 is best (probably human)
/// </summary>
public float ExceptedScore { get { return (float)0.7; } }
}
Some of these values are not used, but i have added them for the future, encase i do use them.
Then i have created my service, which looks like so (i have created an interface for injecting and testing)
IRecaptchaService.cs
public interface IRecaptchaService
{
Task<bool> VerifyAsync(string recaptchaResponse, string expectedAction);
}
RecaptchaService.cs
public class RecaptchaService : IRecaptchaService
{
#region IRecaptchaService
/// <summary>
/// Check our recaptcha
/// </summary>
/// <param name="recaptchaResponse">The response from the client</param>
/// <param name="expectedAction">The action that we are expecting</param>
/// <returns></returns>
public async Task<bool> VerifyAsync(string recaptchaResponse, string expectedAction)
{
// initialize request argument(s)
var createAssessmentRequest = new CreateAssessmentRequest
{
ParentAsProjectName = ProjectName.FromProject(_recaptchaOptions.ProjectId),
Assessment = new Assessment()
{
Event = new Event()
{
SiteKey = _recaptchaOptions.SiteKey,
Token = recaptchaResponse
}
},
};
// client
var cancellationToken = new CancellationToken();
var client = RecaptchaEnterpriseServiceClient.Create();
// Make the request
try
{
var response = await client.CreateAssessmentAsync(createAssessmentRequest, cancellationToken);
return response.TokenProperties.Valid && response.TokenProperties.Action.Equals(expectedAction) && response.RiskAnalysis?.Score >= _recaptchaOptions.ExceptedScore;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return false;
}
}
#endregion
private RecaptchaOptions _recaptchaOptions;
public RecaptchaService(RecaptchaOptions recaptchaOptions)
{
_recaptchaOptions = recaptchaOptions;
}
}
Now my api endpoint, i inject this service and call it. Here is an example API method that calls the recaptchaService.
public async Task<IActionResult> ForgotPasswordAsync([FromBody] ForgotPasswordModel model)
{
// check our recaptchaResponse
var verified = await _recaptchaService.VerifyAsync(model.RecaptchaResponse, "forgotPassword");
if (!verified)
throw new ApplicationException("Recaptcha failed, please try again");
// successful, carry on
}
Hope this helps everyone, if there are any questions, please ask and i will edit this and update it with anything i have missed.