phato.blog

Humble semi-technical blog of Pat Wangrungarun

Rails OAuth with Twitter

06 February 2019 · view history

It had been an eternal struggling with me and OAuth since forever. I didn’t fully get how it works before. I first had to deal with Twitter authentication when I created Twlist (with Node and also Rails) And later on I when created Twit Media Grid I think I understand it a bit more.

But no, that was a mistake. And since then I haven’t worked any on it anymore. So the knowledge eventually got lost in space.

Now fast-forward to the present day I had to work this very Twitter authentication again. So I dug through my old code and realised those are not going to work. At least not production-ready for sure. Well then, perhaps it’s time to work on it properly.

Figured it would be too much pain collecting parameter from scratch and since I’m working on a Rails application so I just have a go with Ruby OAuth gem. Here goes.

1. Create a Consumer object

A Consumer object will be used to get both OAuth token and Access token. To create it, according to Ruby OAuth, here’s how to do so.

OAuth::Consumer.new(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, {:site => "https://api.twitter.com"})

And since this object is reuseable, we can make use of Or Equals and put it in a controller like this.

class ApiController < ApplicationController
  CALLBACK_URL = "/login/callback"
  TWITTER_CONSUMER_KEY = ""
  TWITTER_CONSUMER_SECRET = ""

private

  def consumer
    @@consumer ||= OAuth::Consumer.new(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, {:site => "https://api.twitter.com"})
  end
end

Now that we have a Consumer, we can use it to make a request to get an OAuth token.

2. Getting a request token

At this point, dont’ forget to add the URL to your Twitter app’s Callback URL.

To get a request token, you’ll to make a request using consumer.get_request_token. You’ll also need to supply a callback URL within an object containing :oauth_callback as well.

class ApiController < ApplicationController
  CALLBACK_URL = "/login/callback"
  TWITTER_CONSUMER_KEY = ""
  TWITTER_CONSUMER_SECRET = ""

  def request_token
    _request_token = consumer.get_request_token(:oauth_callback => "#{params[:base_url]}#{CALLBACK_URL}")

    session[:request_token] = _request_token.token,
    session[:request_secret] = _request_token.secret,

    redirect_to _request_token.authorize_url(:oauth_callback => "#{params[:base_url]}#{CALLBACK_URL}"),
  end

private

  def consumer
    @@consumer ||= OAuth::Consumer.new(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, {:site => "https://api.twitter.com"})
  end
end

There are two important values, _request_token.token and _request_token.secret, they both need to be kept in order to recreate _request_token again in another action. So you can store them in Cookie, or session, your call.

Now with a URL generated from _request_token.authorize_url, you’ll need to navigate a user to this URL, which is Twitter’s authorisation page. In the page, if they’re not already logged in they’ll need to fill in their credentials, then they’ll be asked to authorise the app.

Finally they will be redirected back to your application callback URL, along with some query strings, one of them is oauth_verifier.

3. Getting an Access token

Since a user will be landed in another action (in this case, ApiController#access_token), so access to _request_token from ApiController#request_token is lost. We’ll need to recreate it. Remember _request_token.token and _request_token.secret that you keep them? They shall be at help here, along with consumer as a private method.

_request_token = OAuth::RequestToken.from_hash(consumer, {
  oauth_token: params[:request_token_token],
  oauth_token_secret: params[:request_token_secret]
})

Almost there! Now we have the _request_token back, and also oauth_verifier from Twitter. We can make a request for an Access token using _request_token.get_access_token. You’ll also need to supply an OAuth verifier within an object containing :oauth_verifier as well.

class ApiController < ApplicationController
  CALLBACK_URL = "/login/callback"
  TWITTER_CONSUMER_KEY = ""
  TWITTER_CONSUMER_SECRET = ""

  def request_token
    _request_token = consumer.get_request_token(:oauth_callback => "#{params[:base_url]}#{CALLBACK_URL}")

    session[:request_token] = _request_token.token
    session[:request_secret] = _request_token.secret

    redirect_to _request_token.authorize_url(:oauth_callback => "#{params[:base_url]}#{CALLBACK_URL}"),
  end

  def access_token
    _request_token = OAuth::RequestToken.from_hash(consumer, {
      oauth_token: session[:request_token],
      oauth_token_secret: session[:request_secret],
    })

    access_token = _request_token.get_access_token(oauth_verifier: params[:oauth_verifier])

    render json: access_token
  end

private

  def consumer
    @@consumer ||= OAuth::Consumer.new(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, {:site => "https://api.twitter.com"})
  end
end

And finally, Access token get! You can any further request to Twitter using this token as you like. It’s all yours now.