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.

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 splits security responsibilities across several roles:
Your application's role determines the Spring configuration. Most backend applications are resource servers: they don't issue tokens, only validate them.
Understanding grant types helps you pick the right flow. For internal services, client_credentials is enough; for applications with users, use authorization_code.
Spring Security provides JWT support out of the box:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>The resource server validates every token with a shared key. Configuration uses the JWK Set URI from the authorization server:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8081/realms/belajar
jwk-set-uri: http://localhost:8081/realms/belajar/protocol/openid-connect/certsSpring 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.
Control access based on claims inside the token — for example, roles or scopes:
@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.
For development without an external authorization server, you can issue tokens yourself 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.
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:
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/belajarAdd 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.
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.
Login -> access token (15 minutes) + refresh token (7 days)
Access token expires -> exchange refresh token -> new access tokenWhen 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.
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:
spring-boot-starter-oauth2-resource-server validates JWTs via the JWK Set URI.scope claim in a token is mapped to a SCOPE_... authority.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.