Stack Exchange API V2.0: AuthenticationPosted: 2012/01/18
The most obvious addition to the 2.0 version of the Stack Exchange API (beta and contest currently under way) is authentication (authorization technically but the distinction isn’t pertinent to this discussion) in the form of OAuth 2.0. With this new feature, it’s now possible for a user to demonstrate to a third party who they are on any site in the Stack Exchange network.
Why OAuth 2.0?
OAuth 2.0 was a pretty easy choice. For one, there aren’t that many well known authentication protocols out there. OAuth 1.0a, OAuth 2.0, OpenID (sort of), … and that’s about it.
Though we’re quite familiar with OpenID, all it does is demonstrate who you are to a consumer; there’s no token for subsequent privileged requests. Furthermore, OpenID is… tricky to consume. At Stack Exchange we make use of the excellent dotNetOpenAuth, but when you’re providing an API you can’t assume all clients have an “easy out” in the form of a library; simplicity is king.
OAuth 1.0a is something of a disaster, in my professional opinion. It certainly works, but it’s very complicated with numerous flows that frankly most applications don’t need. It also forces developers to deal with the nitty gritty details of implementing signatures, and all sorts of encoding headaches (remember, an API cannot assume that developers can hand that work off to a library). If we had no other options then we’d go with OAuth 1.0a, but thankfully that’s not the case.
OAuth 2.0’s main strength is consumer simplicity. A simple redirect, POST (or just a redirect, depending on flow), and then out pops a token for privileged requests. It does impose a little bit of additional complexity on our end, as HTTPS is mandated by the standard but extra complexity on our end is fine (as opposed to on the consumer’s end). OAuth 2.0 is fairly new, and as with OAuth 1.0a a “conforming implementation” is a matter of debate so it’s not all roses; but it’s the best of the bunch.
I explicitly modeled our implementation of OAuth 2.0 on Facebook’s, under the assumption that in the face of any ambiguities in the spec it’d be best if we went along with a larger provider’s interpretation. Not to imply that Facebook is automatically correct, but that following it is best option for those developing against our API; anyone wildly Googling for OAuth 2.0 is likely to find details on Facebook’s implementation, and it’d be best if they were also true for Stack Exchange’s.
For example the OAuth 2.0 spec calls for scopes to be space delimited while Facebook requires commas, at Stack Exchange we accept both. The spec also (for some bizarre reason) leaves the success and error responses when exchanging auth codes for access tokens in the explicit flow up to the implementation, in both cases we mimic Facebook’s implementation.
The Stack Exchange user model introduces some complications as well. On Stack Exchange you conceptually have 1 account (with any number of credentials) and many users (1 on each different site, potentially), you can be logged in as any number of users (with a cookie on each site) but aren’t really logged in at an account level. Since we didn’t want users to have to authenticate to each site they’re active on (with 70+ sites in the network, this would be incredibly unfriendly for power users) we needed to chose a single site that would serve as a “master site” and mediate logins required during OAuth 2.0 flows; we ended up choosing stackexchange.com to fill this role.
The Elephant In The Room
By now, some of you are thinking “what’s the point, it’s trivial to compromise credentials in an OAuth flow”. Simple phishing for username/password in an app, more complicated script injection schemes, pulling cookies out of hosted browser instances, etc. There are some arguments against it from a UX standpoint as well.
Honestly, I agree with a good deal of these arguments. In a lot of cases OAuth really is a lot weaker than it’s been portrayed, though I would argue that it helps protect honest developers from themselves. You can’t make any silly mistakes around password storage if you never have an opportunity to store a password, for example.
However, these arguments aren’t really pertinent in Stack Exchange’s case because we’ve already settled on OpenID for login. This means that even if we wanted to support something akin to xAuth we couldn’t, a user’s username/password combo is useless to us. So we’re stuck with something that depends on a browser short of pulling off of OpenID altogether (which is almost certainly never going to happen, for reasons I hope are obvious).