Skip to content

Instantly share code, notes, and snippets.

@vanstee
Last active December 23, 2015 05:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vanstee/ee0df7c1e8e754294f64 to your computer and use it in GitHub Desktop.
Save vanstee/ee0df7c1e8e754294f64 to your computer and use it in GitHub Desktop.

OAuth2 and So Can You

Have you recently used an API for accessing Twitter, Facebook or Google? If so, you probably authenticated with OAuth. Instead of using their own authentication schemes, most new services choose to implement OAuth2, the latest revision of the OAuth protocol. It gives your users a secure way to talk to your service, but more importantly, allows users to safely authorize access to their data from third-party services without giving them their credentials. Let's build a sample server implemtation in Ruby and see how it all works.

Here's What We're Going to Build

Since there are a few moving pieces, take a look at this graph of the typical flow of authentication:

 +--------+                               +---------------+
 |        |--(A)- Authorization Request ->|   Resource    |
 |        |                               |     Owner     |
 |        |<-(B)-- Authorization Grant ---|               |
 |        |                               +---------------+
 |        |
 |        |                               +---------------+
 |        |--(C)-- Authorization Grant -->| Authorization |
 | Client |                               |     Server    |
 |        |<-(D)----- Access Token -------|               |
 |        |                               +---------------+
 |        |
 |        |                               +---------------+
 |        |--(E)----- Access Token ------>|    Resource   |
 |        |                               |     Server    |
 |        |<-(F)--- Protected Resource ---|               |
 +--------+                               +---------------+

We start off asking the Resource owner to authorize our request, something like the ability to post tweets on their behalf. The user would then accept our request by clicking a link that would redirect them to Twitter and allow them to enter their credentials. Once Twitter confirms the credentials, it redirects the user back to our service including an access token. We can then use this access token to take the action we had previous asked the user about. The advantages here are that the user never gave us their credentials directly, only granted us a specific scope in which to operate, and still has the ability to revoke our access without changing their credentials or affecting other authorized services. Ok that was a lot of explaining. Let's take a look at some code.

Let's Cowboy Out Some OAuth

So you want to be able to authorize requests just like twitter? Well, we can start out by generating a new rails app and creating an OAuthController. While it's not the best design, to keep things simple we're going to do most of the work right in the controller. We can start off by allowing users to register their application with an application action.

First we'll need to create an Application class to hold on to the client_id.

class Application < ActiveRecord::Base
  has_many :authorizations

  before_save :generate_client_id

  def generate_client_id
    self.client_id = SecureRandom.hex(32)
  end
end

Now we can just create one of these in the matching controller action (we'll add routes for all of these at the end).

def application
  respond_with Application.create
end

Ok now were on to the important part. We need to verify the application and send the access_token back to the requesting application. But remember this isn't an API call, we have to redirect back.

So first let's create another model to hold the access_token we generate.

class Authorization < ActiveRecord::Base
  belongs_to :application

  before_save :generate_access_token

  def generate_access_token
    self.access_token = SecureRandom.hex(32)
  end
end

Now we just need to redirect back to the original url with the access token.

def authorization
  application = Application.where(client_id: params[:client_id]).first
  authorization = application.authorizations.create
  redirect_to "#{params[:request_uri]}?access_token=#{authorization.access_token}"
end

Now for the final touches, wiring up the routes to controller actions.

post '/applications'  => 'oauth#application'
get '/authorization' => 'oauth#authorization'

So let's review how we can use these routes we just made. First we register our application by posting to /applications. In the response we can find the client_id parameter used to generate an access_token. To authorize a user we need to redirect them to /authorization?client_id=... where the application will allow for its own authorization step and then redirect back to the request_uri along with the newly generated access_token.

But what about authorizing requests from a client other than a broswer? All we need to do is add another way to generate an access_token. Typically this means using HTTP Basic Authentication with the credentials of the user. While this doesn't give us the advantage of keeping our credentials from the client application, at least they are only used once after which we can revoke access independently.

There's Still More To Be Done

Keep in mind we didn't cover many peices of OAuth2 that would be present in a typical production implementation. To implement a full provider we would need more in-depth attributes for applications, knowledge of scope parameters as they related to our application, more robust redirection allowing for multiple query parameters, and better security by validating a client secret and supporting refresh tokens. For a great example check out GitHub's implementation of OAuth2.

In the next episode we'll cover how to use OAuth2 from an iOS app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment