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:
- Validates the JWT signature using the configured
jwk-set-uri
- Parses token claims like
sub
,scope
,exp
- 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
inapplication.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
0 Comments