OAuth2 and So Can You

Patrick Van Stee's Headshot
Patrick Van Stee

Have you recently used a Twitter, Facebook or Google API? If so, you probably authenticated with OAuth2. 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 implementation 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:

oauth-diagram

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? Let's go ahead and generate a new rails app and create 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 with an application action. This action will represent the Resource Owner portion of the diagram above.

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 we're on to the important part. We need to find the registered application by client_id and send the access_token back to the requesting application. This action will act as the Authorization Server from the diagram above. But remember this isn't an API call, we have to redirect back.

So 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
      client_id = params[:client_id]
      request_uri = params[:request_uri]
      application = Application.find_by(client_id: client_id)
      authorization = application.authorizations.create
      access_token = authorization.access_token
    
      redirect_to "#{request_uri}?access_token=#{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. 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 browser? 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 pieces 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. Also, there are some great implementations already written like this Rack app and Rails engine.

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

Recent Comments

comments powered by Disqus