30

We are using keycloak-adapter with Jetty for authentication and authorization using Keycloak. As per Keycloak doc for OIDC Auth flow:

Another important aspect of this flow is the concept of a public vs. a confidential client. Confidential clients are required to provide a client secret when they exchange the temporary codes for tokens. Public clients are not required to provide this client secret. Public clients are perfectly fine so long as HTTPS is strictly enforced and you are very strict about what redirect URIs are registered for the client.

HTML5/JavaScript clients always have to be public clients because there is no way to transmit the client secret to them in a secure manner.

We have webapps which connect to Jetty and use auth. So, we have created a public client and it works awesome for webapp/REST authentication.
The problem is as soon as we enable authorization, client type gets converted to Confidential from Public and it does not allow the reset it as Public. Now, we are in soup. We cannot have public clients due to authorization and we cannot connect webapps to confidential client.
This seems to be contradictory to us. Any idea why client needs to be confidential for authorization? Any help on this how can we overcome this issue?
Thanks.

Ilya Serbis
  • 21,149
  • 6
  • 87
  • 74
NumeroUno
  • 1,100
  • 2
  • 14
  • 34
  • Any pointer here please? – NumeroUno Nov 05 '18 at 06:04
  • Which OAuth flow were you using? Was it auth code grant, implicit grant, or something else? – RrR- Nov 19 '18 at 08:49
  • It is normal grant flow based on username/password and jwt token. – NumeroUno Nov 19 '18 at 15:12
  • Both auth code and implicit flow are based on that concept, but created for different purposes. – RrR- Nov 20 '18 at 08:12
  • I believe the jetty app that is a resource server should be private. Your front end client should be public because someone can just use the client and see the client secret in the developer console. I think your question needs more information about the architecture and problem at hand before anyone can help you. – Thomas Lann Jan 10 '19 at 01:29

4 Answers4

31

As far as I understood, you have your frontend and backend applications separated. If your frontend is a static web-app and not being served by the same backend application (server), and your backend is a simple REST API - then you would have two Keycloak clients configured:

  • public client for the frontend app. It would be responsible for acquiring JWT tokens.
  • bearer-only client, which would be attached to your backend application.

To enable authorization you would create roles (either realm or client scoped, start on the realm level as it's easier to comprehend). Every user would then be assigned a role/s in the Keycloak admin UI. Based on this you should configure your keycloak adapter configuration (on the backend).

All things considered, in order to talk to your REST API, you would attach a JWT token to each HTTP request in the Authorization header. Depending on your frontend framework, you can use either of these:

P.S. For debugging I have just written a CLI tool called brauzie that would help you fetch and analyse your JWT tokens (scopes, roles, etc.). It could be used for both public and confidential clients. You could as well use Postman and https://jwt.io

HTH :)

maslick
  • 2,903
  • 3
  • 28
  • 50
  • Is it correct that my backend rest api still to work when I have logged out (from my front app or close session with keycloak admin console) before expiration time? I've a public client in keycloak and my backend app is setup with bearer only. – Hector Aug 19 '20 at 17:59
  • 3
    Can you go into detail how the authorization is supposed to be configured then? If the REST API uses the JWT token passed from the client to evaluate resource access with keycloak, I'm getting `Client application [public-client] is not registered as a resource server.` since keycloak evaluates the `azp` (issued-for) field of the JWT https://github.com/keycloak/keycloak/blob/11.0.3/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java#L133. – lordvlad Feb 18 '21 at 21:00
2

I think you are referring to the "Authorization Enabled" switch in the admin console of Keycloak, when creating a client. If you go over the question mark next to the label, you'll see the hint "Enable/Disable fine grained authorization support for a client.

Create client in Keycloak admin console (v 6.0.1)

This is meant for when you create a client for a backend application that serves as a resource server. In that case, the client will be confidential.

If you want to create a client for a frontend app, to authenticate a user and obtain an JWT, then you don't need this.

See also: https://www.keycloak.org/docs/latest/authorization_services/index.html

  • 1
    Yes, I need this. I need Keycloak to authorize the requests from my SPA (public client) so that I don't need to repeatedly implement the logic in all of my resource server. I don't understand why they don't enable Authorization for public client. – hirikarate Dec 24 '19 at 03:38
2

After much deliberation, we found that authorisation is not required to be enabled on public client really when you connect to it. When any request come to public client, it only does the authentication part. Authorization part is done when actual request lands on the resource server (in our case, Jetty) using the confidential client (as Jetty has knowledge of confidential client configured in it).

NumeroUno
  • 1,100
  • 2
  • 14
  • 34
  • 4
    I still don't get it. Confidential clients still require authentication. If you do the authentication only on the Public client, how do you "pass" the token to the confidential client? or you authenticate again? – monzonj Oct 04 '20 at 09:52
  • In keycloak, you can connect public client to confidential client. This way, public client token will work with confidential client. – NumeroUno Oct 04 '20 at 10:03
  • 1
    @NumeroUno can you please provide more detail, as to how can they be connected? – Amit Dugar Dec 03 '20 at 06:22
  • By connect, I mean to say that if both public and confidential clients are part of same realm, same token will work across them. It is so because token is realm specific, not client. – NumeroUno Dec 03 '20 at 07:18
  • 2
    @NumeroUno any chance you could go a bit into detail? I tried to pass the access token that the public client received to get access to a resource, but since the protection service evaluates the `azp` (issue-for) field of the JWT token, It tries to look up resources on the public client, which has no resources defined: `Client application [public-client] is not registered as a resource server.` – lordvlad Feb 18 '21 at 20:54
  • Hi @lordvlad, I did not get what is protection service. But, for authorization, your server should be connecting to confidential client. Only confidential client support authz. – NumeroUno Feb 19 '21 at 04:34
  • I use `AuthzClient.create(new Configuration(keycloakUrl, realm, CLIENT_ID_AUTHZ, Map.of("secret", getClientSecret(realm, CLIENT_ID_AUTHZ)), null)).protection(userJwt).premission().find(...)` to check if the logged in user is allowed to access a resource. This sends off a permission request to keycloak using `userJwt`. – lordvlad Feb 19 '21 at 08:08
  • Actually we are using keycloak adapter for Jetty and we have configured it with confidential client. If you are using confidential client in same realm as of public client, it should work – NumeroUno Feb 19 '21 at 08:11
  • How are you using authorization? Jetty config or programatically? – lordvlad Feb 19 '21 at 08:45
  • We are using jetty for all * authorization and the having custom logic for resources permission evaluation with the help of RPT token. – NumeroUno Feb 19 '21 at 08:52
  • when getting RPT token, do you authenticate at the protection endpoint using the user JWT or client credentials? i.e. do you `authzClient.protection(userJwt)` or `authzClient.protection()`? – lordvlad Feb 22 '21 at 10:29
  • We call /token endpoint with grant_type=urn:ietf:params:oauth:grant-type:uma-ticket using user jwt token. – NumeroUno Feb 22 '21 at 10:49
  • 2
    okay, that is equivalent with `authzClient.protection(userJwt)`. Now if you authorize with the public client, the `azp` claim in userJwt will point to the public client. If I pass that userJwt from the public client to the confidential client, and let the confidential client ask keycloak for an uma token, it fails, since the token is issued for the public client, and the public client has no authorization configured. How to you switch the context from public to confidential client in your case? – lordvlad Feb 22 '21 at 13:26
  • We pass the jwt to confidential client and it works perfectly. We did not se any issue here and AFAIK, we have not done any extra settings in keycloak – NumeroUno Feb 22 '21 at 13:42
  • @lordvlad Hi! Did you solve this problem somehow? – Harvey Jul 23 '21 at 07:51
  • Might be that our both public and confidential clients are under same realm, it does work correctly. – NumeroUno Jul 23 '21 at 07:57
  • 1
    @Harvey I experimented with [token exchange](https://www.keycloak.org/docs/latest/securing_apps/#_token-exchange) and had it working. Backend exchanges the public client token for a private client token, and performs authz lookup using the new private client token. In the end, I chose to do authorization within our app as the additional roundtrips to keycloak had a really bad impact on our app's response times. – lordvlad Jul 24 '21 at 11:55
  • @lordvlad you rock!! But did you notice the token exchange functionality is still in preview, at least as per documentation? Also, you rightly mentioned a roundtrip on every request is not desirable and kind of defeats the purpose of having keycloak in certain scenarios. By "chose to do authorization within the app," did you mean to say that you send credentials(username, password) from the web app to resource-server(having private client), which in return exchanges credentials + client_id/secret with a token(azp: resource server)? – Ravinder Payal May 10 '22 at 20:48
  • No I mean we chose to have user roles in the token and have those roles quite fine grained. App then checks these roles to determine access privileges. We're planning on extending that further with a custom encoded fine grained grant scheme to reduce token size and add flexibility, as we frequently now have tokens with more than 20 or 30 roles. – lordvlad May 11 '22 at 19:50
1

Any idea why client needs to be confidential for authorization

The client needs to authenticate with keycloak in order to make full use of the protection api, to manage resources, and to create pat tokens.

Web applications should have their own client, with authentication disabled, which use the correct auth flow for SPAs (Auth code flow with PKCE).

The access token issued to the front end client can in fact be used with a different backend client representing the auth server. An example request is documented in the (albeit terse) keycloak documentation here.

curl -X POST \
  http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
  -H "Authorization: Bearer ${access_token}" \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "audience={resource_server_client_id}" \
  --data "permission=Resource A#Scope A" \
  --data "permission=Resource B#Scope B"

The above example shows use of the access token (which may have the frontend client for azp) and the backend client id passed as the audience. This gets around the error mentioned above in the comments following @NumeroUno's answer:

Client application [public-client] is not registered as a resource server. 
w08r
  • 1,639
  • 13
  • 14