OAuth and Django — a cautionary tale
Have you ever implemented OAuth authorization in any project of yours? Well, I have these last two weeks in a Django project I am doing, and it was not a nice experience. The following is going to be a bit of a rant against the state of authentication libraries, but take it as a cautionary tale and remember one thing:
Hours of doing can save you minutes of thinking.
So, let me tell you a little something about my experiences.
The first thing you'll encounter on your quest is the choice between OAuth 1.0 and OAuth 2.0. There are voices for both sides, and they're both very convincing, so what you're going to do (if you're like me) is first implement OAuth1, then switch to OAuth2, then scrap that and go back to OAuth1. Because the first thing you'll try is to find and use a good OAuth1 library for Django, of which there are none. Oh, there are OAuth libraries all around, but I couldn't get any of them to work. So you'll say to yourself, screw this, I'm going to do OAuth2, it's better anyway. Then you'll spend some time reading up on bearer tokens and refresh tokens and grant types and you'll come away more confused than enlightened. Then you'll play around a bit with the existing implementations like Google's wonderful OAuth playground and you'll say to yourself: I can do this! And you'll start and do that, reading parts of the humongous spec on the side and notice more and more that there's loads of stuff in there that you don't even want to think about. Then you'll not get it done right, you won't find any client libraries for using it either, and then finally you'll read Eran Hammer's rant over OAuth2 and decide that it's really not in your best interest to implement OAuth2, and go back to searching for OAuth1 libraries you could use.
So now you're stuck at that part again, but this time, you're determined. You're determined to find a stable and good implementation of OAuth1, that also offers 1.0a, because that's important for security. You'll skip oauthlib because you don't trust it (a mistake, as you'll find out later) and also because the documentation is a bit sparse. Well-written but sparse. So instead you'll go directly for Leah Culvers six-years-old OAuth implementation. It's a bit crummy and you can see its age, but it's quite simple and adaptable, so you copy it verbatim into your project and start hacking away…
A mere two weeks later, you'll have touched every part of the library and added your own crumminess to it. You'll have an OAuth implementation that's half-working, is neither OAuth 1.0 nor OAuth 1.0a but somewhere in between, and you'll find new errors every other day, because you never took the time to read any of the OAuth tutorials there are — because you really couldn't find good and simple ones, detailing the differences between OAuth versions. But, you'll have a working authentication scheme that uses signed requests and never exchanges secrets in the clear, which is a good thing all on its own.
So let's examine the issues you will have had by the time you've come this far.
- You did some reading at the beginning about OAuth and its versions, but couldn't find good enough material to make a decision. Go back and do that now, you'll regret it many times if you don't. Then pick one version and (in the case of OAuth2) one workflow and stick with that. You can extend later, if you need to.
- You didn't find any good, trustworthy-looking libraries, for neither version of OAuth. That's ok, just pick one and stick with it. Switching between libraries will not do you any good but burn your time away.
- Once you've collected some experience and read enough of the materials and the spec, write down your findings and make it easier for others and for future-you to find them and learn from it.
As you can see, I'm in the third stage described. I've tried to address some of the issues I've found with current OAuth implementations and collect my knowledge about that stuff in code. It's far from finished and far from done, but it will be useful once you start an OAuth1 implementation for yourself. You can find that code on GitHub along with some documentation I wrote, and I sincerely hope you can learn from my mistakes.