In the last sprint for our truck routing application PlanDeliver we focused in large parts on making the app talk to a Google Spreadsheet. The idea is, that users can create/update orders in the Spreadsheet, which are then imported into the actual PlanDeliver interface. The spreadsheet on the other hand, receives live status updates of the orders, eg. about the allocation status.

As the integration library of choice we went for the excellent GData gem which wraps around a handful of Google APIs. Now, one of the more interesting things when talking to Google is Authentication (we discussed that before), fairly well explained in the GData on Rails tutorial. For those who don't want to read the whole tutorial: Basically, there are two methods available. The simple solution is to use username and password, the other option would be token based authentication.

Fail at first

Since we're pragmatic, we went with the easy solution first:


  client = GData::Client::Spreadsheets.new(:version => '3', :source => 'PlanDeliver')
  client.clientlogin('[google account email]', '[password]')

This worked fine when testing, but as soon as more people started using the feature, Google shut us down - apparently the number of authentications (per second) for one account is limited. So we started thinking about the token based option, named AuthSub. The good thing about AuthSub is, that you can create a 'session'-token which never expires! The bad thing is, that the whole token generation workflow is based on a request / response cycle which didn't work for us, because our GData integration is 100% backend...

Sinatra to the rescue

So we had to get a session token, but our app wasn't setup to get one. Of course, we could have added a controller to PlanDeliver to make it happen, but that would have created a lot of overhead: branching, merging, testing, deploying, reverting, ... So we decided to write up a very small Sinatra app that works as the endpoint for the Google authrorisation request and dumps the generated token somewhere. Here's all the code we need:


  [config.ru]

  require 'rubygems'
  require 'sinatra'

  root_dir = File.dirname(__FILE__)

  set :environment, ENV['RACK_ENV'].to_sym
  set :root, root_dir
  set :app_file, File.join(root_dir, 'authsub.rb')

  disable :run

  require 'authsub'
  run Sinatra::Application

  [authsub.rb]

  require 'rubygems'
  require 'sinatra'
  require 'gdata'

  get "/upgrade_token" do
    client = GData::Client::Spreadsheets.new(:version => '3', :source => 'PlanDeliver')
    client.authsub_token = params[:token]
    raise client.auth_handler.upgrade.inspect
  end

Start that up with thin and make it available through apache and you're ready to go!

Creating and using the session token

In order to actually use the mini-app, we have to use GData once again. This time we generate the URL which we have to visit (in a browser) to grant access:


  scope = 'https://spreadsheets.google.com/feeds/list'
  next_url = 'http://route/to/your/mini/app'
  secure = false
  session = true
  GData::Auth::AuthSub.get_url(next_url, scope, secure, session)

Open the generated URL in a browser, grant access (with the Google account of your choice) and watch the logs of the Sinatra app to find the session token. Then, instead of using clientlogin, we can use AuthSub when communicating with Google:


  client = GData::Client::Spreadsheets.new(:version => '3', :source => 'PlanDeliver')
  client.authsub_token = "[session token]"

Things on the side

Note that you need a different token for each Google service, eg. we had to repeat the process to get a token linked to the worksheet API (https://spreadsheets.google.com/feeds/worksheets).

The Sinatra app would be a prime candidate for a skinny daemon. Unfortunately the blog post came a day to late - but let us know when you publish (parts of) our code as a gem!