It’s a pretty sad fact that OAuth has come to be a de-facto industry standard for API authentication, because OAuth is so broken.
Before OAuth, creating and consuming APIs across services was hell. We mostly just did stupid stuff like asked users for their passwords, so we could log in on their behalf and maybe do some page scraping stuff. If a proper API actually existed, they probably implemented a custom authentication protocol that required you to read their exact implementation, permissions, and handshake procedure, making the interface un-reuseable.
Ideally, OAuth would come along and solve these problems for us by:
- Allowing untrusted applications to perform actions on behalf of a user at the API provider.
- Authenticating the user’s permission to perform said actions, without divulging the user’s password.
- Selectively granting permissions to an untrusted client, to prevent hijacking of account & login details.
- Revoking the client application’s privileges at command of the user, without requiring a password change.
- Promoting code reuse through a standard protocol for negotiating access to an API provider.
OAuth takes every single one of these requirements…and partially solves all of them.
While OAuth is conceptually great, and is much clearer in the 2.0 spec, it still contains a number of warts that make it a complete pain to integrate. Consider this:
- There’s basically no standard for how to implement an OAuth provider. Try pointing an OAuth client at a different provider and try to count the number of changes you have to make to get it working. It’s mind-boggling the number of unique tweaks and quirks that providers come up with. The whole concept of interoperability is thrown out the window, and you have to go back to “well, it generally works this way, but you have to read all their developer docs and spend an afternoon conforming to the custom design.” And this is all by design! Read it straight from the spec:
…this specification is likely to produce a wide range of non-interoperable implementations…
…with the clear expectation that future work will define prescriptive profiles and extensions necessary to achieve full web-scale interoperability.
- Scopes are pretty much a crapshoot. Take a look at this passage from the spec:
The value of the scope parameter is expressed as a list of space-delimited, case-sensitive strings. The strings are defined by the authorization server.
So…how do you find out what scopes are supported, allowed, or required? Surprise! You don’t. You have to read the developer docs. Assuming they are posted. More generally, it’s impossible to programmatically register a client, learn about server capabilities, discover endpoints, and most other things. Which means hours of slogging through documentation to manually code these into the OAuth client, all so you can do it again for the next provider. Did Amazon’s success in services teach us nothing about the value of being able to programmatically discover, query, register with, and use a service?
- OAuth web flow requires you to visit the provider website through your browser. This makes sense, of course, since you need to authenticate with them before you can authorize the client app. But this flow doesn’t work on mobile, in which case you need to use a different flow, one that requires you to enter your password into the untrusted app. Ugh. To date, there hasn’t been a really good mobile story here from anyone, and we’re still in the dark ages as far as mobile apps are concerned. Which is a shame, because back in my day, we used these things called web browsers.
- There are security issues you can drive a truck through. Consider this cogent explanation by John Bradley of how granting access to an OAuth client application also gives it the ability to impersonate you at *any other* OAuth client for that provider:
The problem is that in the authentication case, websites do have a motivation to inappropriately reuse the access token. The token is no longer just for accessing the protected resource, it now carries with it the implicit notion that the possessor is the resource owner.
So we wind up in the situation where any site the user logs into with their Facebook account can impersonate that user at any other site that accepts Facebook logins through the Client-side Flow (the default for Facebook). The less common Server-side Flow is more secure, but more complicated.
The attack really is quite trivial once you have a access token for the given user, you can cut and paste it into a authorization response in the browser.
OAuth is an ambitious project that has given us a glimpse at how awesome an interactive web can be. It’s just a shame that this is what we’ll have to settle for given the slow pace of improvement in such a widely used authorization framework.