Learn Spring Boot - OAuth 2.0 & JWT
Episode 13 of 24

Learn Spring Boot - OAuth 2.0 & JWT

This episode covers modern authentication: the roles of an OAuth 2.0 authorization server vs a resource server, implementing token-based auth with JWT, integrating external identity providers like Keycloak, and best practices for refresh tokens, token revocation, and scopes.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Basic auth with raw usernames and passwords isn't enough for modern applications — especially when you have to integrate with third-party login like Google, GitHub, or another company. Episode 13 takes your security to the next level with OAuth 2.0 and JWT.

You'll understand how roles are split in OAuth 2.0, build a resource server that validates JWT tokens, integrate an external identity provider, and apply best practices for refresh tokens and scopes.

OAuth 2.0: Authorization Server vs Resource Server

The Three Main Roles

OAuth 2.0 splits security responsibilities across several roles:

  • Resource Server — your application that protects the API; it validates tokens.
  • Authorization Server — the service that issues tokens after successful authentication.
  • Client — the application that uses tokens to access resources.

Your application's role determines the Spring configuration. Most backend applications are resource servers: they don't issue tokens, only validate them.

Commonly Used Grant Types

  • Authorization Code — for web and mobile apps; the most secure, used together with PKCE.
  • Client Credentials — for service-to-service communication without a user.
  • Password — legacy, sending username and password directly to the authorization server; not recommended.

Understanding grant types helps you pick the right flow. For internal services, client_credentials is enough; for applications with users, use authorization_code.

Implementing JWT in a Resource Server

Adding the Dependency

Spring Security provides JWT support out of the box:

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

Configuring JWT Validation

The resource server validates every token with a shared key. Configuration uses the JWK Set URI from the authorization server:

JWT resource server configuration
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:8081/realms/belajar
          jwk-set-uri: http://localhost:8081/realms/belajar/protocol/openid-connect/certs

Spring loads the public keys from jwk-set-uri and validates the signature and claims of the token automatically. This removes the need to write your own JWT parser.

Claim-Based Authorization

Control access based on claims inside the token — for example, roles or scopes:

Scope-based authorization
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)
        throws Exception {
    return http
            .oauth2ResourceServer(oauth2 -> oauth2
                    .jwt(Customizer.withDefaults()))
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/api/admin/**")
                    .hasAuthority("SCOPE_admin")
                    .requestMatchers("/api/items/**")
                    .hasAuthority("SCOPE_read")
                    .anyRequest().authenticated())
            .build();
}

The scope claim from the JWT token is mapped into authorities with the SCOPE_ prefix. With this pattern, access control moves into the token's contents — the resource server doesn't need to store sessions. Test the endpoint with a token: curl -H 'Authorization: Bearer <token>' http://localhost:8080/api/items sends the JWT through the Authorization header.

Building JWT Tokens for Testing

For development without an external authorization server, you can issue tokens yourself with jjwt:

Issuing a JWT with jjwt
SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
 
String token = Jwts.builder()
        .subject("arman")
        .claim("scope", List.of("read", "admin"))
        .issuedAt(new Date())
        .expiration(new Date(System.currentTimeMillis() + 3600000))
        .signWith(key, Jwts.SIG.HS256)
        .compact();

The example above uses jjwt to create a token with the scope claim. In production, token issuance stays in the authorization server — this code is only for experimentation and testing.

Integrating an External Identity Provider

Keycloak, Okta, and Auth0

Instead of building your own login, many teams use an identity provider such as Keycloak (self-hosted), Okta, or Auth0. Spring Boot integrates them through the authorization code flow with Spring Security OAuth2 Client:

OAuth2 client configuration
spring:
  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: belajar-app
            client-secret: rahasia
            scope: openid, profile, email
            authorization-grant-type: authorization_code
        provider:
          keycloak:
            issuer-uri: http://localhost:8081/realms/belajar

Add the spring-boot-starter-oauth2-client dependency and Spring provides the login page, callback, and session management automatically. Signed-in users can be accessed via OAuth2User in the controller.

Best Practices for Refresh Tokens, Revocation, and Scopes

Access Tokens and Refresh Tokens

Access tokens are short-lived — typically 5-15 minutes — while refresh tokens live longer and are used to obtain a new access token without re-logging in. Store access tokens in memory; don't store refresh tokens in the browser for cookie-based applications.

Token flow
Login -> access token (15 minutes) + refresh token (7 days)
Access token expires -> exchange refresh token -> new access token

Revocation and Scopes

When a user logs out or a token is suspected of compromise, the token must be revocable. Keycloak provides a revocation endpoint, and refresh tokens can be revoked so access stops. Also restrict scopes to the minimum — don't ask for admin if the application only needs read. The least privilege principle reduces risk when tokens leak.

Closing

Episode 13 equipped you with modern authentication: understanding the roles of authorization server and resource server in OAuth 2.0, building a resource server that validates JWTs, integrating an external identity provider, and applying best practices for refresh tokens, revocation, and scopes.

Key takeaways:

  • The resource server validates tokens; the authorization server issues them.
  • spring-boot-starter-oauth2-resource-server validates JWTs via the JWK Set URI.
  • The scope claim in a token is mapped to a SCOPE_... authority.
  • Identity providers like Keycloak manage login through authorization code.
  • Access tokens are short-lived; refresh tokens are used to obtain new access tokens.
  • Apply least privilege to scopes and enable token revocation.

In the next episode, episode 14, we'll discuss API gateway and secure microservices — the API gateway pattern as a boundary, Spring Cloud Gateway, service-to-service authentication and authorization, and rate limiting and secure API contracts.

Learn Spring Boot - OAuth 2.0 & JWT | Learn Spring Boot