Security‎ > ‎

The Complete Guide to OAuth 2.0 and OpenID Connect Protocols

posted Sep 20, 2020, 3:01 PM by Chris G   [ updated Sep 20, 2020, 3:02 PM ]


The Complete Guide to OAuth 2.0 and OpenID Connect Protocols

Find out how the most widely-used protocols for authentication and authorization really work

Cover Image
Photo by James Pond on Unsplash

We’ve all seen the “sign in with Google” and “connect to Facebook” buttons on websites and mobile apps. Click the button and a screen opens that says, “This app wants to access your public profile, contacts…” and asks you whether you want to give access. This is OAuth at a high level. Understanding these protocols is crucial for every software engineer, security expert, and even hacker.

TL;DR

A complete guide to OAuth 2.0 and OpenID Connect, the two most widely used protocols on the internet today for authorization and authentication. OAuth 2.0 is used for authorization and OpenID Connect is used for authentication. There are two most common OAuth 2.0 authorization flows, authorization code flow for server-side applications, and implicit flow for browser-based applications. OpenID Connect is an identity layer on top of the OAuth 2.0 protocol to make OAuth suitable for the authentication use cases.


Why OAuth?

To understand the reason for the birth of OAuth we need to understand a term called Delegated Authorization.

Delegated authorization

Delegated authorization is an approach to allowing a third-party application access to a user’s data.

Two approaches to delegated authorization

There are two approaches for delegated authorization, whether you give the third-party application your account password so they can login into your account on your behalf and access your data or you grant the application to access your data using OAuth without giving your password (and none of us will give our password!).

Now we know the need and importance of OAuth, let’s dive deeper into the protocol.


What is OAuth?

OAuth (Open Authorization) is a standard protocol for delegated authorization. It allows applications to access user data without the user’s password.

OAuth 2.0 terminology

Understanding this protocol requires us to understand its terminology:

  • Resource Owner: The user who owns the data that the client application wants to access.
  • Client: The application that wants access to the user’s data.
  • Authorization Server: The authorization server authorizes the client to access a user’s data by granting permission from the user.
  • Resource Server: The system that holds the data that the client wants to access. In some cases, the resource server and authorization server are the same.
  • Access Token: An access token is the only key that the client can use to access the granted data by the user on the resource server.

Take a look at this terminology in the OAuth 2.0 abstract flow:

OAuth 2.0 Abstract Flow
Image Source: Author

The authorization key/grant can be of type code or token. We’ll learn about different authorization grants later. Now, let’s take a look at the authorization flow in detail:

  1. The user starts the authorization flow, usually by clicking a button like Connect with Google or Facebook or any other service.
  2. The client then redirects the user to the authorization server. While redirecting the client sends information such as the client id and the redirect URI.
  3. The authorization server handles user authentication and shows a consent screen, granting permission from the user. If you’re signing in with Google you have to provide your login credentials to Google — i.e. accounts.google.com and not the client.
  4. If the user grants permission, the authorization server redirects the user to the client with the authorization key (code/token).
  5. The client then requests the resource server with the authorization key included, asking the resource server to respond with the user’s data.
  6. The resource server then validates the authorization key and responds with the requested data to the client.

That’s how users give third-party applications access to their data without giving them the password. At this point, the following questions arise:

  • How do we limit the client to access only some part of our data on the resource server?
  • What if we want the client to only read our data and not change it?

These questions take us to another important piece of OAuth terminology: scopes.

Scopes in OAuth

Scopes in OAuth 2.0 are used to limit an application’s access to a user’s data. By issuing an authorization grant that is limited only to the scopes granted by the user.

When the client makes a request to the authorization server for authorization grant, it sends a list of scopes with it. The authorization server uses this list of scopes to generate a consent screen and grants permission from the user. If the user agrees to the consent screen, the authorization server issues a token or authorization code that is limited only to the scopes granted by the user.

For instance, if I grant a client application to see the list of my Google contacts then the token issued to the client by the authorization server can’t be used to delete my contacts or see my calendar events — it’s only scoped to read my Google contacts.


Setup for OAuth 2.0

Before moving on to OAuth flows, it’s good to know some prior OAuth configuration. When the request for authorization grant is initiated, the client sends some configuration data to the authorization server as query params. The basic query params are:

  • response_type the type of response we want to get from the authorization server.
  • scope list of scopes that the client wants access to. This list is used by the authorization server to generate a consent screen for the user.
  • client_id is provided by the authorization service when setting up the client for OAuth. This ID helps the authorization server to determine the client who is initiating the OAuth flow.
  • redirect_uri tells the authorization server where to go when the OAuth flow is completed.
  • client_secret is provided by the authorization service. This parameter may or may not be required, based on the OAuth flow. We’ll see its importance in the authorization code flow.

Understanding Different OAuth Flows

The two most commonly used OAuth 2.0 flows are authorization code flow for server-based applications and implicit flow for pure JavaScript Single Page Applications (SPAs).

To explain the OAuth flows, I’m considering Google as the OAuth service provider.

Authorization code flow

The authorization code flow or authorization code grant is an ideal OAuth flow which is considered to be highly secured because it uses both the front-end channel (browser) and the backend-channel (server) to implement the OAuth 2.0 mechanism.

OAuth 2.0 Authorization Code Flow
Image Source: Author

The client begins the authorization sequence by redirecting the user to the authorization server with response_type set to code this tells the authorization server to respond with an authorization code. The URI for this flow looks like this:

https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
client_id=your_client_id&
scope=profile%20contacts&
redirect_uri=https%3A//oauth2.example.com/code

In the above request, the client is requiring the user’s permission to access his public profile and contacts by defining them in the scope parameter of the request. The result of this request is an authorization code, which the client can exchange for an access token. The authorization code looks like this:

4/W7q7P51a-iMsCeLvIaQc6bYrgtp9

Why exchange code for a token?

An access token is the only thing that can be used to access data on the resource server, not the application code. So why is the client set response_type to code when it actually needs an access token? The reason is to make the OAuth flow highly secure.

OAuth 2.0 Authorization Code Flow broken into Client browser & server to explain the reason behind exchanging code for token
Image Source: Author

Problem: An access token is a secret piece of information that we don’t want someone to access. If the client requests an access token directly and stores it in the browser, it can be stolen because browsers are not fully secure. Anyone can see the page source or potentially use dev tools to acquire the access token.

Solution: To avoid exposure of the access token in the browser, the front end channel of the client gets the application code from the authorization server, then it sends the application code to the client’s back end channel. Now to exchange this application code for an access token, a thing called client_secret is needed. The client_secret is only known by the client’s back-end channel, the back-end channel then makes a POST request to the authorization server with application code and client secret included. The request might look something like this:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=4/W7q7P51a-iMsCeLvIaQc6bYrgtp9&
client_id=your_client_id&
client_secret=your_client_secret_only_known_by_server&
redirect_uri=https%3A//oauth2.example.com/code

The authorization server validates the client secret and application code and responds with an access token. The back end channel stores the access token and potentially use this token to access a resource server. In this way, the browser cannot access the access token.

Implicit flow

The OAuth 2.0 implicit flow is used when you don’t have a back end channel and your website is a static site that uses only the browser. In this case, you skip the last step that happens on the backchannel when you exchange the application code for an access token. In the implicit flow, the authorization server responds with an access token right away.

OAuth 2.0 Implicit Flow
Image Source: Author

The client redirects the browser to the authorization server URI to start the authorization flow with response_type set to token. The authorization server handles the user's login and consent. The result of the request is an access token that the client can consume to access a resource server.

Implicit flow is considered less secure because the browser is responsible for managing the access token, so it could potentially be stolen. Still, it’s widely used for single-page applications.


Authentication vs Authorization

As we know, OAuth solves the delegated authorization problem, but it doesn’t provide a standard way to authenticate the user. You could say that:

  • OAuth 2.0 is for authorization.
  • OpenID Connect is for authentication.

If you’re confused by these terms, here’s the difference between them:

  • Authentication is the assurance that the communicating entity is the one claimed.
  • Authorization is the process of verifying whether the communicating entity has access to the resource.

In other words, authentication cares who you are, authorization cares what permissions you have.


OpenID Connect

OpenID Connect is an identity layer on top of the OAuth 2.0 protocol. It extends OAuth 2.0 to standardize a way for authentication.

OpenID Connect extending OAuth 2.0
Image Source: Author

OAuth does not provide user identity right away but rather it provides an access token for authorization. OpenID Connect enables the client to identify the user based on the authentication performed by the authorization server. This is achieved by defining a scope named openid when requesting the authorization server for user login and consent. openid is a mandatory scope to tell the authorization server that OpenID Connect is required.

The URI for OpenID Connect authentication request made by the client looks like this:

https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
client_id=your_client_id&
scope=openid%20contacts&
redirect_uri=https%3A//oauth2.example.com/code

The result of the request is an application code that the client can exchange for an access token and ID token. If the OAuth flow is implicit then the authorization server responds with an access token and an ID token right away.

The ID token is a JWT or JSON Web Token. A JWT is an encoded token that consists of three parts: header, payload, and signature. After acquiring the ID token, the client can decode it to get the user info encoded in the payload part — like this:

{
"iss": "https://accounts.google.com",
"sub": "10965150351106250715113082368",
"email": "johndoe@example.com",
"iat": 1516239022,
"exp": 1516242922
}

Claims

The payload of the ID token contains some fields known as claims. Basic claims are:

  • iss token issuer.
  • sub unique identifier for the user.
  • email user’s email.
  • iat token issuing time represented as Unix time.
  • exp token expiration time represented as Unix time.

However, claims are not limited to these fields. It’s up to the authorization server to encode claims. The client can use this information to authenticate the user.

If the client needs more user information, the client can specify standard OpenID Connect scopes to tell the authorization server to include the required information in the ID token’s payload. These scopes are profileemailaddress, and phone.


End Note

It’s always good to practice what you have learned. To play with OAuth 2.0 scopes, authorization codes, and tokens go to Google OAuth 2.0 Playground.

Comments