Last June was an exciting time for the engineering team here at Indiegogo. In just over a month, we would release our first client, a sleek iOS app. We sat around a conference table trying to decide how we were going to persist user sessions. “We’ve begun work on extending the legacy one-time-use token system for mobile login”, everyone cringed at the thought. Though it seemed simple enough, extending the existing token system was becoming infeasible. The token system had once been built to allow logins through email links. We had to add basic functionality like revocation and the granting of multiple tokens. But not only was the code difficult to extend, our work seemed to be opening new vulnerabilities as well.
We sat around the table lamenting pulling it off in time for the release date. But then Dan suggested OAuth 2.0, and I wondered: “What does Facebook login have to do with our token system?”
Many may think that OAuth 2.0 is just a way to log in to one site with credentials from elsewhere. The OAuth 2.0 RFC calls this Twitter/Facebook login the “Authorization Code” flow. As we discovered, the Authorization Code flow is only one of four flows in OAuth 2.0.
When a trusted client (a mobile app) needs to make authenticated requests to a resource (an API), one should use the Resource Owner Password Credentials flow. This perversely labeled flow is actually rather straightforward, in it:
- The app requests the username and password from the user (or “resource owner”)
- The app then passes the credentials to an authorization server (e.g. our API)
- The app responds with a token which can be used instead of a password in further requests
The primary benefit of using this flow is that we can keep users logged in without storing their credentials on the phone. The iOS app just retains an access token which regularly expires, and which can be revoked if compromised.
Additionally, using an established OAuth 2.0 flow provides many other benefits,
- It is robust: countless security and web engineers have scrutinized and tested the spec for several years.
- It is comprehensive: the authors considered a diverse set of use cases and created flows to cover them.
- It is extensible: when new use cases become popular, users of OAuth 2.0 will augment the spec to handle them.
- It is convenient: there is already a proven implementation of OAuth 2.0 in most languages. Most implementations provide basic tasks like token revocation, generation, and renewal.
- It’s proven: while OAuth 2.0 is still in request for comments, it is already used in countless established applications. If anyone discovers serious problems with the protocol, the community will swiftly resolve them. The same cannot be said of our legacy token system.
After our discussion, it was pretty clear that we were to build an OAuth 2.0 provider. Building our provider, oauthservice, was a snap thanks to a Rails gem, Doorkeeper.
As we consider any iOS app to be public, as the binary can be downloaded and inspected by anyone, no baked-in secrets are in fact secret. So in our implementation, we chose to drop the optional client ID and secret from the flow. This means that anyone can use our token granting API, but without user credentials there’s not much a malicious user can do. Additionally, we do all communication over SSL, so credentials are never sent in the clear.
In the end, we were glad not to roll our own token system. With OAuth 2.0, someone already did the hard work. And with all our saved time, we wrote this fun blog post!