Search code examples
javascriptc#google-analyticsgoogle-analytics-apigoogle-analytics-firebase

Can't generate Google Analytics DataChart client side with Service Account Token (Invalid Credentials Error)


I'm trying to implement Google Analytics Graphs via their Javascript API, as the example on their site link.

But I keep getting "401 Invalid Credentials" each time I try to execute gapi.analytics.googleCharts.DataChart

I'm getting the access token server side (C#) using the following code with data from the JSON generated for the Service Account

var cred = new ServiceAccountCredential(
    new ServiceAccountCredential.Initializer(clientId)
    {
        Scopes = new[] { AnalyticsReportingService.Scope.AnalyticsReadonly },
        User = clientEmail,
        ProjectId = "projectID"
    }.FromPrivateKey(privateKey));

var token = cred.GetAccessTokenForRequestAsync(authURI);
token.Wait();
var result = token.Result;
return result;

or (using the full json string, see Note too)

GoogleCredential cred;

var gCred = GoogleCredential.FromJson(json).UnderlyingCredential as
    ServiceAccountCredential;

var token = gCred.GetAccessTokenForRequestAsync("https://accounts.google.com/o/oauth2/auth");
return token.Result;

While on the client side

gapi.analytics.auth.authorize({
    'serverAuth': {
      'access_token': '{{ ACCESS_TOKEN_FROM_SERVICE_ACCOUNT }}'
    }
  });

goes through and using gapi.analytics.auth.isAuthorized() returns true using any of the server side functions but it fails when trying to call

  var dataChart1 = new gapi.analytics.googleCharts.DataChart(queryJson);
  dataChart1.execute();

returns 401 "Invalid Credentials", the server side query returns values just fine so I think the user permissions is not the issue

NOTE: Using the same code as the second one (generating the credential using the json string without casting as a ServiceAccountCredential) I can get data from the API server side

cred = gCred.CreateScoped(scopes);

using (var reportingService = new AnalyticsReportingService(new BaseClientService.Initializer
    {
        HttpClientInitializer = cred
    }))
...
var getReportsRequest = new GetReportsRequest
{
    ReportRequests = new List<ReportRequest> { reportRequest }
};
var batchRequest = reportingService.Reports.BatchGet(getReportsRequest);

var response = batchRequest.Execute(); //This returns a response object with all the data I need

Solution

  • If anyone has the same issue:

    You need to use GoogleCredential.GetApplicationDefault() to create the Credential object, in the case of Analytics you should get something like this

    var credential = GoogleCredential.GetApplicationDefault().CreateScoped(AnalyticsReportingService.Scope.AnalyticsReadonly);
    

    This will get the json file from the Environment Variables on Windows, so you need to set GOOGLE_APPLICATION_CREDENTIALS with the path to the json as a System Variable.

    Initialize the service and set the service variable

    using (var reportingService = new AnalyticsReportingService(new BaseClientService.Initializer { HttpClientInitializer = credential }))
    {
      var serviceCredential = cred.UnderlyingCredential as ServiceAccountCredential;
    }