Migrate deprecated Keycloak adapter with Spring Security 6
I’m maintaining an application, which is using the Keycloak Spring Security adapter. Now that Keycloak deprecated their adapter, without providing a migration guide, I had to find a solution.
Migration from Keycloak adapter to default Spring Security
The application has a UI, which has a login-flow handled by Keycloak.
First remove all Keycloak dependencies from your project, like org.keycloak:keycloak-spring-boot-starter
.
For this login-flow you need to add the org.springframework.security:spring-security-oauth2-client
dependency.
Migrate Keycloak adapter properties to Spring properties
Now it’s time to convert the Keycloak properties to Spring Security configuration.
keycloak:
realm: master
auth-server-url: [link to your keycloak server]
resource: master
principal-attribute: preferred_username
credentials:
secret: ${keycloak_client_secret}
security-constraints:
- auth-roles:
- admin
- user
- editor
security-collections:
- patterns:
- /web/*
- security-collections:
- patterns:
- /web/unsecured
First move the Keycloak server properties to the corresponding Spring Security properties
spring:
security:
oauth2:
client:
provider:
keycloak:
user-name-attribute: preferred_username
issuer-uri: ${keycloak-server-url}/realms/master (1)
registration:
keycloak:
client-id: ares
client-secret: ${keycloak_client_secret}
authorization-grant-type: authorization_code
scope: openid
1 | Spring Security will figure out the server’s configuration through the issuer-uri -property. It will get the configuration by requesting it from ${keycloak-server-url}/realms/{realm-name}/.well-known/openid-configuration |
Migrate Keycloak adapter properties to Spring Security Configuration
Next migrate the secured URLs to Spring Security configuration.
In this case everything under /web
can only be accessed by users with roles admin, user, editor
, except /web/unsecured
!
@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration {
@Bean
SecurityFilterChain webOAuth2FilterChain(final HttpSecurity httpSecurity) {
return httpSecurity
.authorizeHttpRequests(authorize ->
authorize
.requestMatchers("/web/**").hasAnyAuthority("user", "editor", "admin")
.requestMatchers("/web/unsecured").permitAll()
.anyRequest().permitAll()
)
.oauth2Login(withDefaults()) (1)
.build();
}
}
1 | .oauth2Login(withDefaults()) takes care of redirecting the user to a login page, to actual login and will redirect to the originally requested page after logging in successfully. |
When you run the application with this configuration, it won’t work. The roles defined in Keycloak aren’t parsed from the token. This is because JWT doesn’t have a standard way to define roles in a token.
Parsing Keycloak roles to GrantedAuthorities
Keycloak adds the roles in the token, but Spring Security isn’t configured to parse the Keycloak-specific roles part of the token.
Keycloak stores them in $.realm_access.roles
.
So we need a way to parse these roles.
@Bean
GrantedAuthoritiesMapper grantedAuthoritiesMapper() {
return authorities -> authorities.stream()
.filter(authority -> authority instanceof OidcUserAuthority)
.map(authority -> (OidcUserAuthority) authority)
.map(oidcUserAuthority -> (Map<String, Object>) oidcUserAuthority.getIdToken().getClaims().get("realm_access"))
.map(realmAccess -> ((List<String>) realmAccess.get("roles"))
.stream().map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet()))
.flatMap(Collection::stream)
.collect(Collectors.toCollection(() -> new HashSet<GrantedAuthority>()));
}
OAuth2 works with different tokens, like idToken, userToken and accessToken. Make sure you configure Keycloak properly to add the roles in the needed token. In this example the roles not only need to be included in the accessToken, but also in the idToken for Spring Security to parse the roles properly. |
Now restart the application, and you’ll see it’s all working now.
Last words
I’ve read a lot on StackOverflow and tried to understand the Spring Security documentation, but you’ll need quite some understanding about OAuth2 to be able to understand the Spring Security documentation. If you, like me, need to migrate away from the Keycloak adapter but don’t have the time to fully understand Spring Security 6 and OAuth2, this blog might help you.
If you want more understanding about these subjects I can recommend examples created by Ch4mpy
on GitHub.
The user wrote a more human-friendly way to explain OAuth2 in combination with Spring Security 6 and has a lot of examples.
Ch4mpy
developed a library to easily migrate away from the Keycloak adapter, but you still depend on a library for your Spring Security configuration.
This situation is explained in the Spring Security documentation, in the OAuth2 Log In, Advanced Configuration section.