Saturday, June 1, 2013

OAuth, Simple?

I really don't think its possible. Any mechanism that requires multiple steps, including ones outside the control of your application, is going to add some layer of complexity. As I've been working with building functionality that consumes OAuth protected resources, I've narrowed down my list of what makes it so difficult to manage:

  1. The authorization flow requires you to maintain state since there is a transfer of control

  2. At least for OAuth 1.0, a very specific series of steps must be followed to sign each request

  3. There is more then one version (even OAuth 1.0 has a revision A and extensions for sessions) and there is room for provider specific requirements



Of the three, I'd say I've had the most headaches with the second point. Signing a request isn't necessarily difficult. Signing it correctly and determining what is wrong about a rejected signature seems to be the challenge.

While learning, I had the fortune to stumble upon an article that described how to build a Rack Middleware to verify the signed header for incoming API requests. At the heart of the solution was the use of a library called Simple OAuth. This library's sole purpose is to create and validate the authorization header. Given the required OAuth parameters, is will generate the signature, and if present, will parse an existing header to compare the header's signature with the one it generated to ensure they match.

After seeing this, I thought that it would be great if something similar existed as a Javascript library so I could just make requests without having to build a proxy to sign everything for me. If generating a request to an OAuth protected API could be as easy as this:



var keys = {
consumer_key: 'R1Y3QW1L15uw8X0t5ddJbQ',
consumer_secret: '7xKJvmTCKm97WBQQllji9Oz8DRQHJoN1svhiY8vo'
},

base = location.protocol + '//' + location.host + (location.port ? ':' + location.port : ''),
url = '/do_something',

oauth = new SimpleOAuth.Header('get', base+url, null, keys);

/**
* Map requests to /api path for reverse proxy
* They must be signed using the path the server will
* see when checking the signature.
*/
$.ajax({
url: '/api' + url,
type: "GET",
processData: false,
headers: { 'Authorization' : oauth.build() }
})
.done( function (data, textStatus, jqXHR) {
console.log('Success: ' + data);
})
.fail( function (jqXHR, textStatus, errorThrown) {
console.log('Fail: ' + jqXHR.status);
});



I thought that would help alleviate that required portion of OAuth by creating a simple, consistent method to build the signed header. So I opened up the OAuth specification and the Ruby implementation and starting trying to understand what was required to create a Javascript version of the library.

After some studying, I determined that UnderscoreJS could be my best friend for mimicking some of the nice features of the Ruby language. Most of library is Hash/Array mapping to apply transforms to the input parameters to satisfy each step outlined in the specification. Underscore parallels most of these functions to enable a similar style to the processing. Unfortunately, Javascript is lacking some of the basic libraries available in Ruby. Something was still required to handle parsing/building URI strings and actually producing the HMAC signature. I managed to cobble together a minimum set of URI processing functions and found some Javascript-based cryptography algorithms that provided the necessary functionality. My current converted Javascript version is available on GitHub.

The next question was will it create valid signatures? I created a series of test cases that compared the output of this library against the output of the Ruby version using the same inputs. Once I worked out those issues, I used it to sign a request to send to a real end-point like Yahoo's Social API.

What's really nice about this library is that it will automatically generate the correct timestamp, nonce values, and deal with normalizing the URL and other parameters. The only thing you really need to be aware of is what URL and request parameters to pass to the library so it generates the same signature that the service provider's verification logic will produce. Generally, you'll need to use the full URL to the service and only pass parameters for form posts. Unfortunately, as I found out, those two remaining items can also lead to a great deal of pain and suffering.

To avoid cross-domain requests, I setup a reverse proxy on my Apache server to pass requests to the remote service. I had started by testing it on a local service written in Ruby using Sinatra. I originally thought that the URL the service would see was the same as the real end-point's full path. However, it appears that Rack Request see's the HTTP_X_FORWARDED_HOST that Apache sets on proxied requests and uses that for the request URL. In that case, I had to sign everything using the path the browser thought I was requesting. However, when I did the same thing to send a request to the Yahoo Social API, it failed. Turns out that Yahoo sees the path for the real request, not the forwarded host. I only came to this conclusion after a lot of trial-and-error since the responses from Yahoo didn't just say "hey, your URL is different!". These issues are what really make implementing OAuth tedious. Something as simple as the URL can cause you hours of work trying to find the one reason among many that the service rejects the request.

I can say that just working on this project gave me a much clearer understanding of the signing mechanism of OAuth. As much as I wanted a solution that just worked, it seems that the true way to fully understand some of the concepts is to sit down and just write the code required to implement them. It made me a lot wiser when trying to debug all the "Bad Request" responses from the various end-points I wanted to interact with. Knowing what could be wrong helps narrow down the possible reasons quickly and, even though its still difficult, does make it a little bit easier then if I didn't understand it as well.