Small Spring Boot auto-configuration for apps that sit behind Keycloak as a resource server.
It fixes the one thing that trips up almost every Spring + Keycloak setup: Spring Security's
default JWT converter has no idea that Keycloak puts roles under realm_access.roles and
resource_access.<clientId>.roles instead of a flat scope claim. Without this, hasRole(...)
and @PreAuthorize silently do nothing because the token never produces any ROLE_* authority.
I've hit this same wiring on every Keycloak-secured backend I've built over the last two years (realms, custom SPI authenticators, the works), and copy-pasted a version of this converter into each one. This is that code, finally pulled out, tested, and packaged so I stop rewriting it.
Quick install: add the JitPack repository, then
com.github.jihedbfr-art:spring-keycloak-toolkit:v0.1.0 as a dependency — see Install
below for the full snippet and the local-build alternative.
KeycloakRealmRoleConverter— reads realm and/or client roles off the JWT and maps them toSimpleGrantedAuthoritywith a configurable prefix (defaults toROLE_, matching Spring Security's own convention).- Auto-configuration that registers a
JwtAuthenticationConverterwired to the role converter, sospring-security-oauth2-resource-serverpicks it up with zero extra config in the common case. ProblemDetailAuthenticationEntryPoint/ProblemDetailAccessDeniedHandler— RFC 7807 JSON bodies for 401/403 instead of Spring Security's default empty response. Not wired into your filter chain automatically (every app matches endpoints differently), just exposed as beans you plug intoexceptionHandling(...).
It does not configure your SecurityFilterChain, does not touch endpoint matchers, and does not
assume a particular realm/client layout beyond "roles live where Keycloak puts them." Auto-wiring
a security chain for you would mean guessing at your public endpoints, and getting that wrong
silently is worse than writing four lines of config yourself.
Via JitPack — add the JitPack repo, then pull the tag as a dependency:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories><dependency>
<groupId>com.github.jihedbfr-art</groupId>
<artifactId>spring-keycloak-toolkit</artifactId>
<version>v0.1.0</version>
</dependency>Or build and install locally:
git clone https://github.com/jihedbfr-art/spring-keycloak-toolkit.git
cd spring-keycloak-toolkit
mvn clean install<dependency>
<groupId>io.github.jihedbfr-art</groupId>
<artifactId>keycloak-toolkit-spring-boot-starter</artifactId>
<version>0.1.0</version>
</dependency>Point it at your Keycloak client ID and you're done for role mapping:
jihedapps:
keycloak-toolkit:
resource-id: my-client # the Keycloak client whose resource_access roles you want
resource-roles-enabled: true
realm-roles-enabled: true # on by defaultIf you want the JSON error bodies too:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http,
ProblemDetailAuthenticationEntryPoint entryPoint,
ProblemDetailAccessDeniedHandler accessDeniedHandler) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.exceptionHandling(e -> e
.authenticationEntryPoint(entryPoint)
.accessDeniedHandler(accessDeniedHandler));
return http.build();
}| Property | Default | What it does |
|---|---|---|
jihedapps.keycloak-toolkit.realm-roles-enabled |
true |
Read realm_access.roles |
jihedapps.keycloak-toolkit.resource-roles-enabled |
true |
Read resource_access.<resource-id>.roles |
jihedapps.keycloak-toolkit.resource-id |
(none) | Keycloak client ID for resource roles; skipped if blank |
jihedapps.keycloak-toolkit.role-prefix |
ROLE_ |
Prefix applied before each role |
jihedapps.keycloak-toolkit.problem-details-enabled |
true |
Register the ProblemDetail entry point / handler beans |
Built and tested against Spring Boot 3.2.5 / Spring Security 6 / Java 17, which is what I run in production. Older Spring Boot 3.x lines will likely work since the autoconfigure API in scope here has been stable, but I haven't tested them.
Things I want to add once this has seen a bit more real use rather than guessing upfront:
- multi-tenant support (more than one
resource-idat a time) - a test fixture module (
JwtTestUtilsor similar) so consumers don't hand-roll fake JWTs the way the tests in this repo do - Maven Central publishing — the
releaseprofile inpom.xmlis wired up (signing + Central Portal), just waiting on a Sonatype account and a GPG keypair
MIT — see LICENSE.