Understanding OAuth2

Explaining the concepts, use-cases, and possibilities of the OAuth2 protocol

Albert Starreveld
The Web Application Security Hub

--

OAuth stands for Open Authorization. OAuth is an open standard for access delegation. The OAuth protocol was developed as a solution for granting access to a limited set of resources for a predefined period of time without sharing usernames and passwords.

OAuth has been widely adopted and may very well be the most used authentication protocol for web applications. Also, cloud vendors heavily rely on this technique.

Most of the OAuth2 documentation is very abstract, but it is essential to understand what OAuth2 exactly is because it is easy to implement it incorrectly, and as you can imagine, this can have a significant impact.

This article covers the following topics:

  • What OAuth is for
  • The different use-cases and where and how to apply it
  • Do’s and don’ts

Terminology

To get a good understanding of what OAuth is, let start with exploring what- and who is involved in obtaining accessusing the OAuth protocol.

The client

When dealing with OAuth, you’ll often come across the terms “client” or “client_id.” In this context, the client does not refer to an end-user or customer.

The term “client” does not mean “end-user” or “customer” in this case. Instead, the term “client” is used to refer to the application that obtaining access to the protected resources.

It is possible that several different clients want access to the same resource.

Resources

A client has resources which are protected with the OAuth protocol. Often, with resources, we mean API-endpoints.

The authorisation server

The authorization server is where the client requests the end-user to authenticate and obtain a token that represents the scope of permissions granted to the client. During this process:

  • The user provides their username and password on the authorization server.
  • The user can consent or deny access to specific resources based on the application’s request, though this feature may be disabled in some cases.

Only registered clients can request end-users to authenticate, ensuring secure access management.

The end-user

OAuth is designed to grant access to resources on behalf of someone or something. The end-user can be a person, but the protocol also supports machine-to-machine communication.

This is why there is not too many emphasis on the term “end-user” in the documentation.

A typical use-case

As you may have read by now, the way the OAuth protocol is described is quite abstract. To make it tangible, the following diagram shows how the OAuth protocol is typically implemented:

OAuth is very often used to protect API endpoints. In many cases, these endpoints are being accessed by a SPA.

When the browser opens the SPA, the SPA will probably want to communicate with the API. To do so, it creates an access-request to the authorisation server. (It navigates the browser to https://{authorisation-server}/authorize?xyz…).

The protocol has been designed to prevent the application from communicating with end-users directly. Infact, as a rule of thumb the application should not even be trusted to do so. The authorization server is optimised to communicate with end-users securely.

After the user has authenticated, conceptually, either the authorisation-server or the user gives consent to use a certain scope of endpoints the SPA may access. This manifests in a token

This token is then included in the requests to the API. It uses this token to either return the requested data or respond with a HTTP-status 401.

How OAuth2 impacts solution architecture

It does not end there. OAuth2 is a versatile protocol designed to also support securing very complex, enterprise-scale application landscapes. That’s why different types of communication between applications are distinguished within the protocol:

  • Server-side web applications.
  • Single Page Applications accessing Server Side applications on behalf of the end-user.
  • Applications accessing other applications on their own behalf.

Let’s zoom in to the following model:

By distinguishing the difference between humans and machines, it is possible to do the following:

  • A user authenticates and uses the SPA to access the API in the DMZ.
  • Only the API in the DMZ may access the downstream API. It does so by using its machine-identity. The downstream API is used to acces the database that contains all customer data.
  • The API in the DMZ is allowed to request the data of every user. But, it will create a request, scoped to the end-user by using their identity.

Utilising authorisation this way makes your application landscape more flexible. By distinguishing trusted- and untrusted communication, it is possible to create a general-purpose API-layer.

Obtaining access

Every different type of communication has a different method of obtain access. These methods are reffered to as “flows” and are specified in the OAuth2- and the OpenID Connect specification. It is important to read those specs and make sure you have thorough understanding of the topic before implementing it.

A key difference between humans and machines is that a machine cannot type in its credentials or apply two factor authentication. That’s why there is a difference between the method used to obtain access.

Obtaining a token with machine to machine communication

This is really the simplest flow. In this case, the client has the credentials needed to obtain a token directly. These credentials may be the username and password or the client_id and the client_secret.

These flows are refered to in the OAuth documentation as

This is how it works:

  1. The client invokes a HTTP-request to the https://{authorization-server}/token endpoint and includes its credentials in the request. The client may in this situation be an API, a console application, or a daemon. In the example previously illustrated, this would be the API in the DMZ.
  2. The authorization responds with a token which does or does not include a users’ identity depending on which grant-type is used to obtain the token.
  3. This token can be included in the request to the resource-server.

A common mistake is implementing this method of authentication in a web context where end-users provide their usernames and passwords to the client (a SPA, for example). This is not secure. Instead, use human-to-machine flows in this scenario.

Obsolete: Human to machine — SPA

In contrast to machine-to-machine communication, in case of human-to-machine communication, the client does not interact with the resource server on its own behalf but on behalf of the end-user.

This makes obtaining access more complex:

  1. The client redirects the end-user to the https://{authorization-server}/authorize endpoint.
  2. The authorization server renders the login-page. This is where users authenticate with their usernames, passwords, and with MFA.
  3. If the user has successfully authenticated, the server redirects to the client and it includes the token in the request. (https://{client}/{configured_redirect_endpoint}#access_token=xyz&x=y&z…)
    Note that the # in this URI makes it impossible for the server-side to read the token, this way only the SPA can read the token.
  4. The SPA includes the token in the HTTP requests to the resource server.

This authentication method is referred to as the Implicit Grant Type.

Important: The general consensus is that this method of authentication is not considered secure anymore. Instead use PKCE and consider moving authentication to the server-side using the BFF-security-pattern.

Human to machine — Server Side Authentication

Implementing server-side authentication is even more complex. The OAuth protocol ensures that only one machine has the token at any given time.

Since end-user authentication is done at the client-side, and the end-user may not see the token in this case, there’s one more step required to obtain the token:

  1. The client redirects the end-user to the https://{authorization-server}/authorize endpoint.
  2. The authorization server renders the login-page. This is where users authenticate with their usernames, passwords, and with MFA.
  3. If the user has successfully authenticated, the server redirects to the client and it includes a code in the request. (https://{client}/{configured_redirect_endpoint}?code=xyz&x=y&z…)
  4. The client now exchanges this code with a secret that only it knows, in return for a token. That way, only the client (at the server side) is able to obtain a token.
  5. The application includes the token in the HTTP requests to the resource server.

The JWT

Authorization manifests in a token. This is an opaque string and can be anything. In most cases, a token is a JWT (JSON Web Token). This is what a JWT looks like:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Note it has dots. These dots separate the three sections of the token:

  • the header
  • the payload
  • the signature

All of these parts are base64 encoded JSON:

The header

When you base64 decode ‘eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9’, this is what you get:

{
"alg": "HS256",
"typ": "JWT"
}

The payload

When you base64 decode `eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ`, then this is what you get:

{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}

Note that the payload of the token contains the user-details.

The signature

The third section of the token is the signature of the token. Everybody can read and create a token. So it’s important to validate wether the token an API receives has not been tampered with. That’s why every token contains a signature that can be used to verify the authenticity of the token.

Session management

When a user has a token, the user can access resources. But at a given moment in time, the user must sign out.

To “sign out” correctly, it is important to understand the difference between the role of the authorization server and the access_token:

  • A token is valid for a given period of time, for a specific client
  • An authorization server issues tokens
  • An authorization server may issue several tokens to several clients for the same end-user
  • Depending on wether a machine or a human is signing in, there may be an active user-session on the authorization sever. This is how Single Sign-On works: If the user has authenticated before, the token is issued directly without asking the user to authenticate.

This means to sign-out successfully, from a functional perspective, you must:

  • Revoke any token that has been issued
  • End the user-session on the authorization server

Risks and risk mitigation

OAuth has quite a history. Security is cat-and-mouse game. First, a protocol is designed and implemented, then hackers find and exploit vulnerabilities, resulting in the protocol being patched as a response to that.

Since the OAuth protocol is very generic, it has become very complex and abstract. As a result, it is easy to misinterpret the documentation. With dire consequences. Without proper understanding and attention to detail, it is easy to implement it in a vulnerable way. Read this list of common OAuth vulnerabilities.

That’s why you should never implement an OAuth server yourself.

  • Use an Identity Provider as a service. (Like IdentityServer, Auth0, Azure Active Directory, and so forth.)
  • Or download and modify an OpenID Connect approved implementation here.

Furthermore, choose carefully which grant type applies in your case. Consider carefully what the likeliness of it being hacked is, and what the impact is.

Conclusion

OAuth2, an open standard for access delegation, has a complex history and is widely adopted in web applications and cloud services. Understanding the protocol is crucial, as incorrect implementation can lead to significant consequences.

While OAuth2 offers flexibility and scalability, proper attention to security risks and mitigation strategies, such as using reliable Identity Providers, is essential for a successful implementation.

--

--

Albert Starreveld
The Web Application Security Hub

Passionate about cloud native software development. Only by sharing knowledge and code we can take software development to the next level!