0

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")
    }
}
SergeBud
  • 7
  • 1
  • 6

1 Answers1

0

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
ch4mp
  • 6,622
  • 6
  • 29
  • 49
  • thank you for the response But I still don't get the point: in all example posts and videos there were no extra effort and configurations, just create resource and permission based on policy of any kind and that's it. I saw only 1 comment somewhere saying it's enough. In resource-server we set the `jwk-set-uri`, keycloak spring starter is deprecated and there is no place for setting `grant-access-url`. So should I implement any custom logic to enable role access management ? – SergeBud Jan 23 '23 at 16:53
  • Keycloak adapter to its proprietary authorization service was deprecated with the rest of Spring adapters. You'll have to write your own client (essay with WebClient, this just a REST call). But as explained in my answer, I wouldn't do that. Evaluate roles on resource-server instead: https://stackoverflow.com/questions/74571191/use-keycloak-spring-adapter-with-spring-boot-3/74572732#74572732 – ch4mp Jan 23 '23 at 17:22
  • I've already tried such an approach, just implemented custom converter and retrieved roles from token, put into spring-context and used @Secured. Everything is ok with that. I thought, and even watch this demo https://www.youtube.com/watch?v=RupQWmYhrLA&t=2431s&ab_channel=Devoxx and understood, that it's enough just admin console settings (resources/policies/permissions) without coding. – SergeBud Jan 23 '23 at 17:49
  • I don't know how to phrase it: that comes with **all the drawbacks explained in the answer** and, as Keycloak adapters for Spring are not ported to spring-security 6, requires you to write your own client service and authentication filter. – ch4mp Jan 23 '23 at 18:16
  • So no out-of-the box solution. Got it Thank you for your time ! – SergeBud Jan 23 '23 at 18:31