Extract Roles

In this lesson we're going to extract the roles from the user's access token.

Understanding JWT Claims

The JWT used for authentication contains useful information about the user called claims.

Among these claims is the realm_access claim which contains the roles assigned to the user in Keycloak.

When we decode a JWT at jwt.io, we can see claims like ROLE_ORGANIZER, ROLE_ATTENDEE, and ROLE_STAFF under the realm_access.roles section.

Implementing Role Extraction

To extract roles from the JWT, we need to create a custom converter that transforms the JWT into Spring Security's internal representation.

Here's how we implement the JwtAuthenticationConverter:

@Component public class JwtAuthenticationConverter implements Converter<Jwt, JwtAuthenticationToken> { @Override public JwtAuthenticationToken convert(Jwt jwt) { Collection<GrantedAuthority> authorities = extractAuthorities(jwt); return new JwtAuthenticationToken(jwt, authorities); } private Collection<GrantedAuthority> extractAuthorities(Jwt jwt) { Map<String, Object> realmAccess = jwt.getClaim("realm_access"); if(null == realmAccess || !realmAccess.containsKey("roles")) { return Collections.emptyList(); } @SuppressWarnings("unchecked") List<String> roles = (List<String>)realmAccess.get("roles"); return roles.stream() .filter(role -> role.startsWith("ROLE_")) .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); } }

The converter does the following:

  1. Gets the realm_access claim from the JWT
  2. Checks if the claim exists and contains roles
  3. Extracts the roles and converts them to Spring Security's SimpleGrantedAuthority objects

Configuring Security to Use the Converter

We need to update our security configuration to use our custom converter:

@Bean public SecurityFilterChain filterChain( HttpSecurity http, UserProvisioningFilter userProvisioningFilter, JwtAuthenticationConverter jwtAuthenticationConverter) throws Exception { http .authorizeHttpRequests(authorize -> authorize .requestMatchers(HttpMethod.GET, "/api/v1/published-events/**").permitAll() // Catch all rule .anyRequest().authenticated()) .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter) // Add this )) .addFilterAfter(userProvisioningFilter, BearerTokenAuthenticationFilter.class); return http.build(); }

The key change is adding the jwtAuthenticationConverter to the JWT configuration.

Summary

  • Added a custom JWT converter to extract a user's roles
  • Updated SecurityConfig to use the JWT converter
© 2026 Devtiro Ltd. All rights reserved