Spring Security OAuth2 Resource Server – Complete Guide

Cover illustration showing Spring Boot secured as an OAuth2 Resource Server with token validation

Learn how to configure and secure a Spring Boot application as an OAuth2 Resource Server. This step-by-step tutorial covers access token validation, protected endpoints, and best practices using Spring Security.

What is an OAuth2 Resource Server?

An OAuth2 Resource Server is a backend application that hosts protected resources (like REST APIs) and accepts access tokens issued by an Authorization Server (e.g., Auth0, Google, Okta, Keycloak).

The Resource Server does not authenticate users directly. Instead, it validates JWT or opaque tokens provided in the Authorization header of incoming requests.

OAuth2 Resource Server vs Authorization Server

It’s important to distinguish between these two components:

Component Description Example
Authorization Server Issues and signs access tokens after authenticating users Keycloak, Google, Firebase, Okta
Resource Server Validates incoming access tokens and secures APIs Spring Boot REST API

Why Use Spring Security for Resource Servers?

Spring Security makes it easy to turn any Spring Boot app into a fully compliant OAuth2 Resource Server with just a few configurations:

  • Supports JWT and opaque tokens
  • Automatically decodes and validates tokens
  • Protects endpoints based on roles/scopes

Required Dependencies

Add the following Maven dependency in your pom.xml:

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

Also include web starter for REST API support:

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

Basic application.yml Configuration

To use JWT tokens, add the public key URL or JWK Set URI from your authorization server:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: https://YOUR_AUTH_SERVER/.well-known/jwks.json

Spring Security will fetch this JWK URI to verify incoming JWT signatures automatically.

Securing an API Endpoint

Let’s create a simple endpoint:

@RestController
public class MessageController {
  @GetMapping("/api/secure")
  public String secured() {
    return "This is a protected resource!";
  }
}

Now secure it in your configuration:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  http
    .authorizeHttpRequests(auth -> auth
      .requestMatchers("/api/secure").authenticated()
      .anyRequest().permitAll())
    .oauth2ResourceServer(oauth2 -> oauth2.jwt());
  return http.build();
}

Token Validation, Claims, and Scopes in Spring OAuth2 Resource Server

How JWT Token Validation Works

When a user accesses a secured endpoint with an Authorization: Bearer <token> header, Spring Security performs:

  1. Validates the JWT signature using the configured jwk-set-uri
  2. Parses token claims like sub, scope, exp
  3. Applies authorization rules based on the claims

If the token is invalid or expired, the API returns 401 Unauthorized.

Extracting Claims from JWT Token

You can inject the JWT and read custom claims inside your controller:

@GetMapping("/api/profile")
public Map<String, Object> userDetails(@AuthenticationPrincipal Jwt jwt) {
  return jwt.getClaims();
}

Or extract specific fields:

String email = jwt.getClaim("email");

Scope-Based Access Control

Scopes (also known as authorities) define what actions a user can perform. These are included in the JWT claims:

"scope": "read write"

Then use Spring expressions to protect endpoints:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  http
    .authorizeHttpRequests(auth -> auth
      .requestMatchers("/api/read").hasAuthority("SCOPE_read")
      .requestMatchers("/api/write").hasAuthority("SCOPE_write")
      .anyRequest().authenticated())
    .oauth2ResourceServer(oauth2 -> oauth2.jwt());
  return http.build();
}

Common Errors & Fixes

  • 401 Unauthorized: Token is missing or expired
  • 403 Forbidden: Token is valid but lacks required scope
  • Cannot convert access token: JWT does not have expected format → Check your JWK URI
  • No JWK set URI configured: Missing jwk-set-uri in application.yml

Custom JWT Converter (Optional)

You can customize how Spring maps JWT claims to granted authorities using a JwtAuthenticationConverter:

public class CustomJwtConverter implements Converter<Jwt, AbstractAuthenticationToken> {
  @Override
  public AbstractAuthenticationToken convert(Jwt jwt) {
    Collection<GrantedAuthority> authorities = jwt.getClaimAsStringList("roles")
      .stream()
      .map(role -> new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))
      .collect(Collectors.toList());

    return new JwtAuthenticationToken(jwt, authorities);
  }
}

And register it:

.oauth2ResourceServer(oauth2 -> oauth2
  .jwt(jwt -> jwt.jwtAuthenticationConverter(new CustomJwtConverter()))
)

Opaque Tokens and Introspection Endpoint in Spring Resource Server

What Are Opaque Tokens?

Unlike JWTs, opaque tokens don’t carry information within them. They are random strings and must be validated via an introspection endpoint hosted by the authorization server.

Use opaque tokens when:

  • You want to hide user details from the client
  • You want the ability to revoke tokens instantly
  • You have full control over the authorization server

How to Enable Opaque Token Support

Replace the JWT config with the introspection endpoint configuration in application.yml:

spring:
  security:
    oauth2:
      resourceserver:
        opaque-token:
          introspection-uri: https://your-auth-server/oauth2/introspect
          client-id: your-client-id
          client-secret: your-client-secret

You must configure client credentials that your Spring Boot app will use to communicate with the auth server.

Sample Introspection Response

The authorization server responds with:

{
  "active": true,
  "scope": "read write",
  "username": "user@example.com",
  "exp": 1714399999,
  "sub": "user-id",
  "client_id": "resource-client"
}

Spring parses this and builds the authenticated principal.

JWT vs Opaque Tokens

Feature JWT Opaque Token
Contains Claims Yes (self-contained) No (server-side lookup)
Revocable Hard to revoke Easily revocable
Validation Local (fast) Remote (slower)
Size Larger Compact

Performance Best Practices

  • Use JWT for high-performance microservices and stateless architecture
  • Use opaque tokens when strict control over token lifecycle is required
  • For introspection, enable token caching to reduce HTTP overhead
  • Secure introspection endpoint with IP allowlisting or authentication

Summary

  • Spring Security makes it simple to enable JWT or opaque token support
  • JWT tokens are self-contained, fast, and ideal for stateless APIs
  • Opaque tokens provide revocability and enhanced privacy
  • Secure and test endpoints using tools like Postman or curl

Post a Comment

0 Comments