python-keycloak icon indicating copy to clipboard operation
python-keycloak copied to clipboard

Using KeycloakAdmin with a confidential client together with username/password

Open zdenek-biberle opened this issue 2 years ago • 2 comments

Hello,

I have a confidential client in Keycloak and a bunch of users with sufficient roles that I want to use to manage Keycloak via the KeycloakAdmin class.

At first I thought that I could simply instantiate KeycloakAdmin like this:

KeycloakAdmin(server_url='https://my-keycloak/auth/',
              client_id='some-client-id',
              client_secret_key='some-client-secret',
              username='some-username',
              password='some-password')

This should work fine, but it doesn't. I found this piece of code which forces the authentication to use client credentials when client_secret_key is set, which, as far as I can tell, makes it impossible to use the password grant type.

This change seems to have been introduced in this commit to, I suspect, support the use of service accounts.

I guess that KeycloakAdmin should only use the client_credentials grant type whenever client_secret_key is set and username is not set. Does that sound like the way to go?

zdenek-biberle avatar Oct 13 '21 16:10 zdenek-biberle

As far as I understood keycloak you either have a confidential client and use client_id+secret or a public client and use username+password. Thus for me this sounds reasonable.

double-a avatar Oct 16 '21 16:10 double-a

Hello @double-a, thanks for the response.

However, I'm not sure that what you are describing is quite correct. Using only client_id + client_secret (i.e. the client_credentials grant type) is only possible when the client has got the "Service Accounts Enabled" option enabled.

Service Accounts Enabled

The fact that a client is confidential doesn't mean that service accounts are enabled, though only a confidential client can have service accounts enabled. If you try using the client_credentials grant type with a client that doesn't have service accounts enabled, you'll get a response like this:

$ curl https://my-keycloak/auth/realms/some-realm/protocol/openid-connect/token -d grant_type=client_credentials -d client_id=some-confidential-client -d client_secret=some-client-secret
{"error":"unauthorized_client","error_description":"Client not enabled to retrieve service account"}

On the other hand, using a username + password combination (i.e. the password grant type) is only possible when the client has got the "Direct Access Grants Enabled" option enabled:

Direct Access Grants Enabled

If you try using the password grant type with a client that doesn't have direct access grants enabled, you'll get a response like this, regardless of it being public or confidential:

$ curl https://my-keycloak/auth/realms/some-realm/protocol/openid-connect/token -d grant_type=password -d client_id=some-confidential-client -d client_secret=some-client-secret -d username=example-user -d password=example-password
{"error":"unauthorized_client","error_description":"Client not allowed for direct access grants"}

However, there's nothing stopping you from enabling direct access grants on a confidential client. In fact, it's a perfectly fine use case - the client secret authenticates the client and the username/password authenticates the user.

In the end I think that there are three relevant ways of authenticating when it comes to KeycloakAdmin:

  • Direct access grant with a public client - this requires a client_id and user credentials (username and password)
  • Direct access grant with a confidential client - this requires a client_id, client_secret and user credentials
  • Service account with a confidential client - this requires a client_id and a client_secret

It seems that only the first and last cases are supported at the moment.

As you can see, the presence of the client secret does not dictate which grant type should be used. To me it seems to be much more appropriate to use the password grant type only when the user credentials are specified and use the client_credentials grant type when a client secret is provided and user credentials are not.

Another alternative might be to provide an explicit parameter for selecting the grant type (and keep the old behavior when it's not specified in order to not break existing code). Or to provide multiple factory methods, each for a different grant type.

zdenek-biberle avatar Oct 17 '21 16:10 zdenek-biberle