One of the new features we've been working on behind the scenes at Mogotest is a Google Analytics (GA) integration. By integrating with the Web's most popular analytics service, we can help users see how specific browser rendering problems are affecting their bounce rates, conversions, and traffic funnels. The sort of stuff you'd like to know about as a site owner, right? Unsurprisingly, we also use GA ourselves to monitor our own traffic metrics and have some automated reports that help us measure our day to day progress.

In late 2008, Google began moving away from their own AuthSub authorization system and started offering access to all their Data APIs via OAuth. That's A Good Thing™. But since there doesn't seem to be a wealth of information on the 'net regarding the GA integration process, I figured it might be worthwhile to document how we integrated it with a Rails-based application and share it with the community. The OAuth Ruby Gem makes most of this process very simple, but there are a few tricky bits that might trip you up if you haven't worked with OAuth and the Google APIs before.

The first thing you need to do whenever you're tying into a system using OAuth is obtain the consumer token and secret from the provider. In this case, the provider is Google and we can retrieve this information by visiting Google's Domain Management Page. Unfortunately, this URL isn't the easiest to find. I probably never would have found it if it wasn't for Dan Sinclair's earlier GA + OAuth tutorial. Here's what it looks like:

Google Domain Management Page

Anyway, once you're on the Domain Manager page, add the domain for your application (the address from which authorization requests will originate). You'll need to verify ownership by either adding a meta tag to your website, adding a new page, or modifying a DNS record. Once you've done that Google will give you the OAuth consumer key and consumer secret you need for the next steps.

NOTE: this verification requirement can be painful if you don't have anything in production yet or need a separate set of credentials for testing or development. I'd suggest pointing DNS for some URL like test.yourdomain.com to a staging server to get it up and running and then changing it post-verification so you have at least two sets of OAuth credentials for the separate environments.

To authorize access to a specific GA account (like one belonging to one of our users), you need an access token. To get an access token, you first need to create a request token. The workflow for getting a request token using the OAuth gem involves creating a new Consumer object, passing in the consumer key and consumer secret, along with a few other options, and then calling the get_request_token method. For convenience, I've wrapped up the process of requesting a new Consumer into a library method, since we'll need to do it a couple times. Make sure that the token paths specified are exactly what is shown below in order for this to work with Google:

class GoogleAuth
  def self.consumer
    OAuth::Consumer.new(oauth_consumer_key, oauth_consumer_secret, {
                        :site               => 'https://www.google.com',
                        :request_token_path => '/accounts/OAuthGetRequestToken',
                        :access_token_path  => '/accounts/OAuthGetAccessToken',
                        :authorize_path     => '/accounts/OAuthAuthorizeToken' })
  end
end

Our call to get_request_token is going to get made inside the new action of our AuthorizationsController. We'll retrieve the request token, specifying a callback and a scope. The callback is the URL that the user will be directed to after authorizing with Google. The scope is used to specify the Google service that the user is authorizing us to access. Google provides many different API services in addition to analytics -- Calendar, Documents, YouTube, etc -- but we want to scope to the Analytics API here. We set the token and secret in the user session because we'll need them later when the user returns to the site. Finally, we get the authorization URL from the request token and redirect the user to it. Note that we also encode instructions to return to our callback URL after successful authorization.

def new
  @request_token = GoogleAuth.consumer.get_request_token({ :oauth_callback => callback_account_authorizations_url },
                                                         { :scope => 'https://www.google.com/analytics/feeds/' })
  session[:request_token] = @request_token.token
  session[:request_secret] = @request_token.secret

  redirect_to("#{@request_token.authorize_url}&oauth_callback=#{CGI.escape(callback_account_authorizations_url)}")
end

From the users' point of view, they'll get a link to click to "enable Google Analytics", which will take them to the new_authorizations_url corresponding to the action above. This will then auto-redirect them to the Google authorization page, which will resemble the screenshot below:

Google Authorization

After they authorize us to access their data, they'll be redirected back to our callback URL. When Google redirects to this URL they'll include an important query parameter that we need to create our precious access token: the OAuth verifier.

Here's what the callback action should look like in our controller:

def callback
  unless session[:request_token] && session[:request_secret] 
    flash[:error] = "No authentication information was found in the session. Please try again."
    redirect_to(root_url)
    return
  end

  unless params[:oauth_token].blank? || session[:request_token] ==  params[:oauth_token]
    flash[:error] = "Authentication information does not match session information. Please try again."
    redirect_to(root_url)
    return
  end

  @request_token = OAuth::RequestToken.new(GoogleAuth.consumer, session[:request_token], session[:request_secret])
  @access_token = @request_token.get_access_token(:oauth_verifier => params[:oauth_verifier])
    
  session[:request_token] = nil
  session[:request_secret] = nil

  current_user.update_attributes(:access_token => @access_token.token, :access_secret => @access_token.secret)

  flash[:notice] = "You have successfully linked your Google Analytics account."
  redirect_to(account_path)

rescue Net::HTTPServerException => e
  case e.message
  when '401 "Unauthorized"'
    flash[:error] = "This authentication request is no longer valid. Please try again."
  else
    flash[:error] = "There was a problem trying to authenticate you. Please try again."
  end 

  redirect_to(:action => 'new')
end

The first two conditional blocks just check to make sure that the token and secret are found in the user session and that the token provided by Google in the parameters matches the one that the user previously established. The next step is creating an access token from the verifier that Google sent back to us. This is the token we'll use to access the GA resources for the user going forward.

We can then clear out the session variables and update the user model with the access token and secret that we've obtained. By storing these values in our database, and associating them with the user account that authorized us, we can rebuild the tokens we need to obtain analytics data from Google at any time, without having to go through this process again.

Here's the controller code for our AuthorizationsController in its entirety (including a method to de-authorize us):

class AuthorizationsController < ApplicationController
  before_filter :require_user

  def new
    @request_token = GoogleAuth.consumer.get_request_token({ :oauth_callback => callback_account_authorizations_url },
                                                           { :scope => 'https://www.google.com/analytics/feeds/' })
    session[:request_token] = @request_token.token
    session[:request_secret] = @request_token.secret

    redirect_to("#{@request_token.authorize_url}&oauth_callback=#{CGI.escape(callback_account_authorizations_url)}")
  end

  def create
    redirect_to(:action => :callback)
  end

  # oauth callback
  def callback
    unless session[:request_token] && session[:request_secret] 
      flash[:error] = "No authentication information was found in the session. Please try again."
      redirect_to(root_url)
      return
    end

   unless params[:oauth_token].blank? || session[:request_token] ==  params[:oauth_token]
     flash[:error] = "Authentication information does not match session information. Please try again."
     redirect_to(root_url)
     return
   end

    @request_token = OAuth::RequestToken.new(GoogleAuth.consumer, session[:request_token], session[:request_secret])
    @access_token = @request_token.get_access_token(:oauth_verifier => params[:oauth_verifier])
    
    session[:request_token] = nil
    session[:request_secret] = nil

    current_user.update_attributes(:access_token => @access_token.token, :access_secret => @access_token.secret)

    flash[:notice] = "You have successfully linked your Google Analytics account."
    redirect_to(account_path)

  rescue Net::HTTPServerException => e
    case e.message
    when '401 "Unauthorized"'
      flash[:error] = "This authentication request is no longer valid. Please try again."
    else
      flash[:error] = "There was a problem trying to authenticate you. Please try again."
    end 

    redirect_to(:action => 'new')
  end
  
  def destroy
    current_user.update_attributes(:access_token => nil, :access_secret => nil)
    flash[:notice] = "You have successfully removed your Google Analytics account link."
    redirect_to(account_url)
  end
end

And example routes:

ActionController::Routing::Routes.draw do |map|
  map.resource :authorizations, :collection => { :callback => :any }
  …
end

At this point we're all set up to use Google Analytics data in our application. So we should probably do something interesting with it. Fortunately, Tony Pitale has made interfacing with GA itself -- that is, once OAuth connection has been established -- insanely easy with his Garb gem. To use Garb we can simply create a new Garb session object using a previously established OAuth access token (I personally like to wrap this up into a method on the User model):

session = Garb::Session.new
session.access_token = OAuth::AccessToken.new(GoogleAuth.consumer, user.access_token, user.access_secret)

And then we can retrieve account and profile data from Garb...

accounts = Garb::Account.all(session)
profile_id = accounts.first.profiles.first.id

In addition to accessing raw account info, stat values, etc, you can create some pretty elaborate "reports" in Garb. A sample report might look like:

class PageViewReport
  extend Garb::Resource

  metrics :unique_pageviews
  dimensions :page_path

  filters { eql(:page_path, '/') }
  filters { eql(:page_path, '/blog') }
end

report = PageViewReport.results(profile_id, :session => session,:start_date => 10.days.ago, :end_date => Time.now)
report[0].page_path
# => '/blog'
report[0].unique_pageviews
# => 500

Tony does a much better job explaining how reports work, and what you can do with them. I'd suggest checking out his Wiki, GitHub repository, or the series of posts over at Viget Extend for more information on Garb.

To present this data to a user, we can now toss together some pretty graphs and charts using my favorite JavaScript graphing library, raphael.js. This is a good way to visualize browser marketshare data, page access trends, and other analytical data that they might want to drill down on.

We hope you found this post useful. If you're interested in how this can be used practice, check out Mogotest. We're using this info to build the next generation of Web testing tools.

Try Mogotest risk free for 14 days.

© 2010 - 2014 Mogoterra, Inc. Back to Top