Sinatra OAuth Workflow - Use This to Speed Up Your App Development

Posted on 09 Feb 2013 by Eric Oestrich

This post was originally published on the SmartLogic Blog.

With this Sinatra workflow, your app development will be as smooth as Frank

When you need to connect to multiple different APIs, it can take a long time to manage your OAuth workflow for each third-party app. On a recent app development project, I was getting really agitated using cURL for every step in the process while working on pulling in data from multiple apps. So I decided to do something about it. I'm a programmer, damn it.

While I don't have a magic solution, I created a Sinatra OAuth proxy app that works with Harvest. This sped up development for connecting with Harvest, and with small changes, the same code can also work for other applications. For example, I tested it out on Foursquare and the same code mostly worked.

This is the code I used to speed up OAuth and inspect the third party app's API.

gist

server.rb
require 'sinatra'
require 'json'
require 'faraday'
require 'faraday_middleware'

enable :sessions

IDENTIFIER = "YOUR_IDENTIFIER"
SECRET = "YOUR_SECRET"
REDIRECT_URI = "http://localhost:3000/auth"
AUTH_URL = "https://api.harvestapp.com/oauth2/authorize"
TOKEN_URL = "https://api.harvestapp.com/oauth2/token"
BASE_API_URL = "https://api.harvestapp.com/"

# Middlware to insert "Accept: application/json" header
class JsonMiddleware < Faraday::Middleware
  def call(env)
    env[:request_headers]["Accept"] = "application/json"
    @app.call(env)
  end
end

# Redirect to the API to start the OAuth handshake
# http://www.getharvest.com/api/authentication-oauth2
# http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-4.1
get "/" do
  query_string = Rack::Utils.build_query({
    :client_id => IDENTIFIER,
    :redirect_uri => REDIRECT_URI,
    :state => "optional-csrf-token",
    :response_type => "code"
  })

  redirect "#{AUTH_URL}?#{query_string}"
end

# Harvest will redirect back here with the OAuth code you
# use to get the access token.
get "/auth" do
  query_string = Rack::Utils.build_query({
    :code => params[:code],
    :client_id => IDENTIFIER,
    :client_secret => SECRET,
    :redirect_uri => REDIRECT_URI,
    :grant_type => "authorization_code"
  })

  response = Faraday.post("#{TOKEN_URL}?#{query_string}")
  session[:token] = JSON.parse(response.body)["access_token"]
  redirect "/token"
end

# Removes favicon warnings
get "/favicon.ico" do
end

# Display your access token
get "/token" do
  session[:token]
end

# Proxy everything after / to Harvest
#
# http://localhost:3000/projects/1/entries?from=2013-01-10&to=2013-01-14
# => https://api.harvest.com/projects/1/entries?from=2013-01-10&to=2013-01-14
get "/*" do
  headers "Content-Type" => "text/plain"

  params.delete("captures")
  url = "/#{params.delete("splat").join}?"
  url += Rack::Utils.build_query(params)

  client = Faraday.new(:url => BASE_API_URL) do |faraday|
    faraday.request :url_encoded
    faraday.request :oauth2, session[:token]
    faraday.use JsonMiddleware
    faraday.adapter Faraday.default_adapter
  end

  response_body = client.get(url).body
  JSON.pretty_generate(JSON.parse(response_body))
end

How do you handle OAuth without cURLin' all day long? Comment and let us know.

For more like this, follow @SmartLogic and @ericoestrich on Twitter.

Image Source

comments powered by Disqus
Eric Oestrich
I am:
All posts
Creative Commons License
This site's content is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License unless otherwise specified. Code on this site is licensed under the MIT License unless otherwise specified.