Tags
Greetings,
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!
Pingback: Pitos Blog » Blog Archive – [GEEKY] How to use Twitter in Rails - This looks like a really nice and detailed tutorial on how to use Twitter in Rails. Check it out! p.s. if you don’t know what the previous sentence means, don’t worr
Pingback: Особое программирование » Post Topic » Интеграция rails-приложения с Twitter
where should i put the TwitterWrapper file
In the lib folder inside your Rails project is fine :)
I don’t understand why you have to register an email first? Is the point to simply sign in with twitter, without using a regular registration?
Hey Jesse,
The point is to simply integrate Twitter to your Rails app, it’s not a tutorial on ‘how to authenticate using Twitter’, of course you could use the integration to do that, but I just chose one of the many uses of the integration.
Why don’t you fork the app and rewrite it using Twitter authentication instead? I’d be happy to link to it :)
Pedro, thank you for replying. I’m desperately frustrated by getting twitter authentication to work. Firstly, I don’t know how to get the localhost callback right. I like your idea, rewrite the app for twitter auth only, but I’m afraid I don’t have the confidence at this point. I will give it a shot, though, and email you if I have any problems that break me down.
Isn’t the ‘http://dev.twipler.com:3000′ callback link working for you?
Without giving it much thought I’m guessing the authentication with Twitter could be implemented by just keeping a session ID associated with an access token and keeping both stored at the database. Since the whole trouble of integrating with Twitter is already there, you’d just need a slight rework to handle the session stuff.
Feel free to ask anything you want!
Thanks a lot for the post, was exactly what I was looking for and worked perfectly.
Thanks a lot for this post. I have an issue with the example application you provided. I am getting the following error:
uninitialized constant TwitterWrapper::OAuth
Could you plesae help me on this?
I’m really gonna need more than that to figure out what the problem was, theoretically the example application should be flawless since it is working perfectly live. One guess I would take is that you didn’t install the twitter gem on your system. Mail me at pedro@pedromtavares.com with further details case that doesn’t solve your problem.
Hi,
This is really nice. Actually where will i get from the consumer_secret and consumer_token. This is same as twitter username and password. callback_urls is different for me or the same will work. Please verify my questions.
my development url is http://localhost:3000/users.
can you help me?
Thanks,
Priya.
As I mention on the blog post, you can get the consumer token and secret by registering an app on http://twitter.com/apps. It obviously is not the same as your username and password as that would break the whole point of using OAuth.
You can input any callback URL you want as long as that deals with the ‘oauth_verifier’ parameter, I just used /twitter/auth because that made more sense.
Hi,
Thanks for your reply. yes now i understand and also got the all keys and url for this.
Thanks,
Priya
Hi i am new to rails , i saw the example page you gave and was searching the very same for my app at localhost. So this is what i did :-
1) Downloaded and extracted the files from
https://github.com/pedromtavares/twitter_integration
2) edited the yaml file with my consumer key and token
3) Tried running script/server i got few warnings but the server was up…
Warnings : -
http://postimage.org/image/1z7uy1q9w/
4) But i wasn’t able to get it work as this is what i got when i opened http://127.0.0.1:3000
http://postimage.org/image/28erog4/
Pls help… Where am i going wrong??
It’s kind of hard to guess what went wrong if you don’t provide me a stack trace, sorry.
how to get the stack trace? And which versions of ruby and rails should i be running on… i guess that mite b d issue….
By looking at the terminal output. If you’re that new to Rails then I wouldn’t advise trying to implement this, try reading a nice tutorial about Rails first, such as this one and then come back, I’m sure you’ll then be able to solve the problem on your own.
Ok .. I gave you the stack trace on my 1st post
http://postimage.org/image/1z7uy1q9w/
But thanks for the tutorial….. I’ll try and figure out things myself… :)
Hi,
i’ve tried using you’r sample code, but it seems that by now the twitter gem has changed (OAuth object doesn’t exists). do you have any thoughts about how to use the newer version?
Thanks,
Roee.
Yeah, right now things have been made way easier due to the popularity of OmniAuth to authenticate with external services. With it you can easily get an access token to supply to the Twitter gem, skipping most of the stuff I mentioned in this post, so what was left was basically doing what everyone (including this post) recommends when dealing with external services: write your own wrapper for it.
There are tons of tutorials and screencasts on OmniAuth out there, so the only work you’ll need to do is actually set it up and read the Twitter gem documentation to learn the new methods you can use. I won’t even bother going through that because I’d be underestimating my readers since it’s just so easy, good luck!