Certainly, some of you might be familiar with this scenario: our web application needs to be protected with a login. Naturally, we also want to secure the backend services behind it from unauthorized external access and interact with other APIs – all of this, including single sign-on (SSO) and a rights/roles concept.
"Wait, there's this OAuth thing, and also JWT, access and refresh tokens, public and confidential clients, authorization codes, PKCE, client secrets, and consent on scopes, IDPs, and so on!" Sounds like a game of buzzword bingo, doesn’t it? It sure does: Ouch!
This is where the challenge comes in when diving into the topic of OAuth. There are countless terms and concepts, and understanding their definitions and distinctions can be tricky. The goal of this blog post is to clarify this.
To get started, let’s look at a topic that often causes confusion:
Authentication vs. Authorization
In this context, you might often come across the abbreviations AuthZ (Authorization) and AuthN (Authentication), which can be confusing.

O(Ouch): “What was AuthN and AuthZ again?”
Authentication/AuthN is the process of verifying an identity, for example, by entering a username and password. In the real world, an example would be going to an airline counter at the airport with your ID. The employee checks if your face matches the photo on the ID, thereby verifying your identity to issue you a boarding pass.
Authorization/AuthZ is the assignment of rights and privileges to resources. Identity is not a factor here. In the real world, an example would be having a boarding pass that authorizes you to board a plane. Your identity is not rechecked when boarding the plane; only the boarding pass is validated.
Now, let’s clarify whether OAuth is AuthN or AuthZ. The answer can be found in the OAuth standard, The OAuth 2.0 Authorization Framework: It is Authorization!
Great, what does this insight bring us? In short, it helps differentiate the use case of OAuth from other protocols like OpenID Connect (OIDC).
How do OAuth and OIDC differ?
OAuth is an industry-standard protocol that specifies the authorization process for an application to access other protected APIs using access tokens. These access tokens can be likened to boarding passes at an airport. They authorize access to the API.
A quick note: OAuth tokens should never be used for authentication, such as displaying user information. This is because it can lead to security issues in your application, as OAuth does not provide a standard for secure encoding of user information in access tokens. If you need to store details about the user in the token, you should consider OIDC.
OIDC is based on OAuth 2.0 and extends it to include an authentication view, allowing the display of user information like email address or first and last name. OIDC ensures that a user's identity is securely verified using the so-called ID tokens. This is done within the context of an active user session.
To help you choose the right protocol, here’s a quick decision guide:


Ouch-Note: There are indeed use cases where the user ID is included in OAuth tokens to allow the called API to provide user-specific data. If only this ID is used and no additional user information (name, email), then you are still within the OAuth standard.
Why you should consider OAuth?
The primary reason for considering OAuth is that it is the industry standard for authorization. This is especially true in the realm of cloud services, such as Azure, AWS, Google, and others, where OAuth is commonly used for authorizing access to cloud resources. OAuth has a broad user base, and the protocol is continually improved.
One of the key advantages of standard protocols, like OAuth, is that they are well-documented and rigorously tested for their effectiveness. Due to their widespread adoption, there are numerous libraries available for all major development frameworks. Additionally, there are pre-built and customizable solutions like Keycloak or Auth0 that make implementation easier.
At this point, we strongly recommend using these pre-built components and avoiding custom protocol implementations, as implementing OAuth from scratch carries a high risk of errors and security issues..
How does OAuth work?
The current standard for OAuth is OAuth 2.0, which is based on the 2012 IETF standard The OAuth 2.0 Authorization Framework. Over time, extensions like "PKCE: Proof Key for Code Exchange" and "Security Best Current Practices" have been added to enhance the protocol. An overview of these extensions can be found here.
Currently, work is underway on version 2.1 of the OAuth protocol, which can be seen as a result of lessons learned from OAuth 2.0.
In the following sections, we will explain the two most important OAuth flows based on our experiences at Pentacor, including adjustments from OAuth 2.1.
Before we dive into the different flows, we'd like to provide an explanation of key recurring terms for newcomers and those interested:
A Resource Server is a protected API to which access is authorized under OAuth. It holds the protected resources that other applications may need.
"Scope" refers to the functionality that the client can access on the Resource Server. The permission to use the scope is called "Consent."
The Resource Owner grants access to protected resources through the Resource Server. This can be a person, another service, or an organization like a company.
The Client is the application that wants to access protected information of the Resource Owner, which is located on the Resource Server. In OAuth, there is a distinction between:
- Public Clients (e.g., JavaScript frontends that cannot securely store client secrets) and
- Confidential Clients (e.g., Spring Boot backends that can securely store client secrets).
The Authorization Server is responsible for authorizing individual clients by issuing access tokens. Additionally, the Authorization Server is also responsible for authenticating the Resource Owner, for example, in the Authorization Code Flow.

OAuth: Whaaaat? The Authorization Server is responsible for authentication in OAuth? OAuth is an authorization framework!
Yes, you read that correctly. OAuth is an authorization framework developed for authorizing and identifying a user. Nevertheless, the user/client needs to sign in (authenticate). How does this fit together?
Quite simply: the form of authentication of the Resource Owner is not specified in the OAuth protocol. This responsibility lies with the Authorization Server. In practice, it often happens that the Authorization Server uses another service for authentication. Authentication could also be realized, for example, by the Authorization Server using BasicAuth.
Then, there are the tokens used in OAuth flows for authorization. You should definitely be familiar with the Access Token. This can be compared to a boarding pass at the airport. Two types are distinguished:
Self-Contained Access Tokens contain all the information necessary for authorization. This means the allowed scope or the ID of the Resource Owner are possible components of the Access Tokens. To ensure the integrity of this type of token, a signature is used.

Opaque Access Tokens only contain an ID. Details regarding the granted authorization must be requested from the Authorization Server using this ID. This has the advantage, among other things, that a token can be invalidated before its expiration, for instance, if the Resource Owner withdraws their consent.

Opaque tokens only contain an ID. Photo by Kay Gradert on Unsplash

O(Ouch): “I know self-contained tokens, these are the JWT tokens, aren’t they?”
In principle, this is true. JWT tokens are a form of self-contained token. JWT tokens are mainly used with OIDC and are enriched with user information. However, use in OAuth flows without user information in the token is also quite common (see RFC 7523 - JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants).
Refresh tokens are used to retrieve new access tokens from the authorization server. They offer two main advantages, which are particularly important in the authorization code flow:
Advantage 1 - Client access without an active user session
Due to the longer validity of the refresh token, the client can access the resource server over a longer period of time without the resource owner having to re-authenticate.
Advantage 2 - Increased security due to short access token validity
When using refresh tokens (longer validity), the validity of the access tokens can be very short. This is an advantage as access tokens, which are sent with every request, are potentially easier for an attacker to intercept.

Note: as refresh tokens are particularly worth protecting due to their long validity, they should only be stored in confidential clients.
How does it work now?
OAuth has different flows for different application scenarios. The Client Credentials and Authorization Code flows are currently common.
The Client Credentials Flow is suitable for the scenario where an application should access another secured API, and the authorization is independent of the user (resource owner).
If my application is also to access an API and its data on behalf of a user, it is advisable to use the Authorization Code Flow. In this case, the resource owner (user) explicitly gives their consent for access to their resources.
Here is a little decision-making aid:

In both flows, it's often the case in practice that the consumer application and the called API are developed by different organizational units. Negotiating a security concept between the application and API can be a complex task. This is where the strength of the OAuth standard comes into play. OAuth specifies, within the context of these flows, how secure requests should be made and what the respective responsibilities are.
Client Credential Flow
Let’s assume we are building a microservice that is supposed to provide us with information about products (ProductService). This service serves as a backend component for a shop system.
Fortunately, our colleagues have provided us with an API that delivers product images (ImageService) and uses OAuth with an Opaque Access Token for authorization. Great, we would be happy to use this API.
For such scenarios (where the application accesses the API without reference to user identity), the Client Credentials Flow is suitable.
This is where our aforementioned characters come into play:
- The ProductService is our Confidential Client and wants to access resources from the ImageService. That means the ImageService is our Resource Server.
- Access from the client to the Resource Server is authorized by the Authorization Server.
Great, now we can get started! We just need to register our client with the Authorization Server (including specifying the requested scopes), and in return, we receive our Client ID and Client Secret.
Let’s get to work!

If the ProductService wishes to retrieve image information from the ImageService, it cannot do so initially because it doesn't possess a valid Access Token. An attempt to make a request without a valid Access Token would result in an HTTP status 401 (unauthorized) response.
The steps in the Client Credential Flow are as follows:
A) Access Token Request
Our ProductService (Client) requests an Access Token from the Authorization Server, authenticating itself using Client Credentials. Optionally, the scope can also be included to specifically request access to necessary resources.
B) Access Token Response
After successful verification by the Authorization Server, it provides an Access Token, which our Client uses as proof that it's authorized as a Confidential Client to access resources from another application (Resource Server).
C) Image Data Request
Now, our ProductService requests images from the ImageService (Resource Server) and includes the Access Token as proof of authorization.
D) Token Introspection Request
Since we are using an Opaque Token, the ImageService must inquire with the Authorization Server to verify if the used Access Token is valid and active. This Authorization Server endpoint should be secured. In practice, this is accomplished through firewall rules, certificates, API keys, or even using client secrets through another Client Credential Flow.
E) Token Introspection Response
Following the verification of the Access Token, the Authorization Server confirms its validity. Additionally, the response may include the issued scope and the remaining validity period.
F) Image Data Request
With the authorization of our ProductService confirmed by the Authorization Server, the ImageService now provides the requested resource for our Client.

OOuch: "So many requests... always going from A to F..."
A typical issue encountered when implementing an OAuth process is the high number of calls within the system. New tokens are requested and validated for each request, requiring all three depicted components to communicate with each other.
Beforehand: if steps A to F are run through for every request to the client, something is not working correctly.
This should be prevented by the Authorization Server providing the remaining lifespan of the Access Token, which is then cached by the client along with the token. As long as the token remains valid, the client uses it to access the Resource Server. Thus, after the Access Token is issued by the Authorization Server, only steps C to F need to be repeated.
There's even the option to reduce it down to just steps C and F by using Self-Contained Tokens.
Authorization Code Flow
The Authorization Code Flow is the preferred method when an application should not be able to access another service without explicit user authorization.
An example scenario would be developing a web application that allows the user to control smart home devices through the manufacturer's Smart Home API. This includes both direct commands (e.g., "turn on the lights") initiated by the user and time-based controls (e.g., "activate Christmas lights daily at 4 PM") managed by the application, without requiring the user to log in each time for the latter.

What would it take to do this?
First of all, we need an API that we can use to control our smart home products. This is kindly provided to us by the smart home manufacturer and supports authorization using the OAuth Authorization Code Flow. Translated into OAuth language, this API is our resource server. The manufacturer also provides us with an Authorization Server, where the user can later authorize the access of our application to their smart home devices.
The application we developed consists of a backend client and a frontend client.

OOuch: “Why are we using two different clients now?”
In principle, the scenario described here could also be implemented without a backend. However, we have decided against this in this example, as the use of a backend has a security advantage. This is due to the fact that the client secretor the access token cannot be stored securely in the front end. It is therefore advisable to store the authentication via client secrets and the tokens in a more secure backend application.
How does the Authorisation Code Flow work?
A) Call
The user (Resource Owner) wants to try out our new application. To do so, they visit the URL of the web application and authenticate using their username and password.
To enable the user to, for example, turn on the lights, they need to grant our application permission to access their smart home lamps via the Smart Home API (Resource Server). They select the manufacturer of their smart bulbs, authenticate them, and agree to be redirected to the manufacturer's Authorization Server.
B) Redirect to Authorization Server
This redirect to the manufacturer's Authorization Server initiates the actual Authorization Code Flow. The query parameters of the redirect include the ID of our application (Client ID), the URL of our application (redirect_uri), and the necessary scope sent to the Authorization Server. A possible scope in this example might be "ControlLights" and "ListAllLights".
C) User Authentication and Consent
Since this involves accessing a protected resource, user login at the Authorization Server is required.

OOuch: "The user doesn't really need to authenticate now, do they?"
Yes, that's absolutely correct. The use of OAuth flows doesn't exclude authentication. Whether and how authentication takes place is not specified in the OAuth protocol. In the Authorization Code Flow, user authentication typically happens at the client and the Authorization Server... it would be inconvenient if anyone could control our user's lights.
After successful authentication at the Authorization Server, the user gives their consent for our application (Client) to control their Smart Home Devices through the API Gateway (Resource Server). The allowed actions our application can perform through the API ("ControlLights" and "ListAllLights") are limited by the scope.
D) Authorization Response
Once the user gives their consent, their browser is redirected back to our application via the "redirect_uri." One of the query parameters of this redirect is the Authorization Code, which is a code that can later be exchanged for an Access Token.
That means we should be able to turn on the lights now.
E) Access Token Request
To allow our application (Client) to make the "turn on the lights" request to the Resource Server (Smart Home API) on behalf of the user (Resource Owner), we need an Access Token. The Access Token Request sends the Authorization Code and redirect_uri to the Authorization Server. Both parameters are cross-referenced by the Authorization Server with the data from steps C and D. Since we're using a Confidential Client here, our Client also sends its Client Credentials to authenticate itself with the Authorization Server.
F) Access Token Response
From this point, the process is analogous to the Client Credentials Flow.
After successful verification by the Authorization Server, it provides an Access Token, which our Client uses as proof that it's authorized as a Confidential Client, to access the user's resources in the Smart Home API (Resource Server).
G) API Request
Now, our application's backend (Confidential Client) requests the user's lights to be turned on from the Smart Home API (Resource Server). The Access Token is sent as authorization proof. This could also occur as part of a night-time schedule, as long as the Refresh Token has been issued for the required duration, allowing the command to be executed without the user having to log in again.
H) Token Introspection Request
Here too, the Smart Home API needs to inquire with the Authorization Server to verify if the used Access Token is valid and active. (In this example, we are using an Opaque Token again.).
I) Token Introspection Response
Following the verification of the Access Token, the Authorization Server confirms its validity. In addition to this information, further details can be provided, including the scope or identifying information, such as the customer ID at the Smart Home provider for which the Access Token was issued.
J) API Response
After verifying the token (H-I), access to the Smart Home API is authorized. Lights on! Following confirmation of authorization and internal processing by the Resource Server, access has been granted, and our user successfully turned on their lights through our application, all within seconds.
(For comparison: The reading time until this O(ooo)Auth moment was 12 minutes.)
Conclusion
So let us state the following:
OAuth is an industry standard protocol that specifies the authorisation process for access from one application to other protected APIs. The access tokens used in OAuth should never be used to authenticate a user. If this is necessary, OIDC should be used. Although a form of authentication also takes place within the OAuth flows, this is independent of the OAuth standard.
Different flows are available for different scenarios, whereby we have presented the two most common ones in this blog post. The Client Credential Flow is suitable for the problem that an application should access another secure API and the authorisation is independent of the user (resource owner).
If my application is to access an API in a different context/organisation on behalf of a user, it is advisable to use the Authorisation Code Flow.
At pentacor, we regularly deal with OAuth in our projects. In some projects this is still relatively simple, as we develop applications that communicate with other APIs via various OAuth flows. We are also no strangers to significantly more complex OAuth problems. For example, in the context of API management, where we deal with questions such as: “How do I register a new API as a resource server on the authorisation server at runtime?” And yes, we can do that too.
Every project has its own challenges and we have already experienced the odd “OOuch, what was that again!” moment.
However, the multitude of possibilities within the OAuth standards, the number of ready-made libraries and, above all, the discussions and lessons learnt with other Pentacor readers ultimately led us to our own O(ooooo)Auth moment 🙂.