diff --git a/articles/flow/integrations/spring/oauth2.adoc b/articles/flow/integrations/spring/oauth2.adoc index f6c32449a4..6f65df66ff 100644 --- a/articles/flow/integrations/spring/oauth2.adoc +++ b/articles/flow/integrations/spring/oauth2.adoc @@ -114,4 +114,37 @@ class SecurityConfiguration { The [methodname]`oauth2LoginPage(String)` method is a shortcut that defaults the post-logout redirect URL to `{baseUrl}`. +== Mapping Keycloak Roles to Authorities + +Keycloak puts a user's roles into the access token instead of the ID token, so Spring Security's default `OidcUserService` never sees them: `@RolesAllowed("admin")` and `hasRole("admin")` don't match a Keycloak role named `admin`. + +Call [methodname]`keycloakRoleMapping` next to [methodname]`oauth2LoginPage` to decode the access token and turn its roles into granted authorities: + +[source,java] +---- + +@Configuration +class SecurityConfiguration { + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.with(VaadinSecurityConfigurer.vaadin(), configurer -> { + configurer.oauth2LoginPage("/oauth2/authorization/keycloak") + .keycloakRoleMapping(); + }); + return http.build(); + } +} +---- + +This maps the realm roles from the `realm_access` claim, the roles that `resource_access` grants for the current client ID, and the token's scopes as `SCOPE_`-prefixed authorities. Roles that `resource_access` grants to other clients are ignored. + +[methodname]`keycloakRoleMapping` only takes effect together with [methodname]`oauth2LoginPage` and its overloads, and it sets the `OidcUserService` that this security filter chain uses to load the authenticated user. An application that configures its own [classname]`OidcUserService` should leave [methodname]`keycloakRoleMapping` off and install the mapper on that service directly instead: + +[source,java] +---- +var oidcUserService = new OidcUserService(); +oidcUserService.setOidcUserConverter(new KeycloakOidcUserMapper()); +---- + + [discussion-id]`EF8F6AC3-BE67-4BE2-9A78-C371C1D4B9FD` diff --git a/articles/flow/security/vaadin-security-configurer.adoc b/articles/flow/security/vaadin-security-configurer.adoc index 7e2a786187..657aba57c6 100644 --- a/articles/flow/security/vaadin-security-configurer.adoc +++ b/articles/flow/security/vaadin-security-configurer.adoc @@ -57,6 +57,7 @@ The following beans are shared by this configurer (if not already shared): * `VaadinRolePrefixHolder` — Holds role prefix accessible outside an active request * `VaadinDefaultRequestCache` — A request cache implementation which ignores requests that are not for routes * `VaadinSavedRequestAwareAuthenticationSuccessHandler` — A strategy that uses an available VaadinSession for retrieving the security context +* `OidcUserService` — Set when Keycloak role mapping is enabled with `keycloakRoleMapping()` ==== Configuration Methods @@ -106,6 +107,13 @@ public VaadinSecurityConfigurer oauth2LoginPage(String oauth2LoginPage, String p Configures the login page for OAuth2 authentication and the post-logout redirect URI. +[source,java] +---- +public VaadinSecurityConfigurer keycloakRoleMapping() +---- + +Enables mapping of Keycloak realm and client roles to Spring Security granted authorities (disabled by default), so that `@RolesAllowed("admin")` and `hasRole("admin")` match a Keycloak role named `admin`. Works only together with `oauth2LoginPage(String)` and its overloads, and sets the `OidcUserService` that this security filter chain uses to load the authenticated user. An application that has its own `OidcUserService` should leave this off and install the mapper on that service instead. See <<{articles}/flow/integrations/spring/oauth2#,OAuth2 Authentication>> for details on what is mapped. + ===== Logout Configuration [source,java]