Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Analytics 3.0 auth flow

EDIT: Originally this question asked how I could authenticate with the Google Analytics API using only my API key. As vlatko pointed out, this isn't possible. Now I'm just focused on getting OAuth2 to work. I will be trying vlatko's suggestions when I get a chance and will update the question. In the meantime, feel free to contribute answers with anything you think I'm missing.


ORIGINAL QUESTION:

I'm trying to make requests to the Google Analytics API. I'm walking through the Hello Analytics tutorial trying to replicate the steps. Whatever I try, I can't seem to authenticate succesfully.

The tutorial says the following:

Open the file you created named HelloAnalyticsApi.java and add the following method:

private static Analytics initializeAnalytics() throws Exception {
    // Authorization.
    Credential credential = OAuth2Native.authorize(
        HTTP_TRANSPORT, JSON_FACTORY, new LocalServerReceiver(),
        Arrays.asList(AnalyticsScopes.ANALYTICS_READONLY));

    // Set up and return Google Analytics API client.
    return Analytics.builder(HTTP_TRANSPORT, JSON_FACTORY)
        .setApplicationName("Google-Analytics-Hello-Analytics-API-Sample")
        .setHttpRequestInitializer(credential)
        .build();
  }

When a user encounters this script, the application will attempt to open the default browser and navigate the user to a URL hosted on google.com. At this point, the user will be prompted to login and grant the application access to their data. Once granted, the application will attempt to read a code from the browser window, then close the window.

The difference is that I'm trying to do this with a servlet application, and I want to use simple API access with an API key (rather than an OAuth 2.0 client ID). I know that OAuth 2.0 is recommended, but I only need to access data that I own and want to simplify the technical requirements. I based this decision on this page, which says:

An API key is a unique key that you generate using the Console. When your application needs to call an API that's enabled in this project, the application passes this key into all API requests as a key={API_key} parameter. Use of this key does not require any user action or consent, does not grant access to any account information, and is not used for authorization.

If you are only calling APIs that do not require user data, such as the Google Custom Search API, then API keys may be simpler to implement. However, if your application already uses an OAuth 2.0 access token, then there is no need to generate an API key as well. In fact, Google ignores passed API keys if an OAuth 2.0 access token is already associated with the corresponding project.

I can't find many code examples of auth flow just using the API key - most everything I've found shows using the client ID with the downloaded .p12 file, for example the GoogleCredential javadoc. The one example application I could find was Google's Books Sample app. Anyway, here's what I tried (mimicking the first request in the tutorial, which gets a list of the accounts from the management API):

Analytics analytics =
        new Analytics.Builder(httpTransport, jsonFactory, null)
        .setApplicationName("Dev API Access")
        .build();
Management.Accounts.List list =
        analytics.management().accounts().list().setKey(apiKey);
Accounts accounts = list.execute();

Where "Dev API Access" is the "Name" field in my API console dashboard. The API key is a server key restricted to my IP address. This fails with the following response:

401 Unauthorized
{
  "code": 401,
  "errors": [
    {
      "domain": "global",
      "location": "Authorization",
      "locationType": "header",
      "message": "Login Required",
      "reason": "required"
    }
  ],
  "message": "Login Required"
}

I also tried this:

Analytics analytics =
        new Analytics.Builder(httpTransport, jsonFactory, null)
        .setApplicationName("Dev API Access")
        .setGoogleClientRequestInitializer(new AnalyticsRequestInitializer(apiKey))
        .build();

Management.Accounts.List list = analytics.management().accounts().list();
Accounts accounts = list.execute();

Which shows the same error. What am I doing wrong here? Is OAuth2 required for analytics calls? If so, why does just using the API key work in the Books Sample app?


Moving on, I went ahead and tried OAuth2 anyway - I created a client ID and downloaded the .p12 private key file. But I couldn't get that working either. Here's what I tried:

Credential credential =
        new GoogleCredential.Builder()
        .setTransport(httpTransport)
        .setJsonFactory(jsonFactory)
        .setServiceAccountId(serviceAccountId)
        .setServiceAccountScopes(AnalyticsScopes.ANALYTICS_READONLY)
        .setServiceAccountPrivateKeyFromP12File(new File(p12FilePath))
        .setServiceAccountUser(serviceAccountUser)
        .build();

Analytics analytics =
        new Analytics.Builder(httpTransport, jsonFactory, credential)
        .setApplicationName("Dev API Access")
        .build();

Management.Accounts.List list = analytics.management().accounts().list();
Accounts accounts = list.execute();

Where serviceAccountId is the email address of the Google account owning the project and serviceAccountUser is the email address listed on the generated client ID. This fails with the following:

400 Bad Request
{
  "error": "invalid_grant"
}

What does "invalid grant" mean, and how do I successfully authenticate (ideally without OAuth2)?

like image 424
Paul Bellora Avatar asked Dec 06 '12 17:12

Paul Bellora


People also ask

Is GA3 going away?

GA3 will be deprecated starting in July of 2023. Specifically, GA3 properties will stop collecting data at this date, but reports will be available until January 2024 at which point the entire service will be shut down.

How do I authenticate Google Analytics?

Click on Authenticate button to open the authentication popup. Choose or login to your Google account,which you wish to connect with Google Analytics WD plugin. The popup window will ask for relevant permissions. Click Allow, then copy the authentication code which will be provided subsequently.

Is Google Analytics being discontinued?

On July 1, 2023, standard Universal Analytics properties will stop processing new hits. If you still rely on Universal Analytics, we recommend that you prepare to use Google Analytics 4 going forward.

Is Google Authenticator oauth2?

Note: Use of Google's implementation of OAuth 2.0 is governed by the OAuth 2.0 Policies. Google APIs use the OAuth 2.0 protocol for authentication and authorization. Google supports common OAuth 2.0 scenarios such as those for web server, client-side, installed, and limited-input device applications.


2 Answers

To answer your first question: in general, OAuth2.0 is used for authorized access to user's private data, so getting user consent and obtaining an access token is required. In the case with Google Books API, however, if you're accessing public data, there is no need for end user consent so an API key is sufficient. If you try accessing non public data with the Books API, you'll still need an OAuth2 token.

The good news for your case is that even with OAuth2, you can bypass user involvement and streamline your flow with Service Accounts - assuming your application has access to the API. There is a way to set that up for the Analytics API, explained here (check the steps in the Service Accounts section). I think you are on the right track with your Credential builder, but I don't think you need to set the service account user in there, since you are not doing any user impersonation.

like image 114
vlatko Avatar answered Sep 28 '22 01:09

vlatko


vlatko's answer got me on the right track. The issue turned out to be that I was confusing the owner email address with the service account email address. For example, I was doing the following:

Credential credential =
    new GoogleCredential.Builder()
    .setTransport(httpTransport)
    .setJsonFactory(jsonFactory)
    .setServiceAccountId("[email protected]")                   //wrong
    .setServiceAccountScopes(AnalyticsScopes.ANALYTICS_READONLY)
    .setServiceAccountPrivateKeyFromP12File(new File(p12FilePath))
    .setServiceAccountUser("[email protected]")    //unnecessary
    .build();

When I needed to do this:

Credential credential =
    new GoogleCredential.Builder()
    .setTransport(httpTransport)
    .setJsonFactory(jsonFactory)
    .setServiceAccountId("[email protected]")
    .setServiceAccountScopes(AnalyticsScopes.ANALYTICS_READONLY)
    .setServiceAccountPrivateKeyFromP12File(new File(p12FilePath))
    .build();

Also, I had added [email protected] as a user on my Analytics application - this similarly needed to be the service account email instead.

like image 24
Paul Bellora Avatar answered Sep 28 '22 01:09

Paul Bellora