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:
- Gets the
realm_accessclaim from the JWT - Checks if the claim exists and contains roles
- Extracts the roles and converts them to Spring Security's
SimpleGrantedAuthorityobjects
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
SecurityConfigto use the JWT converter