python-keycloak
python-keycloak copied to clipboard
Using KeycloakAdmin with a confidential client together with username/password
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?
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.
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.
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:
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.