Integrating a Rails application with Twitter OAuth

Last time I described how to integrate a Rails app with Facebook Connect, today I’d like to take a moment and give you a small tour on how to integrate it with Twitter OAuth.

In case you missed it, OAuth is a protocol for authorizing APIs without the need of supplying any passwords in our application – we just store the tokens we later use for interacting with the API. Ok, let’s get started.

One thing you’ll need is a Rails App – I’ll be using my usual Twitter-clone app called Statusr. I’d like the statuses from Statusr to be pushed to Twitter.

The first thing I’ll need is to register my application on Twitter. To do that, log in to Twitter and go to http://twitter.com/oauth_clients/new. Fill out the form and remember to specify the callback url - it should be the action that will handle the callback from Twitter. It must be a valid url, so http://localhost or http://localhost:3000 will not work. You need to set up a domain name on your machine to make it work (on Mac OS X you can use NetInfo). In my case it’s http://statusr.local/twitter/new. You can always change it later if you’re not sure now.

The Application Type should be set to Browser. Also, select Read & Write under Default Access type. I will not be covering logging with Twitter now, so leave that option empty. When you’re done, click save.

Congratulations, you have an app registered on Twitter! Note down the token and secret keys, and put them on the bottom of config/environment.rb, like this:

TWITTER_CONSUMER_KEY = 'YOUR_CONSUMER_KEY'
TWITTER_CONSUMER_SECRET = 'YOUR_CONSUMER_SECRET'

Now it’s time to integrate our app.

We’ll be using moomerman-twitter_oauth gem, which will allow us to use the api in a simple manner

gem sources -a http://gems.github.com
sudo gem install moomerman-twitter_oauth

Once we have it installed, we’ll start integrating out app.

We will have to prepare our User model to be able to handle the Twitter Api via OAuth. Let’s fire up a migration:

script/generate migration AddTwitterOAuthToUser

In it, put the following definitions:

class AddTwitterOAuthToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :twitter_token, :string
    add_column :users, :twitter_secret, :string
  end

  def self.down
    remove_column :users, :twitter_token
    remove_column :users, :twitter_secret
  end
end

Afterwards, run the migration:

rake db:migrate

Ok, now let’s add some convienience methods to the User model:

class User < ActiveRecord::Base

  #... previous code

  # returns true if user has twitter tokens
  def twitter?
    self.twitter_token && self.twitter_secret
  end

  # holds the twitter client
  attr_accessor :twitter_client

  # returns an instance of TwitterOAuth::Client if user has twitter tokens
  def twitter
    if twitter?
      return self.twitter_client if self.twitter_client
      self.twitter_client = TwitterOAuth::Client.new(
          :consumer_key => TWITTER_CONSUMER_KEY,
          :consumer_secret => TWITTER_CONSUMER_SECRET,
          :token => self.twitter_token,
          :secret => self.twitter_secret
      )
    else
      false
    end
  end
end

With these methods we can access the TwitterOAuth client for a specific user easily. User#twitter? will return a boolean status whether it is integrated or not and User#twitter will return a TwitterOAuth client.

Now we need handling of the authorization process. It will be done by a controller:

require 'twitter_oauth'

class TwittersController < ApplicationController

  skip_before_filter :login_required

  before_filter :link_client

  def destroy
    begin
      current_user.twitter_token = nil
      current_user.twitter_secret = nil
      raise 'Could not save user' if !current_user.save
      flash[:notice] = 'Your have revoked your Twitter account authorization'
    rescue
      flash[:error] = 'Could not revoke your Twitter account authorization'
    end

    redirect_to(edit_user_path(current_user))
  end  

  # authorize oauth client and save access keys
  def new  

    begin
      if params[:oauth_token] != session['oauth_request_token_token']
        flash[:error] = 'Could not authorize your Twitter account'
        return redirect_to(edit_user_path(current_user))
      end

      oauth_token = session['oauth_request_token_token']
      oauth_secret = session['oauth_request_token_secret']

      # if we're here, save the tokens to user
      access_token = @client.authorize(
        oauth_token,
        oauth_secret
      )

      if @client.authorized?
        client_info = @client.info

        current_user.twitter_token = access_token.token
        current_user.twitter_secret = access_token.secret
        raise 'Could not save user' if !current_user.save
      end

      session['oauth_request_token_token'] = nil
      session['oauth_request_token_secret'] = nil
      flash[:notice] = 'Your account has been authorized at Twitter'
    rescue
      flash[:error] = 'There was an error during processing the response from Twitter.'
    end

    redirect_to(edit_user_path(current_user))
  end

  # start a request and send to twitter
  def create
    request_token = @client.request_token

    session['oauth_request_token_token'] = request_token.token
    session['oauth_request_token_secret'] = request_token.secret

    redirect_to request_token.authorize_url
  end

  private

    def link_client
      @client = TwitterOAuth::Client.new(
          :consumer_key => TWITTER_CONSUMER_KEY,
          :consumer_secret => TWITTER_CONSUMER_SECRET
      )
    end

end

Remember to put this in config/routes.rb:

map.resource :twitter

The controller uses 3 REST methods – new, create and destroy. Create is used to get a request token from Twitter and redirect to a page on twitter.com where the User authorizes our app. New handles the callback from twitter, authorizes the client and saves the access keys. Destroy removes the keys from the User essentially disabling the OAuth client.

To let the user authorize, we need to add links to one of the pages. In my example, I put this in users/edit.html.erb:

<h1>Twitter</h1>
<% if current_user.twitter? %>
  Twitter authorized! -
  <%= link_to 'click here to deauthorize Twitter', twitter_path, :method => :delete %>
<% else %>
  <%= link_to 'Authorize Twitter', twitter_path, :method => :post %>
<% end %>

At this point, when you go to users/:id/edit, and click on ‘Authorize Twitter’, you are redirected to Twitter, where you allow (or deny) the application access. If you allow, you are redirected back to the app, which saves your access token and secret and displays a flash message informing you that ‘Your account has been authorized at Twitter’ (that is if nothing went wrong along the way).

At this point we have an authorization mechanism for Twitter OAuth and a client associated to a specific user. So far we’ve been working on authorization – now it’s time to do something fun! Let’s push statuses from Statusr to Twitter. This example will also show how easy it is to interact with the Twitter API via OAuth.

In StatusesController#create, I’ll add the following line right after the status is saved:

current_user.twitter.update(@status.status) if current_user.twitter?

Now let’s try to add a status in Statusr:

Screenshot of Statusr with a status which should go to Twitter

Screenshot of Statusr with a status which should go to Twitter

Ok, status added, but has it been sent to Twitter? I’ve authorized my development account so it should be visible there…

Status pushed to Twitter (PLEASE NOTE: the username has been removed)

Status pushed to Twitter (PLEASE NOTE: the username has been removed)

And it is! The great thing about OAuth is how easy it is to work with the API once the authorization is done. Also, you don’t need to store any passwords which is also great for users. I hope you liked this tutorial! I’ve also included the source code for Statusr – you can download it here.

This entry was posted in Tutorials and tagged , , . Bookmark the permalink.

7 Responses to Integrating a Rails application with Twitter OAuth

  1. Nicolas says:

    Interesting post, but why not using the twitter Auth gem? (http://github.com/mbleigh/twitter-auth/tree/master), it makes everything much easier no?

  2. marcin says:

    Thanks a lot for the response. That gem looks great – I missed it, but I’ll give it a spin soon!

    As for the tutorial, I wanted to go through the whole process in case someone wanted to find out how the oauth works and have more control (like introducing custom callbacks).

  3. zzzoso says:

    I’m always wondering why:

    # returns true if user has twitter tokens
    def twitter?
    if self.twitter_token && self.twitter_secret
    true
    else
    false
    end
    end

    and not simply:

    def twitter?
    self.twitter_token && self.twitter_secret
    end

  4. marcin says:

    not sure why, but a good catch ;) I’ve edited the example as it is more elegant. Thanks!

  5. Keep up the good writing

  6. Chi Soffer says:

    Really interesting article. Thank you for moving along the time with a great read.

  7. gary says:

    I’m trying out ur example..but when i go to twitter.com i’m not getting redirected to my app. Instead a message displayed on twitter :
    “You’ve successfully granted access to iphonepurpletrail!

    Simply return to iphonepurpletrail and enter the following PIN to complete the process.
    5420096″

    My apps callback url is “http://iphone.girish.com/twitter/new” i.e. “http://localhost/twitter/new”.
    thanks for ur help!!!!

Tell us what you think!

We will keep your email private. And you know what the little * means.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>