Conquering Twitter OAuth Authorization in a Rails/React App
Tags: oauth, twitter, rails, react
Published: 2021-02-10 00:49:13

[Cover Photo by Brett Jordan from Pexels](https://www.pexels.com/photo/red-blue-and-yellow-textile-5417837/)

This is a quick walkthrough of making Twitter API requests that require OAuth authorization, in a Rails/React app. You will need a Twitter developer account, an app with read and write permissions registered within your account, and your application’s API key and API key secret. You’ll also need to have added a callback URL for your application in your developer portal. I suggest app.com/auth/twitter/callback.

Please note that Twitter calls their client credentials several different things, but I’ll be using API key and API secret to keep it simple.

If you’ve ever built a React application with a separate Rails API backend, you’ve likely used the omniauth and omniauth-twitter gems to handle authorization for various social media platforms. However, if your application, like my social media assistant app, does not have separate front and backends (i.e. you’re not using views at all), you won’t be able to (easily) use the omniauth gem. 

Instead, we’re going to use oauth and httparty. 

The Twitter 3-legged OAuth flow is made up of three steps:
1. POST oauth/request_token 
 * Your application will send a POST request to https://api.twitter.com/oauth/request_token.
 * The request will include your API key and API key secret
* If successful, the request response will return a token and a token secret
2. GET oauth/authorize
* Your application will send a GET request to https://api.twitter.com/oauth/authorize
* The request will include the previously returned token as a param.
* Your app’s user will be asked to authorize your app
* If successful, your user will be redirected to the callback URL specified in your developer portal
* The request response will include a oauth_token and a oauth_verifier
3. POST oauth/access_token
* Your app will send a POST request to https://api.twitter.com/oauth/access_token
* The request will include the previously returned oauth_token and oauth_verifier as params.

First, in our application’s Gemfile, we’ll add oauth and httparty as dependencies.

*Gemfile*
```
gem 'httparty'
gem 'oauth'
```
Make sure to run `$bundle install`.

Next, we’ll save our API key and API key secret as environmental variables in a **untracked** .env file.

Never, ever, save API keys or other sensitive information into a file that’s being tracked by a version control system; or upload it somewhere public where it can be accessed (e.g. GitHub).

*.env*
```
KEY=THISISOBVIOUSLYNOTMYAPIKEYREPLACEWITHYOURS
SECRET=AGAINTHISISJUSTANEXAMPLE
```

The next step is connecting your callback URL to the sessions controller create action.

*/config/routes.rb*
```
  get '/auth/:provider/callback', to: "sessions#create"
```

Now we’re set up and ready to start the 3-legged OAuth flow.

## Step 1: POST oauth/request_token 
We’re going to be making our API calls from a controller that exclusively handle any social media API calls.

*app/controllers/social_controller.rb*
```
require 'pry'
require 'oauth'
class SocialController < ApplicationController
    def create
        ckey = ENV['KEY']
        csecret = ENV['SECRET']
        consumer = OAuth::Consumer.new(ckey,csecret,
            :site => 'https://api.twitter.com',
            :authorize_path => '/oauth/authenticate',
            :debug_output => false)
        callback_url = "http://127.0.0.1:3000/auth/twitter/callback"
        request_token = consumer.get_request_token(:oauth_callback => callback_url)
        token = request_token.token
        token_secret = request_token.secret
        confirmed = request_token.params["oauth_callback_confirmed"]
        if confirmed === "true"
            redirect_to "https://api.twitter.com/oauth/authorize?oauth_token=#{token}"
        else
            redirect_to "/"
        end
    end
end
```

In the method, first we’re saving our API key and API key secret from our .env file to local variables.

```
def req_token
  ckey = ENV['KEY']
   csecret = ENV['SECRET']
…
end
```

Then we are creating a new consumer instance, passing it the API key and secret, the site we’re making the API request to, the authorize path and setting debug_output to false.

```
def req_token
…
        consumer = OAuth::Consumer.new(ckey,csecret,
            :site => 'https://api.twitter.com',
            :authorize_path => '/oauth/authenticate',
            :debug_output => false)
…
```

Next we save the callback URL into a local variable. We then make the POST request, by calling  get_request_token on our consumer instance, passing in the callback variable and saving the response as request_token (step 1 a-b). We then use that response to save the returned token and token secret (step 1 c).

## Step 2: GET oauth/authorize
```
def req_token
…
callback_url = "http://127.0.0.1:3000/auth/twitter/callback"
request_token = consumer.get_request_token(:oauth_callback => callback_url)
token = request_token.token
 token_secret = request_token.secret
…
end
```
To make sure our request was successful, we’re checking to see if request_token contains oauth_callback_confirmed. If so, we’re redirecting to https://api.twitter.com/oauth/authorize (step 2 a), adding the token we just saved to the URL as a param (step 2 b).

```
def req_token
…
confirmed = request_token.params["oauth_callback_confirmed"]
        if confirmed === "true"
            redirect_to "https://api.twitter.com/oauth/authorize?oauth_token=#{token}"
        else
            redirect_to "/"
        end
    end
```

This is the point in the OAuth flow when the users will be redirected and asked to authorize your application. If they do, they’ll be redirected to yourapp.com/auth/twitter/callback which we connected to sessions#create.

## Step 3: POST oauth/access_token

For example purposes, I’ve dumped all the rest of the flow into sessions#create. 
```

def create
        if params[:oauth_token] && params[:oauth_verifier]
            oauth_token = params["oauth_token"]
            oauth_verifier = params["oauth_verifier"]
            baseUrl = 'https://api.twitter.com/oauth/access_token'
            response = HTTParty.post(baseUrl + "?oauth_token=#{oauth_token}&oauth_verifier=#{oauth_verifier}" )
            @access_token = response.split("&")[0].split("=")[1]
            @access_secret = response.split("&")[1].split("=")[1]
            @user_id = response.split("&")[2].split("=")[1]
            @user_sn = response.split("&")[3].split("=")[1]
            user = User.find_by(username: @user_sn)
            if user
                session[:user_id] = user.id
                render json: UserSerializer.new(user)
            else
                new_user_info = get_user(@user_sn)
                 new_user = User.new(username: new_user_info[“name”], password: SecureRandom.hex, uid: @user_id )
                if @user.save
                   log_in(@user)
                  else
                  render :new
                end
                render json: new_user_info
…
            end
        end
    end
```

If the params promised in step 2e were returned, we’ll use HTTParty to make a post request, passing those params to the base URL.

That’s it! After you get the basics down, you’ll be able to connect the API calls to actions on your frontend.