1

I am developing a spring boot 3 and spring security 6 application. The application is rest api. I will use spring security, spring security resource sever for authorization. Endpoint will be annotated with @PreAuthorize("hasAuthority('SCOPE_scope-name')") to restrict access to it. I want to use OIDC, JWT.

I will use keycloak as the authorization server. Now, I want to restrict access to resources/api for specific user groups. Suppose I have user a,b,c and x,y. User a,b,c will have access to scopes read:test and write:test. But users x,y will only have access to scope read:test. Even if user x,y asks for the write:test scope, keycloak will not give the scope to these users.

Preferably I want to use user groups to easily manage the users. Preferably some roles will be created and assigned to user groups. Each role will have some set of scopes that the role will allow access to. I tried reading the documentation. It was confusing to me. There are scopes and roles in different places. I did not understand how can I achieve what I want to do.

According to spring security documentation, In the application.properties file I just have to set spring.security.oauth2.resourceserver.jwt.issuer-uri. I do not understand how keycloak/spring application will know which client/resource server is this application from just the issuer-uri settings.

In keycloak, how do I set, manage scopes for API endpoint for a resource server, and restrict those scopes per user?

Mirza Prangon
  • 115
  • 12

2 Answers2

2

Scopes Are Not Roles

You may see:

  • roles as what a user is allowed to do
  • scope as what a client is allowed to do on behalf of a user

Scope is defined the OAuth2 spec, but there is nothing about roles, groups, permissions, grants or whatever user access representation. Authorization servers vendors use private claims for that.

By default, Keycloak uses realm_access.roles and resource_access.{client-ID}.roles, but you may define your own mappers to use any claim you like.

Converting Keycloak Roles to Spring Authorities

As there is no standard for roles in OAuth2, Spring default authorities converter uses scope claim as source and prefixes each entry with SCOPE_.

This is rarely satisfying and you frequently need to provide with your own authorities converter.

Complete Working Samples

Refer to my tutorials. In addition to working samples, you'll find some of the OAuth2 / OpenID background you seem to be missing.

ch4mp
  • 6,622
  • 6
  • 29
  • 49
  • Thank you. I expected that keycloak have something similar to [Auth0 like this](https://auth0.com/docs/get-started/apis/scopes/api-scopes#example-an-api-called-by-a-first-party-application0). – Mirza Prangon Jun 06 '23 at 23:37
  • 1
    As stated in my answer, scope is part of OAuth2 standard and Keycloak is compliant with OpenID (which is an OAuth2 specialization), so, of course, Keycloak allows you to define custom scopes and to grant clients with scopes (eventually with user consent), just as Auth0 which is (somehow) OpenID compliant too. But this are different concerns: again, roles are intended to define what a **user** is allowed to do and scope what a **client** is allowed to do on behalf of a user. By the way, Auth0 has user roles too (but puts it in different private claims than Keycloak, see my tutorials) – ch4mp Jun 06 '23 at 23:44
  • Thank you. What is the common way of restricting API for users? Meaning managing what a user is allowed to do. As far as I can understand, from resource server's perspective, if the token has the required scopes, it will process the request. OpenAPI spec does not even have roles for security, only scope. – Mirza Prangon Jun 07 '23 at 00:08
  • The links in my answer are there to be followed. I don't know how expectations on access tokens private claims should be specified with OpenAPI (I'm not even sure it is possible). You just define user access with Spring Security. – ch4mp Jun 07 '23 at 05:03
1

Scopes are fixed at design time and do not provide a full authorization solution. When you want dynamic authorization per user, you must use claims instead. Arrange users into roles however you would like. In Keycloak, at the time of token issuance, include values such as these in tokens:

{
  scope: orders,
  role: customer,
  access_level: read
}

More generally think of a scope representing a business area and being composed of claims. Look into Keycloak claims mappers for the vendor specific way to manage this.

USING CLAIMS

Your Spring resource server will then just receive such values in the authenticated principal, after validating the JWT access token. You can then use these values in the API's business authorization code.

Here is some example code of mine, to give you an idea of how claims can be injected then used for business authorization. Annotations may work to deny access in some cases. In others you will need to do things like filter collections to contain only authorized items.

Gary Archer
  • 22,534
  • 2
  • 12
  • 24
  • Sorry I did not understand. If I use claim, will I be able to use `@PreAuthorize("hasAuthority('SCOPE_scope-name')"`? – Mirza Prangon Jun 06 '23 at 07:01
  • No, because scopes are fixed at design time for all users. You can perhaps use roles in a similar way though, as in [this answer](https://stackoverflow.com/questions/59379645/spring-security-5-populating-authorities-based-on-jwt-claims). – Gary Archer Jun 06 '23 at 13:30