I’m trying to implement a system that allows users to programmatically access resources using API keys, but under the role/scope/rights that are allocated to the user, as if the user were accessing the resource server via regular OAuth, with an access token.
What would the OAuth-native/Keycloak way of doing this look like?
Would the best implementation be Token Exchange, as in https://www.keycloak.org/docs/latest/securing_apps/index.html#_token-exchange? I imagine having a client that has long-lived access tokens, and then I’d have a gateway that exchanges these long-lived tokens for shorter-lived tokens for the other resource servers.
Wonder what your thoughts are of implementing something like a server-side User-Agent that uses an API key like a user+password pair to authenticate with Keycloak. I think then Keycloak would have to be extended using the Authentication SPI, maybe?
Can you provide more context about your setup/intentions?
Are these trusted users?
Yes, these are trusted users, they would already be able to authenticate with keycloak using their username and password.
Thanks for asking! More about the setup: these users are already users on keycloak, and have rights to some resources on a resource server. They normally access the resources through a web frontend, but we want to grant these same users programmatic access to the same resources. Regardless of whether they access the resources programmatically (over API) or through the web GUI, they should have the same permissions to all the resources.
Because their permissions don’t change with the method of access, I thought the best way would be for the resource server to accept access tokens and access tokens only, as opposed to access tokens and API keys, because the resource server would then have to figure out the rights of this user somehow.
If the resource server were to stick with accepting only access tokens, then I thought there would have to be some way to exchange the API key for an access token
By trusted users I mean part of the organization and not external users, so I don’t know this is what you meant.
A public client in Keycloak with Direct Access Grants enabled allows your user to exchange a client_id, username, password and grant_type=password for a token with curl.
With that token you can access your API with curl the same way you would do with you web GUI.
This way there is no need to give out client secrets.
Have you tried this out already?
There are other routes you could go:
- adding an API key as an attribute to the user and check that somehow
- add users that need access to a group and only allowed that group through a client
There are other things to consider also:
- how to quickly invalidate API keys in case of …
- how to renew API keys …
For programmatic access, I’m hoping to achieve a developer experience kind of like https://cloud.google.com/docs/authentication/api-keys, so ideally I prefer not to have to first query for a token, then use that token to access the service, although that’s a good idea for the short term, perhaps until we find a better way.
I thought of user attributes too! But I think that would require a custom authentication extension to keycloak.
As for invalidation and renewing, I’m hoping keycloak’s admin API has someway of removing keys/attributes, and if so, I could probably use that to automate invalidation.
This is an example of api key authentication in keycloak: https://github.com/zak905/keycloak-api-key-demo. When you register for the first time, a user attribute called api-key is generated with a key that is about ~50 chars long. You can check if the key is valid by hitting a custom created endpoint. I wrote a post a about it a while ago: http://www.zakariaamine.com/2019-06-14/extending-keycloak
I hope that helps.
Hey @zak, thanks for doing up the demo! I saw your demo when I was googling for ideas! I’ll definitely look at it again if I have to implement extensions for keycloak.
I’m looking for a way to get the access token so that the resource server (your rest-api-service) would grant access to resources based on the information in the token. In your demo, the API key only provides authentication, and the service itself has to determine authorization some other way.
Yes I see what you mean and what you are trying to accomplish.
May I ask why requesting and passing a token is such a hurdle for you?
Haha because it’s not only for me, it’s for my users, who are business intelligence people who are new-ish to pulling data for themselves. They’re more used to having data served to them in Tableau or other direct database connections, where security is pretty much transparent to them.
To encourage adoption of programmatic access to data through other tools like jupyter notebooks, we want to try and lower the hurdle as much as we can.
One other alternative would be to provide a client side library or a boilerplate two liner to let them exchange their username and password for a token, when they then pass into, say, the python requests library, but then it sort of fosters a bad practice of putting credentials in code. Of course they should be using environment variables and stuff but that might be quite a lot for someone unfamiliar to programmatic access.
Putting the API key in code is about the same as putting username/password in code, but then I guess an API key is more revocable?
Thanks for point that out though, it made me think of the client side library more, and I think that might not be too bad a suggestion, we might even make the API like
which takes no parameters at all, forcing the user to use environment variables (and then help them to set up their environment, which could be good because then they’ll almost always share code (perhaps jupyter notebooks) without credentials by default.
Actually there is another issue. I plan to use confidential clients, and the client can’t be both confidential and public.
For the user to be able to exchange his/her username/password for an access token with the Direct Access Grant flow, the user will have to supply the client and client_secret, so the Direct Access Grant flow to get an access token doesn’t work.
It looks like RFC7523 is a possible way to achieve this? (see section 4)
I could generate a jwt-bearer token as an API key, and then exchange that jwt-bearer token for an access token for a session?
Can’t seem to find any information about this flow in keycloak, has anyone used this before?
Can someone share a cURL example of how this works?
Get a token from Keycloak
curl -v -d "client_id=keycloak-client" -d "firstname.lastname@example.org" -d "password=password" -d "grant_type=password" -X POST "http://localhost:8080/auth/realms/realm-name/protocol/openid-connect/token" | jq -r .access_token;
Call your own API with the token
curl -v --request GET "http://localhost:9000/api/v1/endpoint" --header "Accept: application/json" --header "Authorization: Bearer $TOKEN";
@zonaut thank you for this. I did some testing and the first curl works in my environment - I get the access_token as part of the response.
The second - the api call
curl --location --request GET 'https://whoami.mydomain/api/http/routers' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer eyJhbG...L_J8g' \
--header 'Cookie: _forward_auth_csrf=958ae1cb0c5ff311123b81daad0052a5'
The response is unfortunately the login page. I think this may be limitation of the traefik_forward_auth module I am using to add oauth to any nonsecure backend.
I’ll take this example over to the Traefik forum to see if this should be supported. Thanks for the help so far.
Thank you Zak for sharing the blog post
I would highly appreciate some node.js examples/code snippets for extending Keyclock to capture internal events by implementing EventListenerProvider
Hi @Sara, Thanks. I am glad it helps. You mean like securing a node.js app using keycloak ? or extending keycloak to communicate with a node.js app ? If you want you can open an issue with more details on the github repo mentionned above, and I will try to add an example.
Hi @ ackerleytng, i’m looking for a similiar approach.
I want to give the user the possibilitiy to generate a Personal Access Token for “offline” use with a custom token expiration (about 2 weeks). This PAT will then be used by an authorized party to request our API resource that’s “protected” by this token. The main advantage is that we can use the (already) built-in authorization model.
Did you have any success with your approach?
Maybe this is more suitable Server Administration Guide
Offline access tokens are the closest thing to what you are requesting that is built into keycloak.
thanks for the swift reply @xgp
The documentation says (Server Administration Guide):
This action is useful if your application needs to perform offline actions on behalf of the user even when the user is not online. For example, a regular data backup.
The trusted party will use this token from outside our DMZ (external). I’m not really sure how to interpret not online but in my case the user is online and not on premise.
The “not online” part is just describing an action that is done by an application/machine on behalf of the user, when they are not in front of their device.
“offline access tokens” in Keycloak are basically refresh tokens that survive server restarts. They’re good for the use case where you want to give a user a valid, long-lived token that they can use to request access tokens to use with your services.