A OAuth 2.0 is an authorization framework that allows applications to access protected resources on behalf of a user without exposing the user's credentials.

A JWT (JSON Web Token) is a compact, digitally signed token commonly used to carry authentication and authorization information between clients and servers.

REST APIs typically combine OAuth 2.0 and JWT to build secure, scalable, and stateless authentication systems.

OAuth 2.0 defines how access is granted, while JWT is often used as the access token that proves the client's identity and permissions.

Why Do We Need OAuth 2.0?

Suppose an application asks users to provide their username and password for every API request.
        Client
           |
 Username + Password
           |
           v
 +------------------+
 | Spring Boot API  |
 +------------------+
This approach requires the application to handle user credentials directly on every request. It increases the risk of credential exposure and makes password management more difficult.

It also becomes challenging to integrate with external identity providers such as Google, GitHub, Microsoft Entra ID, or Okta because the application is responsible for validating user credentials itself.

OAuth 2.0 solves this problem by separating authentication from resource access.

Users authenticate once with an Authorization Server, which verifies their identity and issues an Access Token.

The client stores this token and includes it with every API request instead of sending the user's username and password.

The Spring Boot API validates the access token before processing each request.

Since user credentials are never sent directly to the API, applications become more secure, integrate easily with external identity providers, and support stateless, token-based authentication.

OAuth 2.0

OAuth 2.0 is an industry-standard authorization framework that allows an application to access protected resources on behalf of a user without requiring the user's password.

Instead of sharing credentials with every application, users authenticate with an Authorization Server, which issues an Access Token.

The client presents this token when accessing protected resources.

OAuth 2.0 Components

OAuth 2.0 defines four primary participants that work together during the authorization process.

1. The Resource Owner is the user who owns the protected data and grants permission to access it.
2. The Client is the application requesting access on the user's behalf.
3. The Authorization Server authenticates the user, obtains their consent, and issues an Access Token.
4. The Resource Server is the Spring Boot API that validates the access token before serving protected resources.

OAuth 2.0 Flow

The following example shows a typical OAuth 2.0 authorization flow.

The user first authenticates with the Authorization Server, which verifies the user's identity and issues an Access Token and a Refresh Token.

The client stores the access token and includes it in the Authorization header of every API request. The Resource Server validates the token before processing the request.

If the access token expires, the client sends the Refresh Token to the Authorization Server to obtain a new access token without requiring the user to log in again.

The refresh token is sent only to the Authorization Server and is never used to access protected APIs directly.

If the access token is valid, the requested resource is returned; otherwise, the request is rejected with an authorization error.

OAuth 2.0 Grant Types

OAuth 2.0 defines multiple authorization flows, known as Grant Types, to support different types of clients and authentication scenarios.

The most common grant types are shown below.
Grant Type Typical Use Case
Authorization Code + PKCE Browser and mobile applications (recommended)
Client Credentials Machine-to-machine communication between services
Device Authorization Smart TVs, IoT devices, gaming consoles
Refresh Token Obtaining a new access token without user login
Authorization Code Confidential server-side web applications
Implicit (Legacy) Deprecated for browser applications
Resource Owner Password Credentials (Legacy) Deprecated because applications handle user passwords directly
Today, the Authorization Code Flow with PKCE is the recommended choice for browser-based and mobile applications.

The user authenticates with the Authorization Server, which verifies the user's identity and issues a short-lived Authorization Code to the client.

The client then securely exchanges this authorization code along with the PKCE verifier for an Access Token (and optionally a Refresh Token) by making a request to the Authorization Server's token endpoint.

PKCE (Proof Key for Code Exchange) protects this exchange by requiring the client to prove that it is the same application that initiated the authentication request, preventing attackers from using a stolen authorization code.

For machine-to-machine communication, Spring Boot microservices commonly use the Client Credentials Flow.

Since no end user is involved, one service authenticates directly with the Authorization Server using its own client credentials to obtain an access token before invoking another protected service.

The Implicit and Resource Owner Password Credentials grant types are now considered legacy and should generally be avoided in new applications.

What Is PKCE?

PKCE (Proof Key for Code Exchange) is a security mechanism that protects the Authorization Code Flow from authorization code interception attacks.

When the login process begins, the client generates a random secret called the Code Verifier.

It then computes a cryptographic hash of this value, known as the Code Challenge, and sends only the Code Challenge to the Authorization Server.

The original Code Verifier never leaves the client at this stage.

Later, during the Authorization Code exchange, the client sends the original Code Verifier along with the authorization code.

If an attacker intercepts only the Authorization Code, it cannot be exchanged for an access token because the attacker does not know the original Code Verifier.

The Authorization Server recomputes the Code Challenge from the received verifier and compares it with the value stored during the initial login. If they match, the server issues the tokens; otherwise, the request is rejected.

The authorization code exchange must always occur over HTTPS.

If an attacker could intercept both the authorization code and the code verifier, they could impersonate the client. HTTPS protects these values while they are transmitted over the network.

What Is JWT?

A JWT (JSON Web Token) is a compact, URL-safe token used to securely exchange information between a client and a server.

JWTs are commonly used by OAuth 2.0 and modern authentication systems to represent an authenticated user and the permissions granted to that user.

A JWT consists of three parts: a Header, a Payload, and a Signature, separated by dots.
Header.Payload.Signature
A typical JWT looks like this.
eyJhbGciOiJIUzI1NiJ9
        .
eyJzdWIiOiIxMDEiLCJyb2xlIjoiQURNSU4ifQ
        .
abc123xyz
The token is digitally signed so that its contents cannot be modified without invalidating the signature.

Header

The Header contains metadata describing the token, including the signing algorithm and token type.
{
  "alg": "RS256",
  "typ": "JWT"
}
In this example, RS256 indicates that the token is signed using the RSA SHA-256 algorithm, while JWT identifies the token type.

Payload

The Payload contains claims, which are pieces of information about the authenticated user or the token itself.
{
  "sub": "101",
  "username": "john",
  "roles": ["ADMIN"],
  "exp": 1785600000
}
Common claims include the user identifier (sub), username, roles, issuer (iss), audience (aud), issued time (iat), and expiration time (exp).

Signature

The Signature protects the integrity of the token. It is generated by digitally signing the encoded header and payload using the Authorization Server's secret key or private key.
Signature = Sign(Header + Payload)
Whenever the client sends the JWT to the API, the Resource Server verifies the signature before trusting the token.

If the header or payload has been modified, signature verification fails and the token is rejected.
The token is digitally signed so that neither its Header nor its Payload can be modified without invalidating the signature.

For example, suppose the original JWT payload contains the following claim.
{
  "sub": "101",
  "role": "USER"
}
If an attacker modifies the payload to grant administrator privileges,
{
  "sub": "101",
  "role": "ADMIN"
}
the original signature no longer matches the modified payload.

When the JWT reaches the Resource Server, Spring Security recomputes the signature using the received Header and Payload together with the Authorization Server's public key (or shared secret for symmetric algorithms).

If the recomputed signature does not match the signature contained in the JWT, the token is considered invalid and the request is rejected.
Hashing: A hash function converts data into a fixed-length value called a hash. Hashing is one-way. Given the hash, it is computationally infeasible to recover the original data. For example, Password hashing (with algorithms such as SHA-256, BCrypt or Argon2), PKCE Code Challenge and File integrity verification.

Encryption: Encryption protects confidentiality by converting plaintext into ciphertext using a key. Unlike hashing, encryption is reversible. For example, HTTPS encrypts all communication between the browser and the server so that attackers cannot read the transmitted data.

Digital Signing: A digital signature proves that data has not been modified and identifies who created it. Unlike encryption, the data usually remains readable.
HTTPS uses encryption to protect data in transit.
PKCE uses hashing to derive the Code Challenge from the Code Verifier.

JWT uses digital signatures to ensure the Header and Payload have not been tampered with and that the token was issued by a trusted Authorization Server.

For JWT signing There are two common approaches.

1. Symmetric Signing (HS256): Both the Authorization Server and the Resource Server (Spring Boot API) share the same secret key. The Authorization Server signs the JWT: The Spring Boot API verifies the JWT using the same secret key.

2. Asymmetric Signing (RS256): This is what Keycloak, Auth0, Okta, and AWS Cognito typically use. The Authorization Server owns a private/public key pair. The JWT is signed with the private key. The Spring Boot API never sees the private key. Instead, it downloads the public key (usually from a JWK Set endpoint) and verifies the signature.

OAuth 2.0 + JWT Implementation

A typical Spring Boot application acts as an OAuth 2.0 Resource Server.

Instead of authenticating users directly, it relies on an external Authorization Server such as Keycloak, Auth0, Okta, or AWS Cognito to authenticate users and issue JWT Access Tokens.

When a client invokes a protected API, it includes the access token in the Authorization header. Spring Security validates the JWT before allowing the request to reach the application's business logic.
      Browser / Mobile App
               |
             Login
               |
               v
 +----------------------------+
 |   Authorization Server     |
 | Keycloak / Auth0 / Okta    |
 +----------------------------+
               |
        JWT Access Token
               |
               v
 +----------------------------+
 |      Spring Boot API       |
 |     Spring Security        |
 +----------------------------+
               |
               v
        Protected Resources
For example, a client sends the JWT access token with every request.
GET /api/orders HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
JWT access tokens should always be transmitted over HTTPS to prevent interception during transit.

The Bearer scheme tells the Resource Server that the client is presenting a bearer access token for authentication.

Spring Security validates the token before forwarding the request to the application's controllers.
@RestController
@RequestMapping("/orders")
public class OrderController {
    @GetMapping
    public List<Order> findAll() {
        return orderService.findAll();
    }
}
If the JWT is valid, Spring Security authenticates the user and allows the request to execute. If the token is missing, expired, or has an invalid signature, the request is rejected before it reaches the controller.

When an Access Token expires, the client can use a valid Refresh Token to request a new access token from the Authorization Server.

This allows users to remain signed in without repeatedly entering their credentials while ensuring that expired access tokens cannot be used indefinitely.

By delegating authentication to an Authorization Server and validating JWTs in the Spring Boot API, applications remain stateless, secure, and can easily integrate with external identity providers without managing user passwords themselves.
Authentication vs Authorization

Authentication verifies who the user is, while Authorization determines what the user is allowed to access.

For example, when a user logs in using Keycloak or Okta, the Authorization Server authenticates the user's identity before issuing a JWT access token.

Later, when the user invokes a protected Spring Boot API, Spring Security authorizes the request by verifying the JWT and checking whether the user has the required roles or permissions.

When Should You Use OAuth 2.0 + JWT?

OAuth 2.0 enables secure delegated authorization without exposing user credentials to client applications.

JWT provides a compact and self-contained token that allows APIs to validate requests without maintaining server-side sessions, making it well suited for distributed Spring Boot microservices.

Together they support scalable, stateless authentication and integrate easily with cloud identity providers.

When Should It Be Avoided?

For internal applications with a small number of users and simple authentication requirements, OAuth 2.0 may introduce unnecessary complexity compared to basic session-based authentication.

JWT tokens cannot easily be revoked before expiration unless additional mechanisms such as token blacklists or short expiration times are used.

Applications must also protect signing keys carefully because the security of the entire system depends on them.

Summary

OAuth 2.0 provides a standardized framework for authorizing access to protected resources, while JWT provides a secure, self-contained token format that carries user identity and authorization claims.

Together they form the foundation of API security, allowing Spring Boot applications to implement stateless authentication using access tokens instead of server-side sessions.
Nagesh Chauhan

Nagesh Chauhan

Principal Software Engineer β€’ Java β€’ Python β€’ Distributed Systems β€’ AI/ML

Principal Software Engineer with 14+ years of experience designing and delivering large-scale distributed systems, cloud-native applications, and AI-powered platforms.

Passionate about solving complex engineering problems using strong data structures and algorithms, along with expertise in Java, Spring Boot, Python, System Design, Microservices, Cloud, Kafka, Elasticsearch, and Generative AI.

Share this Article

πŸ’¬ Comments

Join the Discussion