Folks, it has been now over 2 years since I originally wrote this post and I see some people are still using it as reference. Although there is some valuable theory here in understanding how OAuth works, I don’t recommend using the code I show here anymore because it is way too outdated and solutions like Omniauth deal with this in a much simpler way, not to mention the tons of updates made to the Twitter gem. The example app I show here will also be turned off by Heroku because it’s running on an ancient stack and I don’t see any point in migrating it since everything here is just…old.
Recently I was asked to develop a small integration with Twitter on an Rails application. I immediately googled for a gem that covered the whole Twitter API and amusingly found a gem called the Twitter gem. I was given a Twitter username and password to work with and after looking at the gem’s homepage and seeing that the HTTPAuth was as easy as a single line of code to get everything I needed left me quite comfortable…until I ran a sample code to see if it worked.
The sample code printed out that Twitter would be deprecating HTTPAuth this month (it was recently postponed to August) and that OAuth was the way to go, so I went on googling stuff about OAuth, which wasn’t all that comfortable. Turns out it’s not so simple, at least not when you’re clueless to what OAuth is, which was my case.
So before actually showing some code I’d like to explain how OAuth works in the context of a web application. First of all, it’s brilliant: you get to integrate your application with any Twitter account without having to know it’s username nor password, so how’s that possible?
OAuth works with the concept of 3 tokens (and their respective secrets): consumer, request and access. The consumer token is provided to you via the API who implements OAuth, I’ll explain how we can get it from Twitter in a bit. The request token may be generated when you ask for authorization to an account, and perhaps the most important thing about it is the authorization URL it generates for each request token, which you redirect the user so he can allow access to his account. Then there’s the access token, which you get after the permission to access an account has been granted. In most cases you will have to store the access token somewhere so you don’t need to ask for authorization all the time, and after getting it you can access an account as easy as you would if you were using HTTPAuth.
Didn’t get how it works? Let’s summarize with a textual example. You’re accessing my application which is supposed to show all your tweets. I’ve already registered my application on Twitter and thus have stored my consumer tokens it provides in a file. When you reach my application there’ll be a button that links to a signin action which generates a request token and redirects you to an authorization URL on Twitter.
After inputting your information on the Twitter permission page you will get redirected back to my application where an access token will be created and stored somewhere. You will then see your tweets without having to go authorize yourself in ever again (unless you revoke access to my application in your Twitter account settings).
If you’re still a bit confused about how all this works out, check out this example application I developed specifically for this post. Case you want to look at all the application code, go on the app’s GitHub repository and clone the whole thing.
Anyway, all the integration can be achieved with a couple of actions in a single Rails controller and a handy dandy wrapper. So let’s get to the code already. What we’re gonna develop here is the backend of an application that has a database with a table of users who want to access Twitter (by tweeting and seeing their timeline), just like the one I developed as an example. Let’s start simple:
I figured the best choice for storing the consumer tokens and the callback URLs due to the simplicity of the whole thing was to use YAML in a format like this:
--- consumer_secret: l0lwtfh4x0rt3hl33td00d1ss03p1ch3h3h3 consumer_token: 0h41c4nuh4xl1k3t3hh3x3rs callback_urls: production: http://twitterintegration.heroku.com/twitter/auth development: http://dev.twipler.com:3000/twitter/auth
I made up those consumer tokens, but it’s extremely easy to get your own, just go on http://twitter.com/apps and register an application. Be sure to mention it’s a browser application and a callback url on your app where you’re doing the authorization (we’ll talk about this callback url later).
After registering you’ll be supplied with a consumer token and secret, just put them on their respective places in the YAML file. By the way, I store this on the config folder of my Rails app and the link http://dev.twipler.com is a DNS I found in some comment on StackOverflow that is registered to localhost (you can’t input localhost:3000 as a callback URL).
So now that we have our YAML file, let’s write a wrapper to manage it and do some low-level coding directly with the Twitter gem.
class TwitterWrapper attr_reader :tokens def initialize(config, user) @config = config @tokens = YAML::load_file @config @callback_url = @tokens['callback_urls'][Rails.env] @auth = Twitter::OAuth.new @tokens['consumer_token'], @tokens['consumer_secret'] @user = user end def request_tokens rtoken = @auth.request_token :oauth_callback => @callback_url [rtoken.token, rtoken.secret] end def authorize_url @auth.request_token(:oauth_callback => @callback_url).authorize_url end def auth(rtoken, rsecret, verifier) @auth.authorize_from_request(rtoken, rsecret, verifier) @user.access_token, @user.access_secret = @auth.access_token.token, @auth.access_token.secret @user.save end def get_twitter @auth.authorize_from_access(@user.access_token, @user.access_secret) twitter = Twitter::Base.new @auth twitter.home_timeline(:count => 1) twitter end end
This code is assuming that you supply it an ActiveRecord User object with access_token and access_secret attributes, which it will use to store and retrieve the access tokens.
Also notice the get_twitter method on how there’s a method call to authorize_from_access, which is the authorization method similar to the HTTPAuth authorization method, so once we’ve gone through all the trouble to get the access tokens, we can simply use them to authorize ourselves in with a single line of code.
Let’s see this wrapper in action on the TwitterController, which will be responsible for managing the integration:
class TwitterController < ApplicationController before_filter :twitter_wrapper, :login_required def index begin @twitter = @wrapper.get_twitter @account = @twitter.user_timeline.first.user.screen_name @tweets = @twitter.home_timeline rescue @twitter = nil end end def signin begin session[:rtoken], session[:rsecret] = @wrapper.request_tokens redirect_to @wrapper.authorize_url rescue flash[:error] = 'Error while connecting with Twitter. Please try again.' redirect_to :action => :index end end def auth begin @wrapper.auth(session[:rtoken], session[:rsecret], params[:oauth_verifier]) flash[:notice] = "Successfully signed in with Twitter." rescue flash[:error] = 'You were not authorized by Twitter!' end redirect_to :action => :index end def tweet begin @twitter = @wrapper.get_twitter @twitter.update params[:tweet] flash[:notice] = "Tweet successfully sent!" rescue flash[:error] = "Error sending the tweet! Twitter might be unstable. Please try again." end redirect_to :action => :index end private #current_user is the user who's logged in def twitter_wrapper @wrapper = TwitterWrapper.new File.join(Rails.root, 'config', 'twitter.yml'), current_user end
Explaining it all:
- The signin action is where you generate request tokens and redirect the user to an authorization url generated from that request token.
A useful thing to notice here is that we’re storing the request key and secret on session. “Why is that?” You ask. We’re redirecting the user to a url on Twitter, so we need to store the request information we just generated somewhere. “And why is THAT?” You annoyingly ask again. On to the next method to answer that one.
- The auth action authorizes us with Twitter by taking in the request token and secret we generated before, along with the PIN code. The big thing we need from Twitter is the PIN code. We use this PIN code to authorize ourselves the first time with Twitter, and only after this authorization we can generate the access token (and authorize ourselves with it). Since we’re a web application, we want Twitter to redirect us back somewhere after the user allows us access to his account, and along with that redirection comes the PIN code.
This code is sent as a paramenter named oauth_verifier which we’re handling here, that’s why we need to set the callback URL to this method.
- The other methods simply deal with the Twitter client object itself, which is pretty easy to mess with once you take a quick look at the Twitter gem documentation. Just for the sake of explaining, the tweet method simply takes a parameter from some form and tweets it.
That wraps it up for the back-end, it’s actually really simple once you understand the process: Redirect to Twitter -> get PIN code -> generate access token -> happiness.
For the front-end you can use this jQuery plugin to count characters gracefully in a textarea, or if you’re really lazy you can simply copy all this front-end code (I’m not saying it’s the best, its just the first one I found :P).
That’s basically it, if you’ve implemented this integration in a more simple way or if you think you have a better solution, please share, cheers!