The XING API is running on OAuth 1.0. It’s a protocol that helps our users to give access to parts of their data and actions to 3rd party apps. It’s core feature is that the users don’t need to give their password to the 3rd party app. Most likely you know that process from other platforms, too.
Before continuing, we need to clarify an important detail. You might be reading this and ask yourself: “Wait .. 1.0 … why are they still using OAuth 1.0? Isn’t OAuth 2.0 out for a very long time?”
We also asked ourselves that question and we want to briefly share our view. To begin with, it works for us. Version 1.0 is simple. Admittedly it also has its flaws like calculating complicated signatures and we’ll talk about this later. Apart from that it’s a well matured protocol and fits our needs perfectly. To be honest, so far we didn’t need any of the OAuth 2.0 features and that’s why we’re still sticking with version 1. Another downside is, that its second version got way more complicated. Did you know that Eran Hammer, lead author and editor of OAuth 2.0, does not want to be associated with the OAuth 2.0 standard any longer.
At XING we use Ruby to power our API. The natural choice, when we started to develop it, was to not reinvent the wheel and use the well written oauth gem. Some months ago we decided to revise this decision and decided to write our own OAuth 1.0 implementation. In this article I want to explain why we did this.
Recently we received a bug report from one of our consumers. He was trying to send an HTTP PUT request. It was a signed request, using HMAC-SHA1, one of the signature methods supported by the OAuth protocol.
The request failed and the consumer managed to convince us that their code was bug free.
It was clear that the request failed, because the OAuth signature did not match. To understand the problem, it’s good to get a little bit of background about those signatures. When using OAuth, every HTTP request is signed to authenticate it. The signing prevents clients from sending the secrets (to be specific, the Consumer Secret and Access Token Secret) along with the request. In the end they are named “secrets” for a reason, right? A signature base string gets calculated over various request parameters, that then gets cryptographically signed with the secrets. On the server side, this process is repeated and the signatures are compared. If they match, the request is authenticated, if not it gets rejected.
To find out what was going on, we reproduced the consumers request and looked at our APIs behavior with a debugger. We managed to trace the cause of the problem to a statement in the oauth gem.
The consumer was sending an HTTP PUT request with a body. This body contained x-www-url-formencoded encoded data, that, by definition, has to be included into the signature base string calculation. The oauth gem, however, does not look at bodies of HTTP PUT requests. Clearly a bug.
How to fix it?
We knew that the error was happening inside the oauth gem and we knew how to fix it. “Why not send a pull request?”, we thought.
As is turned out, the last gem release was in 2012. The project on GitHub also looks no longer maintained, as the last pull-request got merged in December 2014. So sending a pull-request wasn’t an option.
The next thing we thought about, was forking the gem, fixing the bug, and publishing the forked gem. We tried to do this but soon gave up because the dependencies where difficult to setup.
As a next step, we tried to find other gems that implemented OAuth 1.0. We found simple_oauth and it looked very promising. It basically consists of a single Ruby class that implements the verification part of the protocol in 133 lines of code.
Unfortunately we couldn’t use that one either, because it focused on reading OAuth parameters from the Authorization header. We tried to work around that, but soon gave up, as it got too complicated.
Inspired from the simplicity of the “simple_oauth” gem, we decided to write our own implementation.
In the next blog post we’ll explain how we ensured that the new implementation was correct and efficient.