2
votes

I have a query about how keycloak is supposed to be working with client without GUI access.

Basically I have:

  • A keycloak server configured with a realm, clients(Access type confidential) and Users
  • A server application with a GUI that also provide API, secure with keycloak (client, user, blablabla)

This is kind of working already as I am able to log on the GUI, have the redirect, etc..

Even accessing the APIs works well, when I have access to a GUI: I log on my UI, follow the redirect and get my UI to display the token. The the human (to differentiate the user from an application), can use the token in any API client.

In this context the user never sees the client secret, which is instinctively the right way. (note that I am very opened to people telling me my instinct is wrong!)

What I am NOT able to do so far is to find the way a server application (without GUI) can get a valid token?

The authorization_endpoint, as far as I understand it, requires both the client id and the client secret) to get a token, which I would rather avoid: I don't think giving my client secret to all my "customers" is the proper way to do it.

Alternatively I could create an API on my client that woudl ask for user credential and ask for the token in its behalf, but that would expose the clients credentials to my application, which is against the whole concept!

I tried setting my client Access type as public, but when I use the API call below I also get a error:

POST /auth/realms/realmname/protocol/openid-connect/tokenAPI 
'grant_type=client_credentials'
'client_id=client_id'
'username=username'
'password=password'
{
    "error": "unauthorized_client",
    "error_description": "Public client not allowed to retrieve service account"
}

Would anyone know how this is supposed to be done ?

Thanks in advance.

Max

1
Hi again, if my answer fits your needs, do you mind "accepting" it? (cf. meta.stackexchange.com/questions/5234/…). If not, please let me know what's missing. This will help us and future visitors.bsaverino

1 Answers

0
votes

(...) A server application (without GUI) can get a valid token... typically using the Client Credentials flow.

But we would define in this case a dedicated Client for your server (client?) application to authenticate against. The returned token (not bound to a specific user) will serve for authorizations on allowed applications (i.e. your classic GUI or API clients).

So, basically you should (in very short):

  • define a specific confidential Client in your Keycloak
  • add the desired applications (or other Clients) to the Client Scope(s). Those you want to authorize transitively from this Client.
  • authenticate against this Client with Client Credentials flow (given the token endpoint, client id, credentials, scope)
  • ensure that you are authenticating through TLS and that parameters are included in request body (and not in headers - for enhanced privacy)
  • further harden security of your Client(s)

When you do not want anymore this particular server (client?) application to access your applications, you can change the corresponding "authentication" Client's secret/credentials or simply delete it.


"I don't think giving my client secret to all my "customers" is the proper way to do it."

You are right and the proposed method above strictly avoids that. Each customer would have its own credentials.

EDIT

(adding more details)

By performing as above, you would end up with the following scheme:

                                   Flow         Keycloak Server
C/S app. or Customer X  <--- Client Creds --->  Auth. Client X
                         --- Access Token --->  Appl. Client    <-->  Appl. Server

C/S app. or Customer Y  <--- Client Creds --->  Auth. Client Y
                         --- Access Token --->  Appl. Client    <-->  Appl. Server

         Browser users  <--- Standard  ------>  Appl. Client    <-->  Appl. Server

Note: this is not a detailed flow chart. Arrows mostly show relationships here.

Finally, please note that the terminology may differ a little here, but the proposed method is basically the same that Google uses. So you may aswell take some inpiration from there: