Search code examples
javaauthenticationgoogle-analytics-apiruntimeexception

Google Analytics 4 API - service account has full admin privileges - still PERMISSION DENIED to all API calls


I have the following Java code to try and get a single metric from GA4 using the GA4 Java bindings / classes:

String propertyId = "123123123";

GoogleCredentials gc = ServiceAccountCredentials.fromStream(this.getClass().getResourceAsStream("/client_secrets.json"))
                    .createScoped(Collections.singletonList(StorageScopes.DEVSTORAGE_FULL_CONTROL));

            BetaAnalyticsDataSettings betaAnalyticsDataSettings = BetaAnalyticsDataSettings.newBuilder().setCredentialsProvider(FixedCredentialsProvider.create(gc)).build();

            BetaAnalyticsDataClient betaAnalyticsDataClient = BetaAnalyticsDataClient.create(betaAnalyticsDataSettings);

RunReportRequest request = RunReportRequest.newBuilder().setProperty("properties/" + propertyId).
                    addMetrics(Metric.newBuilder().setName("active7DayUsers")).
                    addDateRanges(DateRange.newBuilder().
                            setStartDate("2023-07-31").setEndDate("today")).build();

            RunReportResponse response = betaAnalyticsDataClient.runReport(request);

This results in an exception in the RunReportResponse generation, e. g.

com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: Request had insufficient authentication scopes

How can I get sufficient authentication scope?

I have so far

  • Confirmed, reconfirmed and confirmed again my PropertID passed in code is for the correct GA4 property.
  • Confirmed, reconfirmed and confirmed again that the GA4 account in which this PropertyID resides is in fact the account implied by my client_secrets.json downloaded from the GA4 web interface, via the Serivce Account implied in the client_secrets.json file, via the client_email field.
  • Confirmed, reconfirmed and confirmed again that the GA4 Service Account implied by my client_secrets.json file is, in fact, the GA4 service account associated with this Account and Property I am referencing, in the GA4 web interface.
  • Confirmed, reconfirmed and confirmed again that the Account referenced and Property referenced in code above have full administrator privileges active in the GA4 web interface.

Any ideas or pointers?

I've worked through every GUI option I can find, I have full admin permissions set for the GA4 Account and Property, and using the GA4 GUI I can SEE the properties are there, data is accumulating, etc. - everything is working EXCEPT the API.

The issue is incredibly common and I've worked through several posts about this, but there is simply no resolution.

I've also tried downgrading each permission for both the Account and Property, and upgrading them in all possible combinations, keep getting

com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: Request had insufficient authentication scope

What am I doing wrong? Is there something wrong with the code above?

Thanks


Solution

  • Please check another option as well: Go to your analytics.google account and click on Management of access to the property or Property Access Management (Sorry my page is in Italian language and I am using Translate to English option of my browser to be comprehensible) enter image description here

    then you should see the email available in client_secrets.json file here having the Administrator role.

    enter image description here

    Java Part:

    Supposing credentialsJsonPath variable is a String and the path to your secret json file, and propertyId is the variable containing your client propertyId.
    ... Files.createDirectories(Paths.get(credentialsJsonPath).getParent());

                    GoogleCredentials credentials =
                            GoogleCredentials.fromStream(new FileInputStream(credentialsJsonPath));
        
                    BetaAnalyticsDataSettings betaAnalyticsDataSettings =
                            BetaAnalyticsDataSettings.newBuilder()
                            .setCredentialsProvider(FixedCredentialsProvider.create(credentials))
                            .build();    
        
                    try (BetaAnalyticsDataClient analyticsData =
                            BetaAnalyticsDataClient.create(betaAnalyticsDataSettings)) {
                         
                        int offset=0;
                        int limit=100000;
    RunReportRequest request =
                    RunReportRequest.newBuilder()
                    .setProperty("properties/" + propertyId)
    .addDateRanges(DateRange.newBuilder().setStartDate("your start date").setEndDate("your end date"))
    
    
                    .addDimensions(Dimension.newBuilder().setName("date")) 
                    .addDimensions(Dimension.newBuilder().setName("eventName"))
    
                    .addMetrics(Metric.newBuilder().setName("eventCount"))
                    .addMetrics(Metric.newBuilder().setName("eventsPerSession"))
                    .addMetrics(Metric.newBuilder().setName("eventValue"))
    .setOffset(offset)
                    .setLimit(limit)
                    .build();
    
        RunReportResponse response = analyticsData.runReport(request);
                    System.out.println(response);
     }catch(Exception e){
    e.printStackTrace();
    }
    

    ofcourse, you can read any other compatible dimensions/metrics, I just used some examples.