Tuesday, April 23, 2013

.NET, OAuth2, and the Google Analytics Service account.

Google likes to change things. About a year ago I went through one of their C#  Data Export examples to grab some Analytics data for a program I was writing. The previous method of authentication was easy add the user/pass into the url request or add an API key to the query.

In August 2012 Google deprecated the v2.3 reporting and authentication method in favor of OAuth2 at the time of deprecation Google didn't provide a lot of information for C# / .NET developers who needed to migrate off the old way.

http://code.google.com/p/gdata-issues/issues/detail?id=2955

Since that time they've improved their dot-net-client libraries and wrote a tutorial on authenticating with OAuth2. I wasn't able to get their code to compile when I attempted but it did put me on the correct path.

This is a project of firsts: first time with MVC, first time attempting to make a SPA (Single-Page Application) Knockout.js, and first time working with OAuth2 authentication.  I followed John Papa's excellent SPA Overview for the most part.

The latest WebSite and MVC WebSite default templates from Microsoft have OAuth2 authentication built in and doesn't require any setup at all just two-step authentication with whatever service we're logging in with.
I didn't want this since I only wanted access to my analytics account. The analytics API also seemed to have some issues getting certain information using this method.

After some intense googling I found the OAuth2 Service account of course with no working C#/.NET examples...This is what I ended up doing.

I put everything I could find together into this project.

Follow the beginning of Googles OAuth2  tutorial and register your app in Googles API Console.

Create a ClientID and choose 'service account' download your private key.  


Edit the following:

Models/Utils.cs

 public static IAuthenticator getCredentials(string scope)
    {
        const string ServiceAccountId = "SERVICEACCOUNTID.apps.googleusercontent.com";
        const string ServiceAccountUser = "ServiceAccountUser@developer.gserviceaccount.com";
      try
      {
          AssertionFlowClient client = new AssertionFlowClient(
              GoogleAuthenticationServer.Description,
              new X509Certificate2(
                  HttpContext.Current.Server.MapPath("~/Content/key/0eee0d919aaef70bbb5da23b192aede576577058-privatekey.p12"), "notasecret", X509KeyStorageFlags.Exportable))
{ Scope = scope,  ServiceAccountId = ServiceAccountUser};
 OAuth2Authenticator<AssertionFlowClient> authenticator = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);
 return authenticator;
}
catch (Exception ex)
{ }
 return null;
}
Changing the path to the location of the key download from the console earlier. and changing SERVICEACCOUNTID and ServiceAccountUser to whats listed in the API Console. Once thats completed the Controller will attempt to authenticate using the method below:
try {
    Session["authenticator"] = Utils.getCredentials(AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue());
    IAuthenticator authenticator = Session["authenticator"] as IAuthenticator;
    AnalyticsService service = new AnalyticsService(new BaseClientService.Initializer() {
        Authenticator = authenticator
    });
    var profiles = service.Management.Profiles.List("~all", "~all").Fetch();
    user.profiles = profiles;

} catch (Exception ex) {
    ViewBag.Error = ex.Message;
    return View("Error");
}
One thing to note is that it seems this current version can only authenticate one scope at a time...I've been unable to pass in more than one.

AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue() will only bring back the profiles that are available under the authenticated account.

A different scope is needed to query the profiles and the analytics data in them. In addition to changing the scope the service account  needs to be added to the analytics profile as an authenticated user.

GitHub Project
Google Dot Net Client Libraries
Google OAuth2 
Google Service Account documentation