JWT and OAuth 2.0 with Spring Boot REST APIs

Complete guide with Microservices architecture, Spring Security examples, authentication, authorization, OAuth2 flows, JWT validation, scopes, roles, refresh tokens and service-to-service security.

1. JWT and OAuth 2.0 — The Big Picture

Most important concept: JWT is a token format, whereas OAuth 2.0 is an authorization framework. They are commonly used together, but they are not the same thing.
┌──────────────────────┐ │ Frontend │ │ React / Angular │ └──────────┬───────────┘ │ Login / API call │ ▼ ┌──────────────────────┐ │ Authorization Server │ │ OAuth2 / OIDC │ └──────────┬───────────┘ │ JWT Access Token │ ▼ ┌──────────────────────┐ │ API Gateway │ │ Authentication │ │ Routing / Rate Limit │ └──────────┬───────────┘ │ ┌────────────────┼────────────────┐ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ User │ │ Order │ │ Payment │ │ Service │ │ Service │ │ Service │ └────────────┘ └────────────┘ └────────────┘ │ │ │ └────────────────┼────────────────┘ │ JWT validation

A typical enterprise microservices architecture uses an Authorization Server to authenticate users and issue access tokens. The frontend sends the access token to an API Gateway and protected microservices. Spring Security Resource Server validates JWT access tokens and converts their claims into authenticated principals and authorities.

2. Authentication vs Authorization

Authentication

Authentication answers:

Who are you?

Example:

username = srikanth
password = ********

The authentication system verifies that the credentials belong to the user.

Authorization

Authorization answers:

What are you allowed to do?
Srikanth
   |
   ├── READ orders       ✓
   ├── CREATE orders     ✓
   ├── DELETE orders     ✗
   └── ADMIN operations  ✗
ConceptQuestionExample
AuthenticationWho are you?Username/password, SSO, MFA
AuthorizationWhat can you do?ADMIN can delete orders

3. What is JWT?

JWT = JSON Web Token.

A JWT is a compact token containing claims about a subject. It is digitally signed so that a Resource Server can verify its integrity and origin.

xxxxx.yyyyy.zzzzz

JWT has three parts:

HEADER.PAYLOAD.SIGNATURE

3.1 JWT Header

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-2026-01"
}
ClaimMeaning
algSigning algorithm
typToken type
kidKey identifier used for key selection/rotation

3.2 JWT Payload

{
  "sub": "1001",
  "username": "srikanth",
  "roles": ["USER"],
  "scope": "orders.read orders.write",
  "iss": "https://auth.company.com",
  "aud": "order-service",
  "iat": 1723980000,
  "exp": 1723983600
}

These properties are called claims.

ClaimMeaning
subSubject, normally user/client identifier
issIssuer
audAudience
iatIssued-at time
expExpiration time
nbfNot valid before
scopeOAuth permissions
rolesApplication-specific roles

3.3 JWT Signature

For an asymmetric algorithm such as RS256, the Authorization Server signs the header and payload using a private key.

signature =
    Sign(
       header + "." + payload,
       privateKey
    )

The Resource Server verifies the signature using the corresponding public key.

3.4 JWT is Not Normally Encrypted

Important: A normal signed JWT is encoded, not encrypted. Do not put passwords, secrets, OTPs, credit-card information or other sensitive data into the payload.

The signature protects integrity and authenticity. It does not provide confidentiality.

4. What is OAuth 2.0?

OAuth 2.0 is an authorization framework. It allows a client to obtain an access token and use that token to access protected resources without directly handling the user's password.

Resource Owner | | authorization ▼ Authorization Server | | access token ▼ Client | | access token ▼ Resource Server

OAuth2 Roles

1. Resource Owner

Usually the user who owns the protected resource.

2. Client

Application requesting access, such as a React application, mobile application or backend service.

3. Authorization Server

Authenticates users and issues access/refresh tokens.

4. Resource Server

API that protects business resources and validates access tokens.

Common Authorization Server / Identity Provider choices include Keycloak, Auth0, Okta, Microsoft Entra ID and Spring Authorization Server.

5. JWT vs OAuth2

JWTOAuth 2.0
Token formatAuthorization framework
Defines token structureDefines authorization flows and roles
Header + Payload + SignatureClient, Authorization Server, Resource Server
Can be used independentlyCan use JWT as an access token
Commonly enables stateless validationDefines how access is granted
Remember: OAuth2 can use opaque access tokens or JWT access tokens. JWT does not automatically mean OAuth2.

6. Example Spring Boot Microservices System

Consider an e-commerce application:

ComponentPortResponsibility
API Gateway8080Routing, authentication, rate limiting
Authorization Server9000OAuth2/OIDC, token issuance
User Service8081User APIs
Order Service8082Order APIs
Payment Service8083Payment APIs
┌─────────────────┐ │ Browser │ └────────┬────────┘ │ │ Login ▼ ┌─────────────────┐ │ Authorization │ │ Server :9000 │ └────────┬────────┘ │ Access Token │ ▼ ┌─────────────────┐ │ API Gateway │ │ :8080 │ └────────┬────────┘ │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ User Service Order Service Payment Service :8081 :8082 :8083

7. Spring Boot Resource Server

Suppose the Order Service is a protected REST API.

Maven Dependencies

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

application.yml

server:
  port: 8082

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.company.com

The issuer-uri tells Spring Security which Authorization Server issued the tokens. Spring Boot/Spring Security can use the issuer metadata to discover the public signing keys and validate JWTs.

SecurityConfig

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(
            HttpSecurity http) throws Exception {

        http
            .csrf(csrf -> csrf.disable())

            .authorizeHttpRequests(auth -> auth

                .requestMatchers("/public/**")
                .permitAll()

                .requestMatchers(HttpMethod.GET, "/orders/**")
                .hasAuthority("SCOPE_orders.read")

                .requestMatchers(HttpMethod.POST, "/orders/**")
                .hasAuthority("SCOPE_orders.write")

                .anyRequest()
                .authenticated()
            )

            .oauth2ResourceServer(
                oauth2 -> oauth2.jwt()
            );

        return http.build();
    }
}

Order Controller

@RestController
@RequestMapping("/orders")
public class OrderController {

    @GetMapping
    public List<String> getOrders() {
        return List.of(
            "Order-1001",
            "Order-1002"
        );
    }

    @PostMapping
    public String createOrder() {
        return "Order created";
    }
}

Calling the API

GET /orders
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

Spring Security extracts the Bearer token, validates it and creates an authenticated SecurityContext before the controller is executed.

8. JWT Validation Flow

Client | | Authorization: Bearer JWT ▼ API Gateway / Resource Server | ├── Extract Bearer token | ├── Verify JWT signature | ├── Validate expiration (exp) | ├── Validate not-before (nbf) | ├── Validate issuer (iss) | ├── Validate audience (aud) | ├── Convert claims to authorities | └── Authorization decision | ┌──┴──┐ │ │ YES NO │ │ ▼ ▼ 200/2xx 401/403

401 vs 403

StatusMeaningExamples
401 UnauthorizedAuthentication failedMissing token, invalid signature, expired token
403 ForbiddenAuthenticated but not permittedUser lacks required scope/role
Easy way to remember:
401 = "I don't accept your credentials."
403 = "I know who you are, but you are not allowed."

9. OAuth Scopes

Suppose the JWT contains:

{
  "scope": "orders.read orders.write"
}

Spring Security commonly maps OAuth scopes to authorities using the SCOPE_ prefix.

orders.read
      ↓
SCOPE_orders.read

Therefore this works:

.requestMatchers(HttpMethod.GET, "/orders/**")
.hasAuthority("SCOPE_orders.read")

And:

.requestMatchers(HttpMethod.POST, "/orders/**")
.hasAuthority("SCOPE_orders.write")

10. Roles vs Scopes

RoleScope
Usually describes the user's application roleUsually describes an allowed access capability
ADMIN, USER, MANAGERorders.read, orders.write
Often used for business authorizationOften used for API/resource permissions

A JWT can contain both:

{
  "sub": "1001",
  "roles": ["ADMIN"],
  "scope": "orders.read orders.write"
}

Custom Role Authority

If your Authorization Server puts roles in a custom claim, configure a JWT authority converter appropriate to that claim.

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {

    JwtGrantedAuthoritiesConverter authorities =
            new JwtGrantedAuthoritiesConverter();

    JwtAuthenticationConverter converter =
            new JwtAuthenticationConverter();

    converter.setJwtGrantedAuthoritiesConverter(authorities);

    return converter;
}

The exact converter configuration depends on whether your provider exposes roles in roles, realm_access.roles, groups or another claim.

11. Accessing the Current User

@GetMapping("/me")
public Map<String, Object> me(
        @AuthenticationPrincipal Jwt jwt) {

    return jwt.getClaims();
}

Or:

@GetMapping("/me")
public String currentUser(
        @AuthenticationPrincipal Jwt jwt) {

    return jwt.getSubject();
}

If the token contains:

{
   "sub": "1001"
}

then jwt.getSubject() returns 1001.

12. OAuth2 Authorization Code + PKCE

For browser and mobile public clients, Authorization Code with PKCE is a key OAuth2 flow.

Browser / Client | | 1. Authorization request ▼ Authorization Server | | 2. Login ▼ User | | 3. Authenticate ▼ Authorization Server | | 4. Authorization Code ▼ Client | | 5. Code + PKCE verifier ▼ Authorization Server | | 6. Access Token ▼ Client

Why PKCE?

PKCE protects the authorization-code exchange against interception of the authorization code.

code_verifier

        ↓ SHA-256

code_challenge

The client initially sends the code_challenge. Later it sends the code_verifier. The Authorization Server verifies that the verifier produces the original challenge.

13. OAuth2 Client Credentials Flow

This is especially important for microservice-to-microservice communication where there is no human user.

Order Service | | client_id + client_secret ▼ Authorization Server | | Access Token ▼ Order Service | | Authorization: Bearer JWT ▼ Payment Service

Example:

spring:
  security:
    oauth2:
      client:
        registration:
          payment-service:
            provider: company-auth
            client-id: order-service
            client-secret: ${ORDER_SERVICE_SECRET}
            authorization-grant-type: client_credentials
            scope:
              - payment.read

        provider:
          company-auth:
            token-uri: https://auth.company.com/oauth2/token

Conceptually:

Order Service
     |
     | client_credentials
     ▼
Authorization Server
     |
     | JWT Access Token
     ▼
Order Service
     |
     | Bearer JWT
     ▼
Payment Service

14. Access Token vs Refresh Token

TokenPurposeTypical Lifetime
Access TokenCall protected APIsShort-lived
Refresh TokenObtain a new access tokenLonger-lived

Example:

Access Token  = 15 minutes
Refresh Token = several days (depending on policy)
Client | | Access Token ▼ API | X Expired | ▼ Authorization Server | | Refresh Token ▼ New Access Token | ▼ Client
Access tokens should generally be short-lived to reduce the impact of token theft. Refresh tokens require careful storage, rotation and revocation policies.

15. JWT with RSA / Asymmetric Keys

A strong microservices design is to let only the Authorization Server hold the signing private key.

PRIVATE KEY | | sign ▼ Authorization Server | | JWT ▼ ┌──────────────┐ │ API Gateway │ └──────┬───────┘ | ┌───────┼────────┐ ▼ ▼ ▼ User Order Payment Service Service Service | | | └───────┼────────┘ | PUBLIC KEY | verify

The services only need the public key to verify signatures. They should not possess the private signing key.

16. JWK and JWKS

JWK = JSON Web Key.

JWKS = JSON Web Key Set, a collection of public keys.

Example endpoint:

https://auth.company.com/oauth2/jwks
Authorization Server | | JWKS ▼ Order Service | | Cache public keys ▼ JWT validation

The JWT header can contain a kid value:

{
  "alg": "RS256",
  "kid": "key-002"
}

The Resource Server can select the matching public key from the JWKS.

17. Key Rotation

Suppose the current key is:

kid = key-001

Later the Authorization Server introduces:

kid = key-002

JWKS may temporarily contain both:

key-001
key-002

New tokens use key-002, while Resource Servers can continue validating older tokens signed with key-001 until the old key is retired according to the organization's rotation policy.

18. JWT vs Session Authentication

Traditional Session

Browser | | Login ▼ Server | | Session ID ▼ Browser Server maintains: Session ID → User Information

JWT-Based Access Token

Browser | | JWT ▼ Microservice | | Validate signature + claims ▼ Request allowed

JWT access tokens can enable stateless access-token validation because the Resource Server can validate the token without looking up a server-side HTTP session for every request.

Important nuance: JWT does not mean the entire authentication system has no state. Refresh tokens, revocation records, user sessions, audit records and key management may still require server-side state.

19. API Gateway Authentication vs Microservice Authorization

A common architecture is:

Client | ▼ API Gateway | | Authentication ▼ Orchestrator | | Authorization ▼ Resource Service

For stronger defense in depth:

Client | ▼ Gateway | | Authenticate ▼ Orchestrator | | Authorize ▼ Resource Service | | Validate JWT again | | Business authorization ▼ Database

The downstream service should not blindly trust security headers supplied by an upstream gateway. It should establish its own security context and perform authorization appropriate to the resource it owns.

20. OAuth2 vs OpenID Connect

OAuth2 primarily addresses authorization.

OpenID Connect (OIDC) adds an identity/authentication layer on top of OAuth2.

OAuth2 + Identity Layer = OpenID Connect

OIDC introduces concepts such as:

Access Token vs ID Token

Access TokenID Token
Used to authorize API accessUsed to communicate authentication/identity information to the client
Audience is normally an API/resource serverAudience is normally the client application
Used by Resource ServerUsed by the Client
Contains scopes/authorization informationContains identity claims
Do not treat an ID Token as a replacement for an API access token.

21. Custom JWT Authentication vs OAuth2

Suppose you implement your own login endpoint and generate a JWT:

@PostMapping("/login")
public TokenResponse login(...) {

    // authenticate username/password

    String token = Jwts.builder()
        .subject(user.getUsername())
        .claim("role", user.getRole())
        .issuedAt(new Date())
        .expiration(expiration)
        .signWith(privateKey)
        .compact();

    return new TokenResponse(token);
}

This is JWT-based authentication.

It is not automatically OAuth2. OAuth2 requires the OAuth authorization model and defined grant/authorization flows. A custom JWT login endpoint can be perfectly valid for a controlled application, but it is different from implementing an OAuth2 Authorization Server.

22. Complete OAuth2 + JWT Microservices Architecture

┌───────────────────────┐ │ Frontend │ │ Angular / React │ └───────────┬───────────┘ | | OAuth2 / OIDC ▼ ┌───────────────────────┐ │ Authorization Server │ │ │ │ Keycloak / Auth0 / │ │ Entra ID / Spring AS │ └───────────┬───────────┘ | | JWT Access Token ▼ ┌───────────────────────┐ │ API Gateway │ │ │ │ Authentication │ │ Routing │ │ Rate Limiting │ └───────────┬───────────┘ | ┌───────────────────┼───────────────────┐ | | | ▼ ▼ ▼ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ User Service │ │ Order Service │ │ Payment Service│ │ │ │ │ │ │ │ Resource │ │ Resource │ │ Resource │ │ Server │ │ Server │ │ Server │ └────────────────┘ └────────────────┘ └────────────────┘ | | | └───────────────────┼───────────────────┘ | Databases

23. Mapping This to a Typical Spring Boot Microservices Project

A system with an API Gateway, Orchestrator and Auth Service can evolve into the following design:

┌─────────────────┐ │ Frontend │ └────────┬────────┘ | ▼ ┌─────────────────┐ │ API Gateway │ │ :8080 │ │ JWT Validation │ └────────┬────────┘ | ▼ ┌─────────────────┐ │ Orchestrator │ │ :8081 │ │ Authorization │ └────────┬────────┘ | ┌────────┴────────┐ ▼ ▼ ┌──────────────┐ ┌──────────────┐ │ Auth Service │ │ Other │ │ :8082 │ │ Services │ └──────────────┘ └──────────────┘

With OAuth2/OIDC, the Auth Service can be replaced or supplemented by a dedicated Authorization Server/Identity Provider. The application services can become OAuth2 Resource Servers.

24. Should You Build Your Own Authorization Server?

For enterprise systems, avoid implementing OAuth2 security protocols from scratch unless there is a strong architectural reason.

Common established choices include:

Keycloak Auth0 Okta Microsoft Entra ID Spring Authorization Server

If you need to operate your own Authorization Server, Spring Authorization Server is one option. Note that current Spring Authorization Server releases have their own Java/Spring version requirements, so verify compatibility before selecting it for a Java 11 application.

25. Production Security Checklist

Token Security

  • Short-lived access tokens
  • Strong signing algorithms
  • Validate signature
  • Validate issuer
  • Validate audience
  • Validate expiration
  • Validate not-before where applicable

Authorization

  • Use least privilege
  • Use scopes
  • Use roles where appropriate
  • Perform resource-level authorization
  • Do not rely only on gateway authorization

Infrastructure

  • HTTPS everywhere
  • Secure private-key storage
  • Key rotation
  • JWKS
  • Vault / Secret Manager
  • Audit security events

Refresh Tokens

  • Secure storage
  • Rotation
  • Revocation
  • Shorten lifetime when appropriate
  • Do not expose unnecessarily

Microservices

  • Validate JWT at Resource Server
  • Do not blindly trust gateway headers
  • Use Client Credentials for service-to-service calls where appropriate
  • Use separate scopes/audiences for services

26. Complete Mental Model

AUTHENTICATION | ▼ ┌─────────────┐ │ User │ └──────┬──────┘ | ▼ ┌──────────────────┐ │ Authorization │ │ Server │ │ │ │ OAuth2 / OIDC │ └────────┬─────────┘ | | Access Token ▼ ┌──────────────────┐ │ JWT │ │ │ │ Header │ │ Payload │ │ Signature │ └────────┬─────────┘ | ▼ ┌──────────────────┐ │ API Gateway │ │ │ │ Authenticate │ └────────┬─────────┘ | ▼ ┌──────────────────┐ │ Order Service │ │ │ │ Resource Server │ │ │ │ Authorize │ └────────┬─────────┘ | ▼ Database
TermMeaning
JWTHow a token is represented
OAuth2Framework for delegated authorization
OIDCIdentity/authentication layer on OAuth2
Authorization ServerIssues access/refresh tokens
Resource ServerProtects APIs and validates access tokens
OAuth2 ClientRequests/uses tokens
Access TokenUsed to access protected APIs
Refresh TokenUsed to obtain new access tokens
ScopePermission/capability represented in OAuth2
RoleApplication-level authority such as ADMIN or USER
JWK/JWKSJSON representation/set of cryptographic keys

27. Interview-Ready Answer

Question: Explain JWT and OAuth2 in a microservices architecture.

JWT is a token format containing claims that are digitally signed, while OAuth2 is an authorization framework defining how clients obtain and use access tokens. In a typical Spring Boot microservices architecture, an Authorization Server authenticates the user and issues a short-lived JWT access token. The client sends it as a Bearer token through the API Gateway to protected services. The Gateway and/or downstream Resource Servers validate the JWT signature using the Authorization Server's public key or JWKS and validate claims such as issuer, audience and expiration. Spring Security converts scopes or roles into authorities and performs authorization at the endpoint level. For service-to-service communication where there is no user, OAuth2 Client Credentials is commonly used. For browser-based authorization, Authorization Code with PKCE is commonly used.

The key distinction

OAuth2
  ↓
How authorization/access is granted

JWT
  ↓
How the access token can be represented

Spring Security Resource Server
  ↓
How the API validates the token

Scopes / Roles
  ↓
How API authorization decisions are made
Core takeaway: OAuth2 controls the authorization flow, JWT is a commonly used access-token format, and Spring Security Resource Server validates the token and enforces API authorization.

28. Quick Revision Cheat Sheet

QuestionAnswer
What is JWT?Signed token format containing claims.
Is JWT encrypted?Normally no. It is encoded and signed.
What is OAuth2?Authorization framework.
Does OAuth2 require JWT?No. OAuth2 can use opaque tokens too.
Who issues access tokens?Authorization Server.
Who validates access tokens?Resource Server, often with Spring Security.
What is a scope?Permission/capability.
What is a role?Application/business authority.
What is 401?Authentication failed or credentials are missing/invalid.
What is 403?Authenticated but not authorized.
What is PKCE?Protection for authorization-code flow against code interception.
What is Client Credentials?OAuth2 flow commonly used for service-to-service access.
What is OIDC?Identity layer built on OAuth2.
What is JWKS?Set of JSON Web Keys used to publish verification keys.
Why RS256?Private key signs; public key verifies, making distribution to services safer.