Friday, July 9, 2010

Google Latitude on Rails using OAuth

I'm developing a Ruby on Rails App using the Latitude API.  I've spent the past 2 days working through numerous online turotials, API docs and various other sources to finally get a working result.  I thought I'd write it down to not only help others, but also remind myself when I come back to it next time.

1) Register domain with Google Webmaster Central


Go to: https://www.google.com/accounts/ManageDomains
Add domain, then verify it.

Create a pair of RSA keys. In linux and OS X you can use openssl
openssl req -x509 -nodes -days 365 -newkey rsa:1024 -sha1 -subj ‘/C=US/ST=CA/L=San Francisco/CN=example.com’ -keyout rsakey.pem -out rsacert.pem

Upload the rsacert.pem, and keep the rsakey.pem in a safe place.  For the Target URL path prefix, I just added my domain.  It's not important.

Once the domain is registered, you will see an OAuth Consumer Key and Secret.  You need this for your app.


Now to your App


2) OAuth Gem

Include in your environment.rb
config.gem "oauth"
Obviously you will need to install it on your system if you don't already have it.  I use version 0.4.1

3) Generate an OAuthAccessToken model
$ ruby script/generate model OAuthAccessToken
Note: I've done this because I don't want to store my access token directly in my User model.  This gives me the option of having tokens for multiple scopes for a user through a polymorphic association.

Migration:

create_table :oauth_access_tokens do |t|
      t.string  :oauth_token, :oauth_secret, :scope
      t.string  :subject_type, :limit > 30 #for polymorphic association
      t.integer :subject_id
      t.timestamps
end

4) User model
has_one  :latitude_access_token, :model_name => 'OAuthAccessToken', :as => :subject, :conditions => ['scope = ?','latitude']
5) Setup OAuth Consumer for Latitude

Create class lib/o_auth_consumers.rb


class OAuthConsumers  
  Latitude = OAuth::Consumer.new( CONSUMER_KEY, CONSUMER_SECRET, {
      :site => "https://www.google.com",
      :request_token_path => "/accounts/OAuthGetRequestToken",
      :access_token_path => "/accounts/OAuthGetAccessToken",
      :authorize_path=> "/latitude/apps/OAuthAuthorizeToken",
      :signature_method => "RSA-SHA1",
      :private_key_file => "#{RAILS_ROOT}/config/rsakey.pem" #path to my rsakey that I saved earlier
  }) 
end

Remember to change the CONSUMER_KEY and CONSUMER_SECRET according to the information supplied when you registered your Domain (Step 1)

6) Generate a controller for the app


ruby script/generate controller latitude
require 'oauth'
require 'oauth/signature/rsa/sha1'
class LatitudeController < ApplicationController
  
  def index
    #check the user has a latitude oauth access token
    if current_user.latitude_access_token
      token = OAuth::Token.new(current_user.latitude_access_token.oauth_token, current_user.latitude_access_token.oauth_secret)
      @request = JSON.parse(OAuthConsumers::Google.request('get', "https://www.googleapis.com/latitude/v1/currentLocation", token).body)
      @lat = @request['data']['latitude']
      @lng = @request['data']['longitude']
      @time = Time.at((@request['data']['timestampMs'].to_i)/1000).strftime('%b %d, %Y - %T')
    else
      redirect_to :action => 'authorise'
    end
    
  end
  
  def authorise
    if !params[:oauth_token]
      request_token = OAuthConsumers::Latitude.get_request_token(
                          { :oauth_callback => "http://localhost:3000/latitude/authorise" },
                          { :scope => "https://www.googleapis.com/auth/latitude" }
                        )
      session[:oauth_secret] = request_token.secret
      redirect_to request_token.authorize_url + "&domain=example.com&granularity=best&location=all"
    else
      request_token = OAuth::RequestToken.new(
                         OAuthConsumers::Latitude,
                         params[:oauth_token],
                         session[:oauth_secret]
                       )
      begin
        access_token = request_token.get_access_token( :oauth_verifier => params[:oauth_verifier] )
      rescue
        redirect_to :action => 'index'
      end
      
      if access_token
        #now save the access token for this user 
        OauthAccessToken.create :subject => current_user,
                                :scope => 'latitude',
                                :oauth_token => access_token.token,
                                :oauth_secret => access_token.secret
      end

      redirect_to latitude_path
    end
  end
  
end


7) Enjoy the Latitude


That's pretty much it.  Go to http://localhost:3000/latitude, you will be directed to Google to authorise your account, then once that all goes through, you will be sent back to your app, an Access Token will be created which will be stored for your user.  I'm not exactly sure how long this lasts at the moment, but I'm testing that right now and will update if I find anything important.

Thanks to:



Interested in your thoughts and comments.  Let me know if my code sucks.

Friday, February 19, 2010

Google Reader goes realtime through PubSubHubbub

PubSubHubbub is awesome...

Truly awesome.

Sunday, January 24, 2010

Keep track of your sex life with Nooky Diary

I've built a new site: http://nookydiary.com

My wife has kept a diary for a long time now. I'm a web developer, so I thought there could be a better way.

Nooky Diary is the totally private, safe way to keep track of your sex life. Join now for free with either your Twitter or Facebook account and see what you think.

Tuesday, October 27, 2009

Install MySQL on Snow Leopard including Rails Gem

After much pain, I've finally got MySQL installed on Snow Leopard including the MySQL 2.8.1 gem:

1) I installed the x86_64 package from:
http://dev.mysql.com/downloads/mysql/5.1.html#macosx-dmg

2) Then ran:

sudo env ARCHFLAGS="-arch x86_64" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config --with-mysql-dir=/usr/local/mysql --with-mysql-lib=/usr/local/mysql/lib --with-mysql-include=/usr/local/mysql/include

This was to fix the following problems I was continually having:

Rails:
uninitialized constant MysqlCompat::MysqlRes

Saturday, August 29, 2009