Why is there no fine-grained authorizaion for public clients

Hey there,

I’ve looked at dozens of guides and while some do mention that you need a confidential client to enable fine-grained (and user managed) authorization none of them explain why that is. I’m failing to see the point why user authentication with a public client (e.g. a web app) should not allow me to manage user-owned resources for that client.

The exact use case is a common one (at least I would’ve guessed):

  • web app routes unauthenticated user to keycloak to get an access token
  • web app uses that access token to perform api requests against a backend service

The beauty of this would be that it makes the backend service stateless.

Now the trouble is that i can not enable fine-grained authorization unless I make the client confidential. But i cannot make the client confidential without exposing the client key in the web app.

I’ve found guides showing a setup with two clients: app-front is a public client, used by the web app, no client secret; app-back is a confidential client, used by the backend and has a client secret. This works fine for calls agains the backend, i can validate the access key and allow api calls based on roles embedded in the jwt. I cannot, however, use that same access token to check resource permissions on keycloak however (using authz client), as keycloak validates the access token based on the iss key, and the token is issued to app-front, not app-back.

Now I’ve found that I can have the backend exchange token it got from the frontend for another token using the token_exchange grant, and that new token, I can use to look up resource permissions. Being statless, however, means the backend app will need to perform this exchange for every single request.

Is this the right way to do? What am I missing? Thank you for any hints.

I’m not sure if this helps, but you can use a bearer token from the user to make pdp requests from the backend resource server like this:

curl -u <client_id>:<client_secret> -k -X POST \
  https://localhost:8443/auth/realms/keycloaktest/protocol/openid-connect/token \
  -H 'content-type: application/x-www-form-urlencoded' \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "subject_token=<user_access_token>" \
  --data "permission=<resource#scope>" \
  --data "response_mode=decision" \
  --data "audience=<client_id>"

This will work even though the client_ids do not match? Will need to try this. For now, we’ve chosen to do fine-grained authorizaton in the application layer since checking authorization for every single request would be a performance penalty we’re not ready to pay.