Skip to content
cccskills
FreePublished on GitHub

oauth2-resource-server

Use when configuring Spring Boot as an OAuth2 resource server, validating JWTs from an external auth provider (Keycloak, Auth0, Okta, Cognito), extracting claims, or implementing scope-based authorization.

The English summary is being prepared. Showing the original description.

See how to install

Files included (5)

  • SKILL.md4.6 KB
  • examples/bad-resource-server-config.java1.7 KB
  • examples/good-resource-server-config.java1.6 KB
  • templates/JwtClaimExtractor.java1.7 KB
  • templates/ResourceServerConfig.java3.3 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

OAuth2 Resource Server

Dependency

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

Security Configuration

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class ResourceServerConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(AbstractHttpConfigurer::disable)
            .sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/actuator/health").permitAll()
                .requestMatchers("/api/v1/admin/**").hasAuthority("SCOPE_admin")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter()))
            )
            .build();
    }

    @Bean
    public JwtAuthenticationConverter jwtAuthConverter() {
        var converter = new JwtGrantedAuthoritiesConverter();
        converter.setAuthoritiesClaimName("roles"); // Keycloak uses "roles"
        converter.setAuthorityPrefix("ROLE_");

        var authConverter = new JwtAuthenticationConverter();
        authConverter.setJwtGrantedAuthoritiesConverter(converter);
        return authConverter;
    }
}

application.yml — Common Providers

# Keycloak
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://keycloak.example.com/realms/my-realm
          jwk-set-uri: https://keycloak.example.com/realms/my-realm/protocol/openid-connect/certs

# Auth0
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://your-domain.auth0.com/
          audiences: https://your-api.example.com  # custom claim validation

Custom Claim Extraction

@Component
public class JwtClaimExtractor {

    public UUID getUserId(JwtAuthenticationToken token) {
        return UUID.fromString(token.getToken().getClaimAsString("sub"));
    }

    public String getEmail(JwtAuthenticationToken token) {
        return token.getToken().getClaimAsString("email");
    }

    public List<String> getRoles(JwtAuthenticationToken token) {
        // Keycloak nests roles under realm_access.roles
        Map<String, Object> realmAccess = token.getToken().getClaimAsMap("realm_access");
        if (realmAccess == null) return List.of();
        return (List<String>) realmAccess.getOrDefault("roles", List.of());
    }
}

Controller — Accessing Current User

@RestController
@RequiredArgsConstructor
public class OrderController {

    @GetMapping("/api/v1/orders/my")
    public ApiResponse<List<OrderResponse>> myOrders(
        @AuthenticationPrincipal Jwt jwt  // inject JWT directly
    ) {
        UUID userId = UUID.fromString(jwt.getSubject());
        return ApiResponse.ok(orderService.findByUser(userId));
    }

    // Or with JwtAuthenticationToken for full principal
    @GetMapping("/api/v1/profile")
    public ApiResponse<ProfileResponse> profile(JwtAuthenticationToken token) {
        return ApiResponse.ok(userService.findByEmail(
            token.getToken().getClaimAsString("email")
        ));
    }
}

Method Security with Scopes

@PreAuthorize("hasAuthority('SCOPE_orders:read')")
public List<Order> findAll() { ... }

@PreAuthorize("hasRole('ADMIN') or @orderSecurity.isOwner(#orderId, authentication)")
public Order findById(UUID orderId) { ... }

// Custom security bean
@Component("orderSecurity")
public class OrderSecurityService {
    public boolean isOwner(UUID orderId, Authentication auth) {
        Jwt jwt = (Jwt) auth.getPrincipal();
        UUID userId = UUID.fromString(jwt.getSubject());
        return orderRepository.existsByIdAndCustomerId(orderId, userId);
    }
}

Gotchas

  • Agent uses hasRole("ADMIN") for scope check — scopes use hasAuthority("SCOPE_admin")
  • Agent forgets issuer-uri validation — always configure to prevent token forgery
  • Agent maps roles wrong for Keycloak — roles are nested under realm_access.roles
  • Agent uses getPrincipal() directly — cast to Jwt or use @AuthenticationPrincipal Jwt
  • Agent adds userDetailsService bean — not needed for resource servers (stateless JWT)

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Use when adding monitoring, metrics, logging, or tracing to Spring AI or LLM integration code. Covers token tracking, latency measurement, cost estimation, and prompt/response logging. Use when user mentions AI monitoring, token costs, or LLM observability.

The English summary is being prepared. Showing the original description.

lgzarturo/springboot-course3Updated Oct 17, 2026

Provides expert knowledge for building modern native Android apps using Kotlin, Jetpack Compose, Material Design 3, MVI architecture, Hilt dependency injection, and Media3/ExoPlayer.

The English summary is being prepared. Showing the original description.

lgzarturo/springboot-course3Updated Oct 17, 2026

Design, implement, review, and audit Android Jetpack Compose UI/UX for phone interfaces. Use when a request concerns concrete Compose layout, components, interaction, accessibility, or visual behavior. Do not activate for framework or dependency presence alone; non-UI Android work is excluded.

The English summary is being prepared. Showing the original description.

lgzarturo/springboot-course3Updated Oct 17, 2026

Provides expert knowledge for designing, implementing, and managing REST API versioning strategies with deprecation workflows.

The English summary is being prepared. Showing the original description.

lgzarturo/springboot-course3Updated Oct 17, 2026

astro

Free

Provides expert knowledge for building Astro 5+ sites with Islands Architecture, Content Collections, TypeScript, and performance-first rendering strategies.

The English summary is being prepared. Showing the original description.

lgzarturo/springboot-course3Updated Oct 17, 2026

Audits token storage mechanisms to prevent XSS-based JWT theft.

The English summary is being prepared. Showing the original description.

lgzarturo/springboot-course3Updated Oct 17, 2026

See all skills by lgzarturo

Report a problem with this skill