googleapis / google-api-dotnet-client

Google APIs Client Library for .NET
https://developers.google.com/api-client-library/dotnet
Apache License 2.0
1.35k stars 526 forks source link

Permission denied when trying to create assessment request for recaptcha enterprise #2299

Closed menyanthe closed 1 year ago

menyanthe commented 1 year ago

Hi,

I am trying to use the API library for Recaptcha Enterprise in order to be able to use Workload Identity Foundation authentication. However, I get

The service recaptchaenterprise has thrown an exception. HttpStatusCode is Forbidden. Permission denied for: recaptchaenterprise.assessments.create. Please ensure you have sufficient permissions: https://cloud.google.com/iam/docs/granting-changing-revoking-access)
 ---> The service recaptchaenterprise has thrown an exception.
HttpStatusCode is Forbidden.
Google.Apis.Requests.RequestError
Permission denied for: recaptchaenterprise.assessments.create. Please ensure you have sufficient permissions: https://cloud.google.com/iam/docs/granting-changing-revoking-access [403]

My code looks as follows:

            var googleCredential = GoogleCredential.FromFile(config.Value.FileName).CreateScoped("https://www.googleapis.com/auth/cloud-platform");
            var accessToken = await googleCredential.UnderlyingCredential.GetAccessTokenForRequestAsync();
            var recaptchaService = new Google.Apis.RecaptchaEnterprise.v1.RecaptchaEnterpriseService(new BaseClientService.Initializer
            {
                HttpClientInitializer = googleCredential
            });

            GoogleCloudRecaptchaenterpriseV1Assessment requestData = new GoogleCloudRecaptchaenterpriseV1Assessment()
            {
                Event__ = new GoogleCloudRecaptchaenterpriseV1Event()
                {
                    ExpectedAction = expectedAction,
                    Token = recaptchaResponse,
                    SiteKey = config.Value.ClientKey
                }
            };
            var request = new Google.Apis.RecaptchaEnterprise.v1.ProjectsResource.AssessmentsResource.CreateRequest(recaptchaService, requestData, "projects/test-123456");
            request.AccessToken = accessToken;
            var response = request.Execute();

Am I doing something wrong (missing something perhaps)? Same configuration works with a different nuget package, however they do not support Workload Identity Foundation - only Service Account json keys, using environment variable GOOGLE_APPLICATION_CREDENTIALS. I tried with both json files - the one with the Service Account generated key and the one generated from Workload Identity Foundation.

amanda-tarafa commented 1 year ago

Don't do this:

request.AccessToken = accessToken;

When you set the credential here:

var recaptchaService = new Google.Apis.RecaptchaEnterprise.v1.RecaptchaEnterpriseService(new BaseClientService.Initializer
{
    HttpClientInitializer = googleCredential
});

the client service object will set the access token appropiately (in the authorization header). What you are doing is setting it as a query string parameter which is not recommended (and might not even work in some cases). So your code should look like this:

            var googleCredential = GoogleCredential.FromFile(config.Value.FileName).CreateScoped("https://www.googleapis.com/auth/cloud-platform");
            var recaptchaService = new Google.Apis.RecaptchaEnterprise.v1.RecaptchaEnterpriseService(new BaseClientService.Initializer
            {
                HttpClientInitializer = googleCredential
            });

            GoogleCloudRecaptchaenterpriseV1Assessment requestData = new GoogleCloudRecaptchaenterpriseV1Assessment()
            {
                Event__ = new GoogleCloudRecaptchaenterpriseV1Event()
                {
                    ExpectedAction = expectedAction,
                    Token = recaptchaResponse,
                    SiteKey = config.Value.ClientKey
                }
            };
            var request = new Google.Apis.RecaptchaEnterprise.v1.ProjectsResource.AssessmentsResource.CreateRequest(recaptchaService, requestData, "projects/test-123456");
            var response = request.Execute();

If that still doesn't work, I'd need more information about your Workload Identity Federation scenario, etc.

menyanthe commented 1 year ago

It is still the same error. And I currently use the Service Account key - just to make sure that I use the same working scenario that I use with the other lib (because there are more factors involved when using the Workload Identity Foundation - there I added AWS provider and also an OIDC provider at localhost - to make it easier to test on local environment).

amanda-tarafa commented 1 year ago

If you are using a normal service account key, then can you please double and triple check that the service account has the required permissions and that indeed the service account key that you are using corresponds to the service account with the required permissions?

I'll try to reproduce now and I'll come back here when I know more, but I'm really not expecting this to be a problem with either Google.Apis.Auth or Google.Apis.RecaptchaEnterprise.v1 libraries.

menyanthe commented 1 year ago

Yes, the same file is used in both cases - with the other lib, I have a response and everything, but here - it throws the error above. Thank you for your help.

amanda-tarafa commented 1 year ago

Can I ask which is the other library that works for you?

menyanthe commented 1 year ago

https://www.nuget.org/packages/Google.Cloud.RecaptchaEnterprise.V1

menyanthe commented 1 year ago

The code I have for it is as follows:

  var createAssessmentRequest = new CreateAssessmentRequest
            {
                ParentAsProjectName = ProjectName.FromProject(config.Value.ProjectID),

                Assessment = new Assessment()
                {
                    Event = new Google.Cloud.RecaptchaEnterprise.V1.Event()
                    {
                        SiteKey = config.Value.ClientKey,
                        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 >= 0.7;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);

                return false;
            }
amanda-tarafa commented 1 year ago

OK, several things here:

  1. The same team of people (myself included) maintain Google.Apis.RecaptchaEnterprise.v1 and Google.Cloud.RecaptchaEnterprise.V1. The recommended library is Google.Cloud.RecaptchaEnterprise.V1 which uses the same auth library as Google.Apis.RecaptchaEnterprise.v1, which is Google.Apis.Auth, which means that the same auth features, including WIF, are supported by both libraries. I'll show you how to set up WIF with Google.Cloud.RecaptchaEnterprise.V1 below. You can read more about the different types of libraries we support here: https://cloud.google.com/dotnet/docs/reference/help/library-types
  2. But first let's try and clear the error you are seeing with Google.Apis.RecaptchaEnterprise.v1, which, as expected, I cannot reproduce. Your two codes are not equivalent, and the failure may be because of that. Can you please try the following
            // This uses the credential from env var GOOGLE_APPLICATION_CREDENTIAL.
            var googleCredential = GoogleCredential.GetApplicationDefault().CreateScoped(RecaptchaEnterpriseService.ScopeConstants.CloudPlatform);
            var recaptchaService = new Google.Apis.RecaptchaEnterprise.v1.RecaptchaEnterpriseService(new BaseClientService.Initializer
            {
                HttpClientInitializer = googleCredential
            });

            GoogleCloudRecaptchaenterpriseV1Assessment requestData = new GoogleCloudRecaptchaenterpriseV1Assessment()
            {
                Event__ = new GoogleCloudRecaptchaenterpriseV1Event()
                {
                    // You are not setting an expected action on the code with Google.Cloud.RecaptchaEnterprise.V1 so let's not set it here.
                    Token = recaptchaResponse,
                    SiteKey = config.Value.ClientKey
                }
            };
            // You had a harcoded project ID here, let's make super certain that you are using the right one.
            var request = new Google.Apis.RecaptchaEnterprise.v1.ProjectsResource.AssessmentsResource.CreateRequest(recaptchaService, requestData, $"projects/{config.Value.ProjectID}");
            var response = request.Execute();
  1. If you want to use WIF with Google.Cloud.RecaptchaEnterprise.V1 you need to add an explicit dependency to Google.Apis.Auth v1.58.0 in your project. This is because Google.Cloud.RecaptchaEnterprise.V1 transitively depends on Google.Apis.Auth currently v1.56.0 which does not support WIF. Then you have two options:

Let me know if some of this is not clear, etc.

menyanthe commented 1 year ago

Thank you! It is working now - with both variants. I am yet to test with WIF/AWS, but Service Account key works with both.