Skip to content
Boston Identity

Chapter 8

OAuth 2.0

Part II · IAM Core Concepts15 min read

A thorough understanding of OAuth 2 is indispensable for professionals operating in IAM. OAuth 2 has emerged as a widely adopted authorization framework, enabling secure and delegated access to resources on behalf of users. IAM professionals need to comprehend the OAuth 2 workflow, including the roles of various entities such as clients, resource owners, authorization servers, and resource servers.

Understanding OAuth 2 grants, such as authorization code, implicit, client credentials, and refresh tokens, allows IAM professionals to design and implement secure authentication and authorization mechanisms for their systems.

Additionally, familiarity with OAuth 2 scopes and scopes management is vital for effectively controlling the level of access granted to different resources and APIs. Being knowledgeable about OAuth 2 best practices ensures IAM professionals can mitigate common security risks and vulnerabilities, such as token leakage or unauthorized access.

So, a strong grasp of OAuth 2 equips IAM professionals with the expertise to implement robust, secure, and user-centric access control mechanisms, facilitating seamless integration and interoperability across various applications and platforms.

First, before we dive into the nitty gritty of OAuth 2.0, it's very helpful to have a brief understanding of why OAuth was created.

Key Points

  • OAuth 2.0 Motivation:
    • Addresses the issue of secure privilege delegation.
    • Eliminates the need to share user credentials with third-party applications.
  • Key Concepts:
    • Resource Owner: The user owning the protected resources.
    • Client: The application requesting access to resources.
    • Authorization Server: Issues access tokens.
    • Resource Server: Stores and protects resources.
    • Access Token: Credential for accessing resources (often a JWT).
    • Grants: Workflows to obtain access tokens.
    • Scope: Defines the extent of access granted.
  • Workflow:
    1. Client redirects the user to the Authorization Server.
    2. User authenticates and grants consent (if required).
    3. Authorization Server issues an access token to the Client.
    4. Client uses the access token to access resources on the Resource Server.
  • Client Registration:
    • Clients must register with the Authorization Server, providing Client ID, Client Secret, and Redirect URIs.
  • Grant Types:
    • Authorization Code: Browser-based flow for user authorization.
      1. User authenticates and grants authorization.
      2. Authorization Server issues an authorization code.
      3. Client exchanges the code for an access token (and refresh token).
    • Client Credentials: For machine-to-machine communication.
      1. Client authenticates using Client ID and Client Secret.
      2. Authorization Server issues an access token.
    • Other grant types: PKCE, Device Code, Refresh Token, Implicit (deprecated), User Password (deprecated).

Issues Before OAuth?

OAuth (Open Authorization) 1.0 was invented in 2007 to fix the above issue and later in 2012, OAuth 2.0 was published to fix some critical issues in OAuth 1.0. Note OAuth 2.0 is not backward compatible with OAuth 1.0. Also, OAuth 2.1 is being drafted to consolidate the functionality in OAuth 2.0 and other new usage patterns.

So what causes the invention of OAuth protocol? The OAuth protocol was invented to solve the problem of privilege delegation. Let's first look at an example to understand what privilege delegation is.

As in the below diagram, a user wants to use an Email Management Application to manage his two email accounts so that he doesn't need to login each email account separately. In this case, the Email Management App will need to access his two email accounts (Gmail and Yahoo Mail), but the question is how.

Diagram: Issues Before OAuth?

In the old days, the user would need to give his username and password for each of the two email accounts to the Email Management App, so that the app can login and retrieve emails from those two email providers. This is obviously NOT a good way considering user privacy and security. For example:

  • Password could be breached
  • User doesn't know whether the Email Management App is doing other things rather than just retrieving the emails
  • When the password of Gmail and Yahoo Mail is updated, the user needs to update the password information in the Email Management App as well, which is difficult to maintain when the user has more email accounts

When the above problem became prevalent online, some internet standard organizations started to design a protocol to fix the issue.

How OAuth Fixes the Issue

The genius idea here is to add another entity or party (Authorization Server) to the above diagram (see below).

Diagram: How OAuth Fixes the Issue

An Authorization Server is added to the flow and in this case, the user doesn't need to give the credentials to the Email Management App anymore. Why? Because the Authorization Server will issue a token (called Access Token) to the Email Management App and the app will use that token as credential/proof to access Gmail and Yahoo Mail. Of course, the Access Token needs to be verified.

So instead of exposing the personal password to the Email Management App, the end user authenticates into the Authorization Server. Then the question is why is this a better situation? As you are still exposing the personal password to the Authorization Server.

From a view with a larger scope, this Authorization Server is usually not some random server, but rather one with Authority in a given context. For example, in a company's internal network, the company can establish a formal Authorization Server for all OAuth related flows and in this case, we can trust this Authorization Server within the scope of the company's internal network.

Furthermore, another crucial benefit is that when the user authenticates to the Authorization Server, he could just use one user account (such as his company credential) for accessing different Applications. Here we are talking about the Email Management App, but in the real world, there could be hundreds of apps like this and they can all rely on this Authorization Server with the one user account for each user.

As you can see, the critical part is the introduction of the Authorization Server that changes the privilege delegation game and this is why a lot of times, people will say OAuth is a delegation protocol.

Some Terminology

Note Resource Owner, Client, Authorization Server and Resource Server are the four main roles in the OAuth 2.0 protocol and we will talk more about them later.

Diagram: Some Terminology
  • JWT - A JWT (JSON Web Token) is a method for representing claims securely between two parties as defined in RFC 7519.
  • Resource Owner - The end user who owns some resources (e.g. The user owns Gmail and Yahoo Mail accounts)
  • Client - An application that will access protected resources (e.g. Email Management App)
  • Authorization Server - The server that issues Access Token
  • Resource Server - A server that hosts protected resources (e.g. Gmail server and Yahoo Mail server)
  • Access Token - A self-contained string used to access protected resources. JWT Token is the most popular. In the pic below, the left side is a JWT token and the right side are decoded values.
  • Grants - Different workflows to get Access Token
  • Scope - A way to limit the amount of access to the protected resources

Roles and High-level Workflow

Understanding roles will help us to better understand OAuth 2.0.

There are four roles in OAuth 2.0: Resource Owner, Client, Authorization Server and Resource Server, as shown below.

Diagram: Roles and High-level Workflow

Let's look at the OAuth 2.0 workflow at a high level.

The OAuth 2.0 workflow starts with the end user (Resource Owner) trying to use Email Management App (Client). In order for the Email Management App to work or fulfill its functionalities, it needs to access Gmail and Yahoo Mail server (Resource Server) for the user's email resources.

However, without proper credentials, the Email Management App just can't access those resources, as they are protected. The credential that the client needs is the Access Token and in order for the client to get the Access Token, the client redirects (usually a browser http 302 redirect) the user to Authorization Server, where the user will get authenticated.

Note OAuth 2.0 standard does NOT specify what kind of authentication method to be used, so any method that suits the context will be fine. This is another reason why people say OAuth 2.0 is a Delegation Protocol, but NOT an Authentication Protocol.

After the user is authenticated, the Authorization Server might present a consent page asking the user whether the user will give the listed permissions to the Client App to access the resources. Below is an example. The workflow will be terminated if the user chooses deny or cancel.

Diagram: Roles and High-level Workflow

This consent page is NOT required in the flow. Many Authorization Servers support 'implicit-consent' mode so that only user authentication is required.

After user authentication and optional consent is finished, the Authorization Server will return an Access Token (The Access Token in practice is usually a JWT token, but it doesn't have to be) to the Email Management App (Client). Then the Email Management App sends a request to the Gmail Server (Resource Server) with the Access Token telling that it needs those emails the end user (Resource Owner) possesses. The Gmail Server checks the request and verifies the Access Token. After that, the Gmail Server returns those resources to the Email Manage App and the workflow is finished.

One thing to mention is that the OAuth workflow doesn't have to involve a user. In this case, the client triggers the OAuth 2.0 flow by itself and will get an access token for its own usage. This is often named as machine-to-machine call.

Diagram: Roles and High-level Workflow

From an even more abstract view, the whole point of the workflow is to get an Access Token from the Authorization Server so that the client can access the Resource Server for the protected resources.

Please also note, this is just a high-level process of the OAuth 2.0 workflow and in practical situations, there are more details and tweaks going on.

Client Registration

Before the client can interact with the Authorization Server, it needs to be registered in the Authorization Server to provide the following details:

  • Client ID
  • Client Secret
  • Redirect URI or Callback URL (could be multiple)

Client ID and Client Secret are similar to Username and Password. The former is used for Application and the latter is used for human users. By providing the client id and secret to the Authorization Server, it knows the client is a legitimate one to trigger the OAuth flow.

The Redirect URI is where the Authorization Server will do a http browser redirect to after the user authenticates in the Authorization Server.

OAuth 2.0 Grant Types

In part I, we talked about the OAuth 2.0 high level workflow. Now we will dive in with more details and one important part is to understand different grant types. Different grant types are just different workflows to get an Access Token and based on the scenario, we should pick the proper grant type.

Here is the list of Grant Types:

  • Authorization Code
  • Client Credentials
  • PKCE
  • Device Code
  • Refresh Token
  • Implicit (deprecated)
  • User Password (deprecated)

The first two, Authorization Code and Client Credentials, are the most often used. Authorization Code is used when an End User is involved (user-to-machine flow) while Client Credentials is used when NO End User is present (machine-to-machine flow).

The last two, Implicit and User Password are legacy flows and will be deprecated in the future.

In this post, we will go through the Authorization Code and Client Credentials workflow, as those two are crucial to understand OAuth 2.0.

Authorization Code Grant

The Authorization Code grant type is the most commonly used. There are mainly two parts in the process:

User authenticates to Authorization server and Authorization Server returns Authorization Code to the Client (Browser based)

Client uses Authorization Code to exchange an Access Token (No Browser)

Part one will be based on browser redirection and part two will be an Http Post where NO browser is involved.

Note, some Authorization Server (such as ForgeRock) supports step 1 - getting Authorization Code without Browser, meaning that Client can make a REST call to its endpoint providing the same parameters as payload and get back the authorization code. This depends on the Authorization Server.

Diagram: Authorization Code Grant

Step 1. User Accesses the Client.

User interacts with the Client app, which will trigger an Authorization Code grant workflow.

Step 2. Client Starts the Authorization Code

Remember the first part of the flow is browser-based, so it will start with an URL with some parameters.

https://authorization-server.com/oauth/authorize?response_type=code&client_id=test-client123&redirect_uri=https://oauth-app.com/callback&scope=read&state=PPKswuzDbVL06ankyPO6FoVwi1CdysR5

Notice that https://authorization-server.com/oauth/authorize is the Authorization Server address with the endpoint to start the flow. This is defined by the Authorization Server itself.

  • response_type=code - This tells the Authorization Server that the Client App is initializing the Authorization Code flow
  • client_id - The registered Client Name/ID from the Authorization Server
  • redirect_uri - The URI where Authorization Server will redirect to after authenticating the user
  • scope - One or more strings (separated by space by default) indicating which permissions the application is requesting
  • state - A random string to keep track of current OAuth 2 flow transaction; the same value will be returned by the Authorization Server; this is to prevent CSRF attacks

Step 3. User Authentication

In this step, User needs to log in to the Authentication Server. OAuth 2.0 does NOT specify what kind of authentication method is required, so it all depends on the need. Sometimes a simple username/password will do, while other times, MFA (Multi-Factor Authentication) is needed.

After the user is authenticated, Authorization Server might present a consent page to list out permission that the Client App is requesting to access. If the user denies it, the flow will be terminated.

The consent page is optional and a lot of times, the Authorization Server can turn it off to indicate an 'implicit-consent'. This suits some use cases well.

Step 4. Authorization Server Returns the Authorization Code to the Client App

The return of the Authorization Code happens in the URL parameter and will be an Http browser redirect.

https://oauth-app.com/callback?code=2ummcKJvTt3ocgeI3RtdUOEWtZ6wMhmT&state=PPKswuzDbVL06ankyPO6FoVwi1CdysR5
  • code - This is the authorization code generated by the Authorization Server; it is usually short-lived (between 1-10 mins) and will be invalid once used
  • state - This will be the same value from the request; if the Client App sees a different value, the flow should be terminated

Step 5. Exchange Authorization Code for an Access Token

Once the Authorization Code is retrieved by the Client App, it will make an Http POST request to the corresponding Authorization Server endpoint to exchange the Authorization Code for an Access Token.

To send out the POST request, you can use CURL from the command line or REST Client Tool like POSTMAN. The POST request will look like below:

POST /oauth/token HTTP/1.1

Host: authorization-server.com

grant_type=authorization_code

&code=2ummcKJvTt3ocgeI3RtdUOEWtZ6wMhmT

&redirect_uri=https://oauth-app.com/callback

&client_id=test-client123

&client_secret=xxxxxxxxxx

  • grant_type=authorization_code - This tells the Authorization Server that the Client app is using the Authorization Code flow; do NOT confuse with 'response_type=code' in step 2
  • code - The same as the value returned by the Authorization Server
  • redirect_uri - The same redirect URI that was used when requesting the code; this may not be required depending on the Authorization Server
  • client_id - The same client id that is used to request the code
  • client_secret - The secret with the registered Client to make sure it is a legitimate Client

Step 6 Authorization Server Returns an Access Token

All the parameters sent by the Client App will be verified by the Authorization Server and then it will send an Http response with the Access Token in the response body.

HTTP/1.1 200 OK

Content-Type: application/json

Cache-Control: no-store

Pragma: no-cache

{

"access_token":"eyJ0eXAiOiJKV1QiLCciOiJSUzI1NiIs...",

"token_type":"Bearer",

"expires_in":3599,

"scope":"read",

"refresh_token":"eyJ0eXAiOiJKV1QiAqdFSBzjqfTGAM4B4..."

}

Note a Refresh Token is included in the response as well. Access Token usually expires in a couple of hours and then the Client App can use the Refresh Token to call another Authorization Server endpoint to get a new Access Token, without having the same End User to log in the Authorization Server again.

Step 7 Client App Access Resource Server with Access Token

Next, the Client App requests access to the protected resources on the Resource Server.

The Access Token is a bearer token, meaning it is self-contained. Any client possessing this token will be authenticated as long as the token itself is still valid.

The Access Token is put in the Http request Header with header name 'Authorization' and value 'Bearer ' (word 'Bearer' plus a space) inserted before the actual token. The word 'Bearer' is case-insensitive.

Authorization: Bearer eyJ0eXAiOiJKV1QiLCciOiJSUzI1NiIs...

Step 8 Resource Server Verifies the Access Token

Resource Server must always verify the Access Token before granting the Client App the access. Depending on the type of the Access Token, there are different ways to verify it.

One way is to send the Access Token back to the Authorization Server endpoint (introspection) and let it do the work. Another way is that if the Access Token is a JWT (a lot of times is), the Resource Server can decode the Access Token payload and verify expiration, scope, issuer as well as signature.

Token Introspection

By default, a Client can only introspect its own Access Token. Depending on the Authorization Server, a Client can introspect an Access Token issued to other Clients with some special scope values.

POST /oauth/introspect HTTP/1.1

Host: authorization-server.com

token=eyJ0eXAiOiJKV1QiLCciOiJSUzI1NiIs...

The Authorization Server will respond with the Access Token information.

{

"active": true,

"scope": "read",

"client_id": "test-client123",

"token_type": "Bearer",

"exp": 1419356238

}

There are other endpoints as well such as revoking an Access Token, Jwks and .wellknown endpoints. We'll look more on this in OIDC.

Step 9 Resource Server Returns Resources to the Client App

Once the Access Token is verified, the Resource Server will send the requested resources back to the Client App and the flow is finished.

Client Credentials Grant

The client credentials grant is used when Client App is calling some API resources hosted by the Resource Server. This is considered a machine-to-machine (or server-to-server) call as opposed in the Authorization Code grant where the user is involved.

Client Credentials Grant process is simpler than Authorization Code, where NO redirect is needed. Client App sends the request with Http POST to the Authorization Server and after verification, it will return the Access Token.

Diagram: Client Credentials Grant

Step 1. Client Initializes the Client Credentials Flow

This will be a straight Http POST call to the Authorization Server.

POST /oauth/token HTTP/1.1

Host: authorization-server.com

grant_type=client_credentials&client_id=test-client123&client_secret=xxxxxxxxxx&scope=read

Notice that https://authorization-server.com/oauth/token is the Authorization Server address with the endpoint to start the flow. This is defined by the Authorization Server itself.

  • grant_type=client_credentials - This tells the Authorization Server that the Client app is initializing the Client Credentials flow
  • client_id - The same client id that is used to request the code
  • client_secret - The secret with the registered Client to make sure it is a legitimate Client
  • scope - One or more strings (separated by space by default) indicating which permissions the application is requesting

Step 2. Return with Access Token

All the parameters sent by the Client App will be verified by the Authorization Server and then it will send an Http response with the Access Token in the response body.

HTTP/1.1 200 OK

Content-Type: application/json

Cache-Control: no-store

Pragma: no-cache

{

"access_token":"eyJ0eXAiOiJKV1QiLCJhbGSUzI1NiIs...",

"token_type":"Bearer",

"expires_in":3599,

"scope":"read"

}

Note NO Refresh Token will be included in the Client Credentials flow.

The remaining steps are the same as in the Authorization Code grant flow.