David wen avatar
David Wen
Entrepreneur, software developer, management consultant. He once killed 10,000 newborn chicks for charity.
66cda44c2d2a12f00f89b73d2a6a248333b3814d
Oh My Auth

Today reminded me of a Sherlock Holmes story – The Adventure of the Engineer’s Thumb (no, I didn’t lose my thumb or almost get crushed by a hydraulic press… though some people today did want to burn the house to the ground).  I thought of it because the engineer in the story was deliberately driven around in circles even though the destination was right next to where he started.  That’s what it felt like to learn about OAuth and Twitter today at Dev Bootcamp.

I’m no Sherlock Holmes but here’s an attempt to make the path to your destination more pleasant.  This post will focus on using ruby, sinatra, OAuth 1.0 with Twitter, and simply getting a tweet to work from a third-party site (your app).

Step 0: Set up your environment

In your gemfile and environment files, make sure to require / include the ‘oauth‘ and ‘twitter‘ ruby gems.  Get a high-level understanding of OAuth by reading this light-hearted back and forth blog post.  If you’re thinking this process is more complicated than it needs to be, good for you for reading everything.  At the end of the day, remember that you’re just trying to sign in to a website.  Except someone else has the account details.  And they need to make sure you’re legit.  Twice.

Step 1: Create a consumer

Twitter needs to know you are a real developer making something useful with their authentication API.  Simply go to the Twitter Dev Site and register.  Make sure you have the ‘Read and Write’ access level – not just the ‘Read’. After you have registered, Twitter will give you a ‘consumer key’ and a ‘consumer secret’ – both of which are required for Twitter to know you are a registered developer with them.

The OAuth gem that you installed allows you to create a consumer object with the following code, which we put into a helper file:

>> helper.rb

def create_consumer
  OAuth::Consumer.new(
    ENV['CONSUMER_KEY'], ENV['CONSUMER_SECRET'],
    :site => "https://api.twitter.com")
end

>> environment.rb

if Sinatra::Application.development?
  twitter_data = YAML.load_file(APP_ROOT.join('config',
  'twitter.yml'))
  ENV['CONSUMER_KEY'] = twitter_data['consumer_key']
  ENV['CONSUMER_SECRET'] = twitter_data['consumer_secret']
end

You could put your consumer key / secret directly in the ‘environment.rb’ file if you like; however, often times we are pushing to GitHub and you don’t want to give the world access to your twitter account!  Who knows what mean people will post with your account.  That’s why we hide the key and the secret separately in a YAML file – in this case, saved as ‘twitter.yml’ in the config directory.  That file looks something like the below.  Make sure to put ‘/config/twitter.yml’ file in your .gitignore or else it’ll still be pushed to GitHub!

>> twitter.yml

consumer_key: 'abcdefghijkl'
consumer_secret: 'mnopqrstuvwxyz0123456789'

Step 2: Get request token

You can use the consumer object you created in step one to make a call to Twitter to get your request token.  This token is the first of many magical tokens you need in this process.  Two things to notice are that first, we are storing this request token in a session called ‘session[:request]‘.  Second, we are including, as an argument, something called a callback URL.  This is the URL that the user will be directed to after they sign in to Twitter on Twitter’s website.

>> helper.rb

def request_token
  host = request.host
  host << ":9292" if request.host == "localhost"
  session[:request_token] ||=
    create_consumer.get_request_token(
    :oauth_callback => "http://#{host}/auth")
end

We haven’t talked about routes yet up to this point.  Assuming a very basic site, you will have an index that will have nothing on it except a link to sign in with Twitter.  That link will take the user to the route that will call the helper methods (‘create_consumer’ and ‘request_token’).  Note that the ‘host’ variable is set here to dynamically obtain the callback URL (so if you post to Heroku your user is not redirected to localhost!).

>> index.rb (controller)

get '/' do
  erb :index
end

get "/request" do
  redirect request_token.authorize_url
end

The ‘authorize_url’ method is a method on the request token object, which makes a url out of the request token that Twitter returned.  This is when the user is prompted to sign in on Twitter’s site.  Because you sent in a callback URL earlier, Twitter already knows where to redirect the user when he or she is finished signing in.

Step 3: Get access token

Once the user signs in, we can go ahead and ask Twitter for the access tokens.  We do so in the ‘/auth’ route since that’s where we told Twitter to send the user when we set the callback URL.

>> index.rb (controller)

get '/auth' do
  get_access_token
  erb :tweet
end

The ‘get_access_token’ method is written in our helper as such:

>> helper.rb

def get_access_token
  access_token = request_token.get_access_token(
    :oauth_verifier => params[:oauth_verifier])
  session[:token] = access_token.token
  session[:secret] = access_token.secret
  session.delete(:request_token)
end

If you’re thinking, “Woah there, you’re calling ‘request_token’ again?” – nice catch!  But don’t worry, we used the ‘||=’ method earlier on so it won’t actually make another request to Twitter – it’ll simply return what was stored in the session.  The ‘get_access_token’ needs an argument ‘:oauth_verifier’ which would have been passed back with the request token.  Specifically, it is passed back in the URL, which means you can use ‘params[:oauth_verifier]‘ to access it.  If you’re thinking this is a bit convoluted by now, you’re not alone.  Why so many tokens?  Enter OAuth 2.0 (unfortunately, that’s not the topic of discussion for this post).

Once we get back the access_token, you can do things like call the methods ‘token’ and ‘secret’ on it and store them in their respectively-named sessions.  We also deleted the ‘:reqeust_token’ session since request tokens are only useful once anyways and we like to keep our sessions clean.

Step 4: Post to Twitter!

At this point, make sure you have your Twitter gem configured correctly:

>> environment.rb

Twitter.configure do |config|
  config.consumer_key = ENV['CONSUMER_KEY']
  config.consumer_secret = ENV['CONSUMER_SECRET']
end

From Step 3, we can see that the user is now looking at the ‘tweet.erb’ page.  When they post a tweet, they are directed to the appropriate route:

>> index.rb (controller)

post '/tweet/new' do
  tweet = client.update(params[:text])
  if tweet
    tweet.text
  else
    status 500
    "Something bad happened :("
  end
end

This route calls a method ‘client’ which uses the Twitter gem to create a Twitter Client object.  This object is what we use to call the ‘update’ method, which takes as an argument the text that you want to post to Twitter.  You’ll also notice the ‘if-else’ statement.  In our code, this route was actually called using AJAX, so we wanted to send back a success/fail message and status.  The code for the ‘client’ method is below:

>> helper.rb

def client
  @client = Twitter::Client.new(
  :oauth_token => session[:token],
  :oauth_token_secret => session[:secret])
end

The Twitter gem requires authentication of the user that wants to post, which we now have!  They are stored happily in our sessions and the above code takes them and puts them into the arguments.

And… that’s it!  Hopefully the mystery is solved.  If not, and you are still deeply confused and saddened, take the advice that Sherlock Holmes gave to the engineer after the engineer asked, “what did I gain from all this?”

“A good story,” replies Holmes.

Thanks!

Check out the GitHub repo and the link to our deployed Heroku app here - https://github.com/Dawenster/twitter_jd.  Special credits go to J and Shadi for helping make this work!

Subscribe la!
David wen avatar
David Wen
Entrepreneur, software developer, management consultant. He likes to type as if he's playing the piano dramatically.