A Crash Course In OAuth2.0

Sometimes it’s important to let others in 🙏

James Collerton
10 min readOct 11, 2022
Knowing me, knowing you

Audience

The purpose of this article is to give a brief overview of OAuth2.0 and its use cases. Personally, I have found it incredibly confusing trying to nail down exactly what OAuth2.0 is, and what it isn’t.

We will start off with a brief definition of the OAuth2.0 framework, and then introduce one of its extensions, Open ID Connect (OIDC). A lot of my uncertainty around the area stems from not understanding the line between OAuth2.0 and its additional parts, so hopefully this will help clear things up.

Argument

Let’s start at the start.

What is OAuth2.0?

OAuth2.0 is a delegated authorisation framework. This means it allows application A to access resources hosted by application B on behalf of a resource owner.

Let’s imagine we have a basic application, ‘Example App’. Example App (application A) would like to access some of a user’s (resource owner’s) information currently hosted on Google (application B). A screen showing Example App requesting access to the information Google holds on the user is below.

The user grants Example App access to some of their information they stored in Google (in this case their basic profile info and email address). However, how does Example App retrieve this information, and how does Google know to allow Example App access? This is the role of OAuth2.0.

To unpack the formal definition we introduce two core concepts:

  • Authentication (AuthN): Making sure you are who you say you are.
  • Authorisation (AuthZ): Making sure you are only allowed to do what you’re supposed to.

OAuth2.0 is a framework for authorisation and not authentication. It allows an application (Example App) access to resources (basic profile info and email address) hosted by another application (Google). Google might handle the authentication component, but this is separate to OAuth2.0.

OAuth2.0 cares about what you can do, but not who you are.

Basic OAuth2.0

To introduce the main components of the framework, let’s employ a popular analogy: visiting a hotel.

We (the client: a system that is requesting access to a resource) are visiting a hotel (the resource owner: whoever is in charge of what we would like to access). We go to the front desk (the authorisation server: a server responsible for giving us access to resources), perhaps show some identification (authenticating, not necessarily part of OAuth2.0), and the front desk gives us a key card (access token: a token that resource servers will check to see if we can access their resources) that we can use to try to access different parts of the hotel: the gym, the sauna or the bar (a resource: something we want access to, the scopes: what we are allowed access to according to the permissions the resource owner gave us at the start). The order in which we do these steps is known as the grant types/ flows.

The above is fairly dense, and none of this is particularly straightforward, so it may be worth going through it a couple of times!

A pictorial example of what’s going on

At this juncture it’s worth re-emphasising that OAuth2.0 is a framework with a very loose definition. Although some of the concepts may seem vague, we will concrete them in later sections using extensions of OAuth. Bear with it!

From here we have to bid our analogy goodbye in order to introduce one more piece of OAuth2.0 specific terminology: refresh tokens. These allow us to request a new access token once the original expires.

Let’s take each component in turn and examine it a little further

Client

The client is the application that requires access to protected resources. To get to resources, the client must hold the access token with an appropriate scope. They send this token to a resource server (defined shortly), have it accepted and the resource returned.

The client will also have a client ID and a client secret. There will be multiple clients all vying for access tokens from the same authorisation server (remember, lots of apps use Google to get user data), so we need a way of identifying which is which. This is the role of the client ID.

Theoretically if someone got ahold of your client ID they could pretend to be you and start making requests! To prevent this we also have the client secret, which can be sent with the ID to verify it is you.

Authorisation Server

This server takes requests from the client for tokens, issuing them according to the terms of the resource owner.

The authorisation server exposes two endpoints:

  1. The Authorisation endpoint: This handles the process whereby the user gives consent to having their resources shared. This includes limiting the scope of the resource-sharing.
  2. The Token endpoint: This handles all application to application communication for requesting tokens etc.

Scopes

Scopes are a way of limiting a client’s access to resources. An application can request a number of scopes, the resource owner then decides which scopes they would like the application to have. In OAuth2.0 there are no hard and fast rules about what scopes should look like, except for it needing to be a unique string.

Some examples of scopes include using a rest style naming convention: /users/email-addresses. We simply need a string that resource servers can interpret in order to see if the scope matches. These scopes are then included in the access token when it is sent to the resource server, such that the resource server can examine and implement them.

Resource/ Resource Owner/ Resource Server

The resource is something we would like, for example a user’s email address. The resource owner is the user or system that owns the protected resource. In our example the user themselves are the resource owners of their email addresses.

The resource server is then a server that provides access to resources following a client request with a valid access token. In this case we may have a user service sitting on a box with an exposed API which requires a valid access token as a header.

Access Token

Access tokens are pieces of data that the client can provide to resource servers to request resources. It’s not important for the client to be able to read the token, all they need to do is be able to request it from the authorisation server and provide it to the resource server. It is the resource server’s responsibility to interpret it.

In terms of security, naturally access tokens must be kept confidential! Additionally they will typically have a limited lifetime in case they are compromised or the information within the token becomes out of date.

Refresh Token

Expiring access tokens takes us nicely onto our next section, refresh tokens. As we don’t want our client to have to go to any effort to get a new access token if the old one expires (covered in the Grant Type/ Flows section), we have a second token, the refresh token. We can make requests to the authorisation server using the refresh token to get new access and refresh tokens.

This refresh token has a longer lifetime than the access tokens and must be securely stored. It’s interesting to note that the expiration time of the refresh token is never communicated to the client. If at any point the refresh token becomes invalid the only option is to force the client to start the flow again.

The most secure option for dealing with refresh tokens is to issue a new refresh token each time the previous one is is used. This is as if a refresh token has been used twice we know if has likely been copied by a nefarious party, and the authorisation server can revoke all necessary related tokens.

Grant Type/ Flow

At this point we have the main components of OAuth2.0 laid out. However, now we want to concrete how they communicate. The steps clients must take in order to get access tokens are called grants. There are several different types depending on your needs:

  1. Authorisation Code Grant: Traditional web apps. Some Single Page Apps and Mobile/ Native Apps (although Authorisation Code with PKCE is preferred)
  2. Authorisation Code Grant with Proof Key for Code Exchange (PKCE): This is similar to the above, but with additional steps to make it more secure for SPAs and Mobile/ Native apps.
  3. Client Credentials Grant: Used for non-interactive applications such as services or scheduled tasks
  4. Device Authorisation Flow: Used on devices with limited input, such as TVs.
  5. Refresh Token Grant: How we get new refresh tokens
  6. Implicit Grant (not recommended): A simplified Authorisation Code Grant.
  7. Resource Owner Credentials Grant (not recommended): The client directly uses the resource owner’s credentials (in our example that would mean taking the user’s username and password). Obviously this is pretty risky, but sometimes there are reasons to do it!

Let’s dip into them one by one:

Authorisation Code Grant

Because regular web apps are server-side apps where the source code is not publicly exposed, they can use the Authorization Code Flow (defined in OAuth2.0 RFC 6749, section 4.1)

This grant type is mainly for traditional web apps. The reason is that we can securely store the client secret. It has the rough overall choreography:

  1. Application makes a request to the authorisation server for an access token attaching their client ID, the required scopes and a redirect URI
  2. The authorisation server does any work with the user to check what resources they would like to allow access to with access tokens.
  3. The authorisation server then redirects to back to the URI with an authorisation code

A more in-depth coverage is found here and here.

Authorisation Code Grant with Proof Key for Code Exchange

This grant type is mainly for SPAs and Mobile/ Native apps and is designed for when we can’t securely store the client secret.

This differs to the previous grant flow in that initially the client generates creates a random code_verifier and from this a code_challenge. Now when the client sends a request for an authorisation code to the authorisation server it includes the code_challenge.

When the client sends the authorisation code to the authorisation server it includes the code_verifier. This must match up to the code_challenge in order to get an access token, verifying that the same client sent both the request and the authorisation code.

Client Credentials Grant

The Client Credentials grant is used when applications would like an access token to access their own resources, not a user’s. In our working example perhaps our ‘Example App’ would like to update their logo in Google, or get some user stats.

Device Authorisation Flow

This is used with input-constrained devices (for example a smart TV or speaker). Unsurprisingly there are two parts to it: one on the device and one in the browser.

The device makes a request to the Authorisation server which returns a user code, URL and device code. It then begins polling to see if the user has authorised the device in the browser yet.

The user visits the URL in the browser and carries out the necessary authorisation, at which point the device’s polling returns successfully and the device gets an access token!

Refresh Token Grant

This grant can be implemented in multiple ways. Previously we covered how new refresh tokens can be returned with each access token (in which case this gets absorbed into any of the previous flows that use refresh tokens). Alternatively we can have a separate refresh token grant.

Implicit Grant (Not Recommended)

The implicit flow is not recommended, and was a simplified version of the authorisation code grant, which returned an access token without the authorisation code.

Resource Owner Credentials Grant (Not Recommended)

This resource owner credentials grant flow is not recommended (more info here) as it directly takes a user’s authentication information.

Extending OAuth2.0 with Open ID Connect

The next thing we will focus on is how to extend OAuth2.0 using Open ID Connect (OIDC).

At the very start of this page we delineated the difference between authorisation and authentication. We took great pains to point out that OAuth2.0 is an authorisation framework, and does not deal with authentication. OIDC handles authentication.

The purpose of OIDC is to give you one login for multiple sites. If you logged into ‘Example App’ using Google then you have used OIDC. In purely OAuth2.0 terms Example App has asked Google if it can have access to some of your information. OIDC extends this to say not only can Example App have access to your information, but Google confirms it is in fact you using the site and returns information on you and the context of your login for authorisation (e.g. was 2FA used).

Additionally it’s possible for a client to request more information about a user from the authorisation server using the access token.

Enter The ID Token

The way that Google communicates with the client is via an ID Token, which is in JWT format. This JWT token contains ‘claims’ which are essentially fields (issuer, expiry date, user name, user email address etc.)

A standard set of claims about the user exists here. A standard set of claims about the ID token and issuer exists here. This token works by simply including the fields in the payload.

Conclusion

In conclusion we’ve jumped headlong into OAuth2.0 and how it works, as well as covering OIDC, a commonly found extension.

--

--

James Collerton

Senior Software Engineer at Spotify, Ex-Principal Engineer at the BBC