Spring boot + Keycloak + Permission based on authorization scope + Spring security

537 views
Skip to first unread message

Salvatore Schiavottiello

unread,
Oct 30, 2023, 3:11:18 AM10/30/23
to Keycloak User

Bad, good or best practice?

Context: Spring boot + Keycloak + Permission based on authorization scope + Spring security

Requirement: Secure an API, MyService, developed with Spring boot and RestController. The client provides an authentication JWT


Details:

  • The JWT is provided by a front end via a standard flow
  • Permissions on keycloak are defined via granularly configured permissions, for example GET:STATUS is an authorization scope to read the states of a resource.

Keycloak:

  • Added two clients to keycloak, one for the front end application (standard flow) called client_fe and one called client_service for the states service.
  • Permissions configurations for client_service:
    • n. 3 AUTHORIZATION_SCOPE: DELETE:STATUS, GET:STATUS e POST:STATUS
    • n. 1 RESOURCE called status-check-resource with URI=/users/status/check and scopes: DELETE:STATUS, GET:STATUS e POST:STATUS
    • n. 2 POLICY based on role:
      • only_configurator based on role configurator
      • only_doctor based on role doctor
    • n. 3 PERMISSIONS based on scope
      • delete-status-permission, DELETE:STATUS, only_configurator and only_doctor
      • get-status-permission, GET:STATUS, only_doctor
      • post-status-permission, POST:STATUS, only_configurator


Spring Boot (MyService):

pom.xml

org.springframework.boot
spring-boot-starter-oauth2-resource-server

org.springframework.boot
spring-boot-starter-security


application.yml

spring.security.oauth2.resourceserver.jwt.jwk-set-uri = http://localhost:8080/realms/myrealm/protocol/openid-connect/certs


Parts of java controller class StatusController (@RestController, @RequestMapping("/users"))

  • @PreAuthorize("hasAuthority('SCOPE_GET:STATUS')")
  • @GetMapping("/status/check"){...}
  • @PreAuthorize("hasAuthority('SCOPE_DELETE:STATUS')")
  • @DeleteMapping("/status/check"){...}
  • @PreAuthorize("hasAuthority('SCOPE_POST:STATUS')")
  • @PostMapping("/status/check"){...}


Spring security layer configuration

...

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

  JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
  jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(new KeycloakScopeConverter());

  http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()).

    oauth2ResourceServer( oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter))
  

  );

  return http.build();
}

KeycloakScopeConverter, a java class that, given the incoming JWT (the one provided by the front end), obtains an RPT token and extracts the authorization scopes from this.

makes an http POST with

url: http://localhost:8080/realms/myrealm/protocol/openid-connect/token

grant_type: urn:ietf:params:oauth:grant-type:uma-ticket

audience: client_service

header: "Authorization", " Bearer " + jwt.getTokenValue()) (jwt fornito dal FE)

The scopes are extracted from the array permissions:

ArrayList _scopes = new ArrayList<>();
  for (Map permission : permissions) {
  ArrayList scopes = (ArrayList) permission.get(“scopes”);
  if (scopes != null) {
    for (String scope : scopes) {
      _scopes.add(scope);
    }
  }
}

Collection returnValue = scopes.stream().map(scope -> “SCOPE“ + scope).map(SimpleGrantedAuthority::new).collect(Collectors.toList());

In this way a control using the @PreAuthorize("hasAuthority("...")) 

The question is: Is this a bad, good or best practice?

Over to you.

Björn Sonntag

unread,
Oct 30, 2023, 5:40:09 AM10/30/23
to Salvatore Schiavottiello, Keycloak User
Hello,

this is a valid solution, usually depends this on the project environment / circumstances. Most companies uses an LDAP or (Azure) AD to store user informations; in this case is it easier to map an AD group to a role instead of a scope.


With best regards

Björn


--
You received this message because you are subscribed to the Google Groups "Keycloak User" group.
To unsubscribe from this group and stop receiving emails from it, send an email to keycloak-use...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/keycloak-user/fd3b207c-1090-42f5-93a3-5e9e192821b5n%40googlegroups.com.

Reply all
Reply to author
Forward
0 new messages