Granted Authorities from an endpoint? #205
-
I've been using spring-addons and it's been great. Recently, I have been implementing permissions based authentication and so far However, as more roles are being added the token is now too big for the request headers. I have increased the max headers size a few times but was wondering if there is an alternative solution. From a few research, I've seen on the keycloak forem they recommended excluding the claims from the JWT token and instead getting them to the User info endpoint. How can I get the roles from the user info and convert them into granted Authorities? Is it as simple as passing the hashmap to the @Configuration
@EnableMethodSecurity
public static class SecurityConfig {
@Bean
Converter<Jwt, OAuthentication<OpenidClaimSet>> authenticationFactory(Converter<Map<String, Object>, Collection<? extends GrantedAuthority>> authoritiesConverter) {
return jwt -> new OAuthentication<>(new OpenidClaimSet(jwt.getClaims()),
authoritiesConverter.convert(jwt.getClaims()), jwt.getTokenValue());
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Hi @david-randoll ! I'm glad to read that spring-addons is useful to you ;) As a preamble, the best option is probably to find a way to keep the tokens small enough to avoid that roundtrip to the authorization server: it will add some latency to the requests and some extra load on the authorization server (you could use some caching, but still, these is quite some resources wasting). As it is pretty rare to need hundreds of roles for a single client, an option I'd recommend to evaluate is to limit the usage of "realm" roles as much as possible and favor the usage of "client" roles. With correct client roles mappers, you could probably seriously reduce tokens size. That said, yes you can use whatever you like as authorities source, even a database or web-service call, when converting a If you want access to the raw token string to call the To write your own authorities converter, you may get some inspiration from the You'll probably end with something like that (exposing such a bean would be just enough with spring-addons): @Component
public class RemoteGrantedAuthoritiesRetriever implements ClaimSetAuthoritiesConverter {
private final KeycloakAdminApi keycloak;
private final String keycloakRealm;
public RemoteGrantedAuthoritiesRetriever(KeycloakAdminApi keycloak, @Value("${keycloak-realm}") String keycloakRealm) {
this.keycloak = keycloak;
this.keycloakRealm = keycloakRealm;
}
@Override
@Cacheable
public Set<GrantedAuthority> convert(@NonNull Map<String, Object> claims) {
final var userRoleMappings = keycloak.getUserRoleMappings(keycloakRealm, (String) claims.get(JwtClaimNames.SUB));
// see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#MappingsRepresentation
final var realmRoles = ((List<Map<String, Object>>) userRoleMappings.get("realmMappings")).stream();
final var clientsRoles = ((Map<String, List<Map<String, Object>>>) userRoleMappings.get("clientMappings")).values().stream().flatMap(List::stream);
return Stream.concat(realmRoles, clientsRoles).map(role -> (String) role.get("name")).map(SimpleGrantedAuthority::new).collect(Collectors.toSet());
}
} With: @HttpExchange(accept = MediaType.APPLICATION_JSON_VALUE)
public interface KeycloakAdminApi {
@GetExchange(url = "/admin/realms/{realm}/users/{user-id}/role-mappings")
Map<String, Object> getUserRoleMappings(@PathVariable(name = "realm") String realm, @PathVariable(name = "user-id") String userId);
} You can of course use your favorite REST client as usual to send the query, but you could also have a look at the new "magically generated" Again, think twice before implementing such a solution. It would be preferable to remove some fat from tokens rather than adding a roundtrip to the authorization server. For that, you could be using client roles, or smarter Keycloak mappers, or whatever (hard to tell without knowing your system internals). |
Beta Was this translation helpful? Give feedback.
Hi @david-randoll !
I'm glad to read that spring-addons is useful to you ;)
As a preamble, the best option is probably to find a way to keep the tokens small enough to avoid that roundtrip to the authorization server: it will add some latency to the requests and some extra load on the authorization server (you could use some caching, but still, these is quite some resources wasting). As it is pretty rare to need hundreds of roles for a single client, an option I'd recommend to evaluate is to limit the usage of "realm" roles as much as possible and favor the usage of "client" roles. With correct client roles mappers, you could probably seriously reduce tokens size.
That said, yes you can u…