Where should be user group membership stored for RBAC when using OAuth to access SPA and REST API

Viewed 522

I am working on implementing oauth2 to secure web app that will call REST API as well as give access to other potential clients to access the same rest API. I would like to use role based access to control the data returned from the API.
I will use Keycloak as the Authorization server as well as for user / group management.

The use case is that

  1. I will create keycloak realm with public client (SPA) and confidential possibly bearer only client (REST API) and also groups and users who will be part of those groups
  2. User will login to the SPA via authorization flow and will receive an access token.
  3. The SPA will make the request (XHR) to the REST service passing the token as a Bearer token and retrieves the data or perform an operation that is permitted based on the group that the user is in.

I am trying to understand / best practice where would I should store the list of groups that the user is part of. Is it in the access token or in the ID token that can be retrieved and passed by the SPA and/or REST service would have to retrieve that data from the Authorization server using the access token and the userinfo endpoint. It seems that keycloak uses JWT for both access and ID token and roles / groups can be included in both. I read mixed suggestions that access token should not be read by the REST service and only used to prove that user is authenticated but then I see that it is used to pass user groups.

Another question that I have is that if I want to allow an automated client to access the REST API which won't be able to use authorization flow is it in the best practice to use client flow and on board that client in keycloak and provide the client clientId and secret to be able to retrieve access token use it to authenticate to the REST service (Bearer authentication header)

UPDATE

I have few more follow up questions to hopefully make it all clear.

As far as ID Token, I am thinking that the ID token is only should be used by the application (SPA) that is authenticating the user and will get information about the user (username, email, and few other things) based on the claims and user approving permissions. Possibly to display those things in the app. The ID token should not (never) be sent to the REST API to retrieve the data.

On the other hand Access Token should not be read the the application (SPA) but used in every request to the API server (Bearer $AUTH_TOKEN) with API server validating the token and then retrieving users's groups information and return allowed response.

What is still not clear is that if an application received an authorization token doesn't that mean the user is authenticated. Why do we need ID Token.

Also, if access token does not always carry information and could be just a random string then how would you know user's permissions. I was reading that there are two types of tokens “identifier type” and “self-contained type”. I am guessing that if the token is an identifier type then REST Service will have to send a request to the authorization server to get that information (groups/permissions) via retrospect api.

Found two good articles on this:
https://darutk.medium.com/oauth-access-token-implementation-30c2e8b90ff0.
https://darutk.medium.com/api-protection-by-id-token-3123481e96f2

2 Answers

Keycloak provides Open ID Connect (OIDC) standard. OIDC is based on OAuth 2.0. You are getting ID token only when you are using OIDC (openid scope in your auth request). ID token should be used for authentication. I would say authentication doesn't need to know anything about user group. Groups/roles are important only for authorization, which is a task for OAuth - access token. So I would keep groups/roles in the access token.

(Unfortunately) real world has already apps, which are not following this convention (hi Concourse :-)). They are expecting groups/roles in the ID token and you can't configure it. That is a good reason why Keycloak supports claims in the access token and also in the ID token.

Recommended blog post: https://medium.com/@nilasini/id-token-vs-access-token-17e7dd622084

OAuth offers client credentials flow for machine to machine application, so it is a right choice for automated clients, such as cron/CI scripts, 3rd party app access, ..., where you don't need user identity.

You should always put authorization information in your access tokens, not ID tokens. The information about the user's groups can appear in the ID token if, for example, you need them in your client - maybe you want to color-code some information based on the user's groups, etc.

Another question that I have is that if I want to allow an automated client to access the REST API which won't be able to use authorization flow is it in the best practice to use client flow and on board that client in keycloak and provide the client clientId and secret to be able to retrieve access token use it to authenticate to the REST service (Bearer authentication header)

As Jan pointed out, the client credentials flow is created exactly for this purpose - so that backends can talk to each other. It is a means of authenticating a client.

What is still not clear is that if an application received an authorization token doesn't that mean the user is authenticated. Why do we need ID Token.

It's exactly as you've written - access tokens are meant for the API, ID tokens are meant for the client. Of course, when your client obtains an access token it means that the user is authenticated, but many clients want to have additional information about the authenticated user, so they can e.g. display your profile image, your username, know your email, etc. Hence the ID token - the ID token gives you information about the authenticated user, not only proves that they are authenticated. If all you need is a proof that a user authenticated then an access token is sufficient.

Also, if access token does not always carry information and could be just a random string then how would you know user's permissions. I was reading that there are two types of tokens “identifier type” and “self-contained type”. I am guessing that if the token is an identifier type then REST Service will have to send a request to the authorization server to get that information (groups/permissions) via retrospect api.

Exactly, if you use an opaque string as the access token then the API will have to perform an introspection flow (a standard defined in RFC 7662) to the Authorization Server to validate the token and get the associated authorization information (like user's groups, etc.). If you're concerned that this is a lot of traffic between your services and the Authorization Server you can have a look at a pattern called the Phantom Token flow which utilises API Gateway and its cache to perform the introspection instead of the services themselves. I would recommend using this approach especially as you said you will have 3d party clients accessing your API. It is a good practice to not share JWT access tokens with 3d party integrators.

Have a look also at this article about API Security Best Practices, it may shine some more light on this stuff.

Related