As you already know, there’re tons of tutorials and resources out there on the Internet trying to explain OAuth 2.0 and OpenId Connect to people, so why do I bother writing yet another article on these protocols?
The answer is a bit similar to the situation when you’re trying to figure out what a Monad is because you’re interested in functional programming, but most of existing articles about Monad simply give some weird terminology in category theory or strange image illustration, without addressing real problems that Monad solves. So if you’re like me, you’ve heard of OAuth 2.0 and OpenID Connect, or you have the opportunity to use them in your projects for the first time, you’ll probably feel like being in a great mystery. Just like above, there is so much confusing information on the Internet on how these work, that most of the time we cannot understand the WHY of these protocols before getting into them.
So the purpose of this article is to discuss the raison d’être of OAuth 2.0 and OpenId Connect and demystify some common misconceptions about them.
History: Please Give Us Your Data
A long time ago, some application would like to gather more user data so they could expand their service, develop advertising campaigns, or simply do some science stuff to better understand their users. There’re some good ways and bad ways to achieve the goal.
For example, Dropbox in the early days wanted to acquire as many users as they could, by simply asking you to invite your friends to join their service. So they established a simple referral program like the following:
Still users had to provide their friends list manually.
Today if you have a Dropbox account, registered with an email (for example @yahoo.com), there’re some more possibilities to invite your friends:
If you would like to do so, just click on the left button “Invite your Yahoo! contacts…” et then it’s up to you to agree or not:
But in the old days, other services like Yelp did it in another but inefficient way:
You might realise that no matter how trusty Yelp may be, giving your email password to them is like giving your home key to someone so that he or she could enter your house just to get your address book. Not a great idea.
Notice that I do not intend to break down the image of Yelp, I just want to give a bad example on how to get user data by asking their password.
So arose the delegated authorisation question, that is how we could provide a service with a little bit of our data without giving it our precious password.
I believe this is the main reason why OAuth was invented and it is so-called authorisation protocol since then.
Proliferation of OAuth
So we’re here at the end of 2018. There’re innumerable applications using OAuth 2.0, being considered the standard way to do data access delegation.
But it’s when several problems arise.
Imagine you’re interested in OAuth 2.0 and you want to use it in your frontend React or Angular application. You try to google on how to setup OAuth 2.0 and all you get is examples about authenticating a user using OAuth 2.0.
Or you might luckily found a small precious tutorial on getting started OAuth 2.0 with React. But after some hours struggling to make the first request work, your backend colleague might come to you indicating that this wouldn’t be the good way to do it, maybe use OAuth 2.0 with Node and Passport.js instead. Accordingly, you probably spend a lot of time reading the Passport documentation which assumes that you’re already familiar with OAuth 2.0…
If you’re newbie to OAuth, you’ll probably have similar experience.
But this is not the worst case. I personally had an opportunity to maintain a custom built-in legacy OAuth service, the thing I wish our team wouldn’t have built again. Normally, for such a service the best way is to use external identity services like Okta or Auth0 so that you have much more time focusing on your business first.
So what is so confused about OAuth?
OAuth 2.0 Terminology & standard flow
From my point of view, this is where most tutorials differ from one another. To fully grasp the these concepts, let’s look at the most complete and most secure way to make use of OAuth 2.0 (also known as Authorization Code scenario):
From the schema above, we can associate OAuth 2.0 terminology to each element:
- Resource Owner: you and me most of the time.
- Client: a web application toto.com which want to access our data.
- Authorisation Server: server which approves or not our consent. For example Google Authorisation Server.
- Authorisation Code (or Authorisation Grant): a time-limited verification code. This is used to exchange with the authorisation server for an access token.
- Access token: reference token which allows toto.com to access user data.
- Resource Server: physical server which contains our data. Based on the access token AND the scopes (permissions) toto.com initially asked for, it will do some verification in order to allow or deny a request to it.
For those who read this article, there are no need to describe the OAuth 2.0 flow because it could be found on a bunch of other resources on the Internet. I will not rewrite yet another description on it.
I would like just to call your attention to the steps 3 and 4 on the schema above. One might ask why we have to make so many requests back and forth just to get an access_token. Why don’t the authorisation server just give us directly the access_token from step 3?
To understand that, let’s demystify two possible communication channels between parties:
- Front channel (less secure): communication happens through the browser to the authorisation server. These are steps 1, 2, 3. Technically all information exchanged on these steps aren’t considered sensitive information, even the authorisation code. The latter is usually returned on the query parameter and somebody sitting behind you can get it by looking at your browser.
- Back channel (highly secure): communication from toto.com backend server to the authorisation server. These are steps 4 and 5. When exchanging the authorisation code for an access token, the backend server usually include a client secret to prove that it’s the toto.com backend who made the exchange request. The client secret can be obtained along with the client id when first registering toto.com on the authorisation service provider. Most of the time, we don’t want to store this client secret in frontend application because it’s considered sensitive information. Notice that the access token has to be protected too. That’s why we prefer these communications happen on the back channel.
Another point that I found pretty common is that most of the time, tutorials on OAuth 2.0 begin with a Single Page Application (SPA for short) and recommend using Implicit Flow. In such a scenario, you won’t have any back channel communication, and thus no authorisation code, just an access token returned directly from the authorisation server. As explained above, this is not secure and might prevent your team from using the refresh token mechanism. So just don’t use it anymore.
Why OpenId Connect?
If you try to google OpenId Connect, you probably find a bunch of resources explaining difference between authorisation and authentication. Personally it just drives me crazy for a while because academically everyone knows the distinction between these two concepts, but in practice what is the technical difference between when we implement OpenId Connect and OAuth 2.0?
Let’s say we want build up a web page that let people log in using their social network credentials. Nearly half of tutorials found try to use OAuth 2.0 and we end up in a situation where OAuth was invented to solve the delegated authorisation problem and it’s used for authentication as well.
Notice that OAuth is about scope (permissions) to access our data, it doesn’t care about who we are. There’re no standard way to get user information because scopes provided by different social networks aren’t the same. Each social network implements the login button on their own to have their specific way to get user information. So this is one of the reasons it could be confused when reading OAuth implementation docs from Google or Facebook or Twitter.
So in the effort of making an extra authentication extension on top of OAuth 2.0 to be used when needed, OpenId Connect came out as a result. Typically, for those who have already OAuth 2.0 in place, they need just to do a little extra stuff to make authentication work the way it should be.
Below is what difference OpenId Connect brings to the table:
- Id Token: encoded string which contains some user information.
- User Info Endpoint: get more user information if information from id token isn’t enough.
Notice that OpenId Connect flow is the same as OAuth 2.0 flow, with the exception of openid passed in the scope.
Notice that OAuth 2.0 specification does not enforce the access token format. In OpenId Connect, id token is usually a JWT (JSON Web Token). The difference between these two is: access token is some sort of reference token whereas id token is a stateless one. With access token, we need to send it to an external server to get data, so the external server has some mechanism to maintain a state of this access token to be able to perform the answer. On the contrary, with a JWT token, we can decode it to get user information, make some validation on its signature without any help from an external server.
A final point to finish, many web applications today try to use OAuth 2.0 to access our data. Just pay attention to the scopes they ask for before allowing them to use our data. Below is an example when registering an Dropbox account with a Google email:
They tend to ask for too much permission, so don’t hesitate to refuse that. Sometimes, a good old registration form is rather the good choice when you don’t want to give away your data.
I hope that through this long adventure you will have some clear idea about how OAuth 2.0 and OpenId Connect work and especially why they are invented. I list below excellent resources on OAuth 2.0 without these I could not accomplish this article. Thanks for your attention and please let me know if some points aren’t clear enough for you.