Code4Fun
Code4Fun

Reputation: 473

Google reCAPTCHA Enterprise with ASP.NET CORE 3.1

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.

Upvotes: 0

Views: 7144

Answers (1)

Gillardo
Gillardo

Reputation: 9828

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.

Upvotes: 3

Related Questions