Home > OS >  Keycloak: resource rolebased acccess management
Keycloak: resource rolebased acccess management

Time:01-24

Input data:

  • eureka (localhost:8761)
  • spring boot cloud gateway service with keycloak (localhost:8765)
  • developer resource service (localhost:8082)
  • kecloak (localhost:8080)

Keycloak:

  • created realm
  • created client with auth ON
  • created 2 users with 2 different roles: developer and manager
  • created 1 resource for path /developer/** (it's a prefix for my developer-service endpoint)
  • created role based policy for role=developer (required!)
  • created resource permission based on policy above

Case: in browser making request

http://localhost:8765/developer/developers

logging in as manager !!!

EXPECTED: access denied

ACTUAL: 200 with response = list of developers

QUESTION: have i missed something ? Is this role permission filtration inside of keacloak already? Have already watched several videos and posts, some of them are based on front-end keycloak-js lib and filtration, backend @RolesAllowed. I'm just curious if it's possible to block the request just using the keycloak admin console?

GATEWAY yaml:

server:
  port: 8765

logging:
  level:
    root: info

eureka:
  client:
    serviceUrl:
      defaultZone: http://eurekauser:eureka!@localhost:8761/eureka
  instance:
    hostname: localhost
    prefer-ip-address: false

spring:
  application:
    name: GATEWAY
  cloud:
    gateway:
      discovery.locator.enabled: true
      routes:
        - id: developer
          uri: lb://DEVELOPER
          predicates:
            - Path=/developer/**
          filters:
            TokenRelay=
  security:
    oauth2:
      client:
        registration:
          keycloak:
            provider: keycloak
            client-id: test_client
            client-secret: lBIz3la07j3a5uEEFdQgoapFa4s1seeD
            authorization-grant-type: authorization_code
            redirect-uri: "http://localhost:${server.port}/login/oauth2/code/{registrationId}"
            scope:
              - openid
        provider:
          keycloak:
            issuer-uri: http://localhost:8080/realms/TestRealm
            authorization-uri: http://localhost:8080/realms/TestRealm/protocol/openid-connect/auth
            token-uri: http://localhost:8080/realms/TestRealm/protocol/openid-connect/token
            user-info-uri: http://localhost:8080/realms/TestRealm/protocol/openid-connect/userinfo
            jwk-set-uri: http://localhost:8080/realms/TestRealm/protocol/openid-connect/certs
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss

DEVELOPER SERVICE: YAML:

server:
  port: 8082
  error:
    include-message: always
  servlet:
    context-path: /developer

spring:
  application:
    name: DEVELOPER
  security:
    oauth2:
      resource-server:
        jwt:
          jwk-set-uri: http://localhost:8080/realms/TestRealm/protocol/openid-connect/certs

eureka:
  client:
    serviceUrl:
      defaultZone: http://eurekauser:eureka!@localhost:8761/eureka
  instance:
    prefer-ip-address: false
    hostname: localhost

Endpoint:

@RestController
@RequestMapping("/developers")
class DevelopController {

    private val developers = mapOf(
        Pair(1L, "developer#1"),
        Pair(2L, "developer#2"),
        Pair(3L, "developer#3"),
        Pair(4L, "developer#4"),
        Pair(5L, "developer#5")
    )

    @GetMapping
    fun findAll(authentication: Authentication) = developers.entries

    @GetMapping("/{developerId}")
    fun findById(@PathVariable developerId: Long): String = developers[developerId] ?: let {
        throw RuntimeException("Not found by id=$developerId")
    }
}

CodePudding user response:

Role based access-control won't be achieved on access-token emission: an access-token is emitted for a given user on a specific client and can be used to authorize many requests to many resources.

Details for role-based access-control in Spring-boot resource-servers in the accepted answer to "Use Keycloak Spring Adapter with Spring Boot 3"

There is an authorization-service in Keycloak which you could use to centralize access-control, but:

  • as Keycloak adapters for Spring are not ported to spring-security 6, it requires you to write your own client service and authentication filter
  • this service is not called before the request reaches the resource-server, it is called by the resource-server to delegate the decision to grant an access to a resource for a request with a given access-token
  • this is pretty inefficient compared to evaluating, on the resource-server(s), the roles contained in a JWT (which requires only one single request to Keycloak at startup): with authorization-service, a request is made to Keycloak for each and every request to resource-server (just like when introspection is used instead of JWT decoding)
  • it is very Keycloak adherent: there is no standard around it and access-control rules must be defined in Keycloak => if Keycloak stops supporting it or if you have to switch to another OIDC authorization-server, you're screwed
  • it is not easily testable (you can easily write unit-tests of Spring access-control rules with mocked identities on resource-server)
  • it is, in my opinion, harder to figure out exactly what access-control rules apply to a specific resource when all rules are put at the same place, compared to method security with SpEL on @Controller method
  • Related