The Complete Production SOP: How to Deploy and Run Spring Security 6 OAuth2 JWT Resource Server in Autonomous AI Agents
Step-by-step SOP to install and run Spring Security 6 OAuth2 JWT Resource Server in Codex, Claude Code, and Cursor. Master JWT claim mappings and method security.
Technical Overview & Architectural Context
Modern Spring Boot 3.x and Spring Security 6 applications eliminate deprecated adapter patterns like WebSecurityConfigurerAdapter, introducing functional, lambda-based SecurityFilterChain configurations. Autonomous coding agents (Codex, Claude Code, Cursor, Windsurf) frequently hallucinate legacy Spring Security 5 patterns, misconfigure stateless session management, or inadvertently default to antMatchers() instead of requestMatchers(). Furthermore, LLMs repeatedly fail to properly implement JwtAuthenticationConverter, resulting in 403 Forbidden errors when Keycloak or Auth0 roles inside nested claims (such as realm_access.roles) fail to map to Spring's required 'ROLE_' prefixed GrantedAuthorities. The 'spring-security-oauth2-resource-server' deterministic skill installs an authoritative protocol directly into your agent's system prompt. It restricts the LLM to pure Spring Security 6 idioms, mandates @EnableMethodSecurity instead of @EnableGlobalMethodSecurity, enforces stateless SessionCreationPolicy, and supplies production-ready converters that extract nested claims into standard authorities. By encapsulating these boundaries within a standardized SKILL.md runtime manifest, your agent produces verified, compile-ready security architectures on the first pass.
Prerequisites & Environment Requirements
Verified Search Intents & Long-Tail Queries Solved
This operational tutorial is engineered to provide deterministic solutions for these developer search queries:
Step-by-Step Execution Playbook
Local Environment Setup & SKILL.md Mounting
Create the local skill manifest directory and inject verified procedural rules into your agent's configuration registry.
Autonomous agents discover context by scanning the local workspace or global agent dotfiles. To enable the spring-security-oauth2-resource-server skill, mount the manifest into your runtime's designated skills registry (`.claude/skills`, `.cursor/skills`, or `~/.agents/skills`). This ensures the runtime triggers method security and JWT validation workflows whenever prompts mention Spring Security 6, JWT configuration, or @PreAuthorize rules.
mkdir -p .agents/skills/spring-security-oauth2-resource-server && curl -fsSL https://askills.dev/api/skills/spring-security-oauth2-resource-server/raw -o .agents/skills/spring-security-oauth2-resource-server/SKILL.md---
name: "spring-security-oauth2-resource-server"
description: "Secure Spring Boot REST APIs with stateless JWT validation and method security."
metadata:
author: "Spring"
version: "1.0.0"
runtime_compatibility:
- "codex"
- "claude-code"
- "cursor"
- "windsurf"
triggers:
- "configure spring security 6 jwt"
- "spring oauth2 resource server"
- "@preauthorize hasrole spring"
permissions:
fs_read: true
fs_write: "workspace"
shell_exec: false
net_access: false
---Place the SKILL.md file directly within the repository root under `.agents/skills` to ensure version control reproducibility across entire engineering teams using different IDE runtimes.
Do not grant `shell_exec: true` unless you explicitly require the agent to independently run Maven or Gradle verification targets in containerized sandboxes.
Environment Secrets & Dependency Validation
Validate required Spring Security dependencies and declare IdP provider properties in application.yml.
Instruct the agent to inspect `pom.xml` or `build.gradle` for `spring-boot-starter-oauth2-resource-server`. Next, inject the OAuth2 Issuer URI or JWK Set URI into `application.yml` referencing environment variables rather than hardcoded URLs, ensuring secure transitions between staging and production Identity Providers (such as Okta, Keycloak, or Auth0).
grep -q 'spring-boot-starter-oauth2-resource-server' pom.xml || echo 'Add spring-boot-starter-oauth2-resource-server to pom.xml'spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${OAUTH2_ISSUER_URI:http://localhost:8080/realms/master}
# Optional JWK Set URI fallback if IdP discovery endpoint is behind proxy:
# jwk-set-uri: ${OAUTH2_JWK_SET_URI:http://localhost:8080/realms/master/protocol/openid-connect/certs}Use `issuer-uri` instead of hardcoded `jwk-set-uri` whenever possible to enable automatic rotation of public keys and discovery of standard OpenID metadata endpoints.
If using Keycloak or self-hosted IdPs behind a Docker network bridge, ensure the issuer URI inside the token payload (`iss` claim) exactly matches the `issuer-uri` configured in Spring Boot.
Intent Matching & SecurityFilterChain Architecture
Invoke the agent using targeted triggers to construct a stateless SecurityFilterChain and custom JwtAuthenticationConverter.
Prompt the agent using one of the frontmatter triggers (`configure spring security 6 jwt`). The agent executes the SOP: instantiating a `@Configuration` class with `@EnableWebSecurity` and `@EnableMethodSecurity`, defining the `SecurityFilterChain` bean with `SessionCreationPolicy.STATELESS`, and wiring a `JwtAuthenticationConverter` that converts realm roles into Spring Security `ROLE_` authorities.
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health", "/public/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
);
return http.build();
}
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(new KeycloakRoleConverter());
return converter;
}
static class KeycloakRoleConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
@Override
@SuppressWarnings("unchecked")
public Collection<GrantedAuthority> convert(Jwt jwt) {
Map<String, Object> realmAccess = (Map<String, Object>) jwt.getClaims().get("realm_access");
if (realmAccess == null || realmAccess.isEmpty()) {
return Collections.emptyList();
}
List<String> roles = (List<String>) realmAccess.get("roles");
if (roles == null) {
return Collections.emptyList();
}
return roles.stream()
.map(roleName -> new SimpleGrantedAuthority("ROLE_" + roleName.toUpperCase()))
.collect(Collectors.toList());
}
}
}Instruct the agent to place the `Converter` in an isolated nested static class or separate service bean to prevent circular dependency injection during bean graph construction.
Omitting `@EnableMethodSecurity` will allow code with `@PreAuthorize("hasRole('ADMIN')")` to compile and run, but security checks will silently fail to execute, leaving endpoints unprotected.
Verification, Linting & Output Quality Gate
Enforce a deterministic self-healing check using Spring Security Test mock MVC assertions.
Before accepting changes, instruct the agent to run unit and integration tests using `@WebMvcTest` and `SecurityMockMvcRequestPostProcessors.jwt()`. This validates that public endpoints remain open, protected endpoints return HTTP 401 when unauthenticated, and `@PreAuthorize` returns HTTP 403 when the required role is missing.
./mvnw clean test -Dtest=SecurityConfigTestpackage com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class SecurityConfigTest {
@Autowired
private MockMvc mockMvc;
@Test
void givenNoToken_whenProtectedResource_thenUnauthorized() throws Exception {
mockMvc.perform(get("/api/v1/admin"))
.andExpect(status().isUnauthorized());
}
@Test
void givenValidRoleAdmin_whenAdminEndpoint_thenOk() throws Exception {
mockMvc.perform(get("/api/v1/admin")
.with(jwt().authorities(new SimpleGrantedAuthority("ROLE_ADMIN"))))
.andExpect(status().isOk());
}
}Always verify that your mock tests use `.with(jwt().authorities(...))` rather than legacy `.with(user(...))` to accurately simulate the JwtAuthenticationToken pipeline.
MockMvc without `@AutoConfigureMockMvc` will not instantiate the full `SecurityFilterChain`, leading to false positive test passes.
Real-World Scenario: Stateless Keycloak JWT Resource Server Migration for Financial Ledger API
“Apply skill spring-security-oauth2-resource-server: Secure our Spring Boot 3.2 financial ledger API. Implement a stateless SecurityFilterChain validating Bearer JWTs against Keycloak. Map nested 'realm_access.roles' to Spring Security 'ROLE_' authorities and protect GET /api/v1/ledger with @PreAuthorize("hasRole('AUDITOR')"). Provide integration test verification.”
Runtime Troubleshooting & Failure Modes
Root Cause: The custom JwtAuthenticationConverter is not properly registered, or is reading claims from 'scope'/'scp' instead of nested IdP structures (e.g., realm_access.roles), or the GrantedAuthority lacks the mandatory 'ROLE_' prefix expected by hasRole().
Ensure your converter prefixes extracted roles: new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()), and verify the converter is passed to oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())).Root Cause: Spring Boot failed to connect to the OpenID provider discovery metadata endpoint at application startup due to network unreachability, DNS resolution failure in container sandbox, or uninitialized IdP.
Verify network access to the IdP host or configure a static jwk-set-uri in application.yml to decouple key validation from runtime provider metadata discovery: spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://host.docker.internal:8080/realms/master/protocol/openid-connect/certsFrequently Asked Questions (Spring Security 6 OAuth2 JWT Resource Server SOP)
Q:Can I use Spring Security 6 OAuth2 JWT Resource Server across Codex, Claude Code, and Cursor simultaneously?
Yes. The skill conforms to the universal SKILL.md specification supported across all modern agent runtimes. By placing the file in `.agents/skills/` or runtime-specific skill folders, any compatible agent parses the frontmatter triggers and enforces identical Spring Security 6 rules.
Q:How does Spring Security 6 OAuth2 JWT Resource Server prevent context window overflow during long agent sessions?
Instead of re-pasting entire Spring Security documentation pages into the prompt, the SKILL.md provides deterministic architectural constraints (exact bean names, lambda structures, converter patterns). This allows the LLM to generate production code using minimal prompt tokens without iterative guess-and-check cycles.
Q:Does this skill require escalated root or container permissions?
No. The skill is audited with a security score of 98/100. It requires read and workspace write permissions solely to update Java source and YAML configuration files, operating completely within sandboxed, unprivileged workspace bounds.
Ready to equip your autonomous agent with Spring Security 6 OAuth2 JWT Resource Server?
Copy the complete audited SKILL.md specification or download the 1-click zip bundle directly from our verified registry.