Demystifying OAuth2 (and OpenID Connect)

Hey everyone!

Today we are going to talk about OAuth2, the authorization framework that is also the base of the OpenID Connect Authentication protocol. I will focus only on the explicit flow on this post, the implicit flow should be easier to understand after this.

I implemented the OAuth2 protocol from scratch on some of the applications I work on at Bionexo (where I work currently :) ) and also tested and used some production-ready solutions that provide an OAuth2/OpenID Connect module.

Currently at Bionexo we have two providers deployed in production using OpenID Connect and OAuth2 with multiple applications using them. The best thing of using OAuth2 is that the protocol is widely used and it makes our life much easier when connecting a new application with our platform.

So yeah, OAuth2…

According to the RFC specification, the OAuth2 “authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf”. Fancy eh? It’s not as hard as it looks

Basically, OAuth2 provides a way to people allow different applications to use their resources on another application. A very common example would be when an app asks your permission to use your Facebook or Google data. Oauth2 makes this process easy and secure. How that works? Not magic:

Understanding the flow

We have four main elements on the OAuth2 framework, the user, the client, the provider and the resource server. I’m also going to explain one more important guy in the flow, the user-agent.

User

Hmm… The user? The user is someone that is trying to log-in or provide his information to other app. Also known as Resource Owner.

Client

This is the application that is trying to obtain access of the user resources on another place.

Provider

Also known as Authorization Server, this is the guy that is working as the authorization provider. This could be an Identity Server, and, for the sake of this post, this is where the user data is, also known as…

Resource Server

This is the place where the user stuff is stored, like photos, address etc.

User Agent

This is the browser, or whatever the user is using to access the application (usually a browser or a webview of some kind)

How everything I just said work together

So, let’s imagine this. Suppose you are trying to log into a wedding invitation list website called inviteeveryone.com. This website provides a way to you connect using your Facebook account and allow them to use your friends information to build the invitation list. How would that work?

First of all, Facebook must know the InviteEveryone website so he track what the website is using and provide some extra security for the user (and also generate some keys that we are going to use later on). That’s why the InviteEveryone team already registered an application on the Facebook Developers website.

For the sake of this post, let’s agree that Facebook implements the OAuth2 protocol exactly as the RFC describes it. In real life, you may see some differences, and the URLs I provided here are also not real, but I think that the RFC implementation also works, they only added more stuff and made things optional.

Here, take a look at this diagram I just did:

Looks like a lot of stuff right? So let’s break it down into small steps:

Identifying the user

The InviteEveryone website doesn’t know who you are, because you are not authenticated there. So, it kindly asks you to identify yourself. The team back at InviteEveryone doesn’t want to have to deal with user passwords and stuff, so they decided to use peoples facebook account, by providing a “Log in with facebook” button.

When you click at that button, no communication is made between the InviteEveryone and the Facebook server, the only thing that happens is that the user-agent, your browser gets redirected to the facebook log in website, with some extra parameters on the query string. This is what the request might look like:

http://www.facebook.com/oauth2/authorize?response_type=code&client_id=da3hs8v6sh47f&redirect_uri=http://inviteeveryone.com/oauth2/callback

We can see some stuff there:

  • response_type: This is the kind of response we want Facebook to give us. According to the specification, it must be code, but some providers also have the token response type, which is much more insecure.
  • client_id: This is a simple string that identifies the InviteEveryone website, so Facebook can show the fancy app icon and app name on the permissions page. Later on we are going to send another string identifying the app that you match with this one. This string is provided by Facebook when you registered your app beforehand (some providers allow to use a custom client_id).
  • redirect_uri: This is the URL where Facebook will tell the browser to go after the user authenticates (just a simple redirect). This URL must also be registered beforehand along with your application.

Still don’t know who you are…

When you get at Facebook, if you are already logged in, you might just see a white page flashing, and you will be on the next stage of the flow, or maybe an app permissions page. If you are not logged in, Facebook will ask you to provide your username and password. The cool part of this is that you are typing your username and password on the Facebook website, not on the InviteEveryone website, thus not sharing your password with anything else.

After you type your credentials, Facebook will authenticate you and (usually) show the permissions page, asking you if you really want to share all the stuff the app asked with the InviteEveryone website. If you click “OK, SHARE IT ALL” you will be redirected to the redirect_uri we talked earlier with the code parameter. It might look like that:

http://inviteeveryone.com/oauth2/callback?code=as7d7g6addsb89ghj6sdk
  • code: This is the authorization code you requested, we are going to use that to get a token from the facebook website later.

Nice, now I know he knows who you are

Now the InviteEveryone website knows that Facebook knows who you are, but he still doesn’t have any info about you. So what he must do? Yes, ask Facebook. Now that we have the authorization code, things start happening on the server side.

First of all, we need to fetch a Token. Everything we must get from a resource server must be requested using a token. To get a token, we simply request it POSTing to the token endpoint:

http://facebook.com/oauth2/token?grant_type=authorizaton_code&code=as7d7g6addsb89ghj6sdk&redirect_uri=http://inviteeveryone.com/oauth2/callback&client_id=da3hs8v6sh47f&client_secret=7f6xxhf8a99gjqtt8c

To the parameters:

  • grant_type: This is required and must be set to authorization_code. No idea why...
  • code: This is the code you just received from the Authorization Code request.
  • client_id: This is the same client_id you got when you registered your app on the server
  • redirect_uri: Again, the same URL you used on the Authorization Code request.
  • client_secret: This is something described as Client Password on the RFC. If you have registered your app, then you must prove that you are that app, and most providers ask you to send them your client_secret. This secret was also generated when you registered, and is provided to you with your client_id.

After this request, the provider will answer you with the access token, some info about the token, and it may also provide you a refresh token, that can be used to refresh the access token after its expiration time. An response might look like this:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
 {
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}

Here we have the 200 HTTP status indicating that everything went well, and some other info that the provider wants to give you, such as expiration time for the token, its type (usually “Bearer”) and maybe even some more random info.

Woaaaa, look at that shiny token!

Now that you have the token, you can request for any info of the user that he allowed you to see. Let’s imagine that Facebook has an endpoint called profile, where you can get (guess what) the user profile info, and another endpoint called friends, where you can get (you guessed it) his friends info! How do we get that stuff?

As we learned before, usually the provider send you a token of the Bearer type. Simply use that token to sign any protected resources request you want to do. You can do that by adding the Authorization: header on your request. For instance:

GET /resource/1 HTTP/1.1
Host: facebook.com/profile
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA

The server must validate this token, and if everything went well, you should get the desired information :)

Cool, but what about OpenID Connect?

OpenID Connect simply makes it easier to SSO solutions by adding a new response_type to the OAuth protocol, called id_token (it have A LOT of extra stuff actually, but this is what most solutions use). The id_token is a JSON Web Token (JWT) that contains some basic info about the user. That way you don't need to make another request to an endpoint and then get the user information. The request may look like this:

GET /authorize?
response_type=id_token%20code
&client_id=s6BhdRkqt3
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb

See the difference? The response_type id_token is added to the response types, along with code. You may also remove the code type and add any other type provided by the service. Once you have a JWT token, you can decode it and then it should look like this:

{
"sub": "248289761001",
"name": "Jane Doe",
"given_name": "Jane",
"family_name": "Doe",
"preferred_username": "j.doe",
"email": "janedoe@example.com",
"picture": "http://example.com/janedoe/me.jpg"
}

If you still need more info, OpenID Connect also specifies a new endpoint called UserInfo, where you can make a signed request and get more info about the user.

So yeah, this is it for today. I only showed you the happy flow, but the RFC also describes the error flows and other optional stuff, so if you are implementing something related to OAuth2, you should keep the RFC link opened :)

Here are some links that might help you out when implementing an OAuth2 or OpenID Connect solution:

Docs:

Cool Posts and Tutorials:

Cool projects / Libraries that will save your life:

Ruby gems

Java

node.js

Standalone

I tested some of these solutions myself, and I must say that the ruby ones are MUCH MUCH MUCH easier to use. The standalone solutions work, but usually you need to use some kind of specific server or you might need months to make it compatible to another server.

Thank you Diego Busin Poblete, Viviane França and Francisco Lopes for helping me out and reviewing this post, and thanks Bionexo where I learned most of the stuff on this post!

Thank you for your time! If you have any other good links, please share below and gimme all your suggestions and critics :D


This post was previously published on my first personal blog in July 2015, and then migrated to Medium.