Keycloak is a sophisticated Open Source identity and access management system, which I have been working a lot with recently as part of the Day Job tm.
We have been building out some sophisticated changes to our infrastructure, and the rest of the team and I have been getting involved in the guts of Keycloak, perhaps more than most.
We’ve got our hands dirty with custom federation, SAML authentication with third party organisational logins, and now, writing our own profile management tools. This latter involved me using the new, and pretty much entirely undocumented, account_api
Accounts Management api.
I figured I’d write up my experiences here, so that you dear reader, will actually have a bit of documentation, rather than have to pick apart Java code!
Why use the accounts management API?
This is the first question, especially when there’s already a quite mature and well documented Admin API.
Well, that’s easy.
Using the account api allows a user to manage their own account, using their own bearer token credentials.
This means that it is well suited to a distributed cloud based microservice environment (like the one we’ve got at the Day Job tm), since it means we don’t have to bake in our admin credentials into everything.
This is obviously more secure, and easier to maintain.
Enabling the Accounts API
The accounts management api is still currently under development, and so is not on by default. You also need to be sure you’re using the latest version of Keycloak.
Once you’ve done that you need to switch it on. So either pass:
bin/standalone.sh -Dkeycloak.profile.feature.account_api=enabled
To standalone.sh
, or permanently switch it on with a profile.properties
file in the standalone configuration directory.
Personally, I just scatter gunned it and turned on all preview features, by creating the file /opt/jboss/keycloak/standalone/configuration/profile.properties
with the following contents:
profile=preview
If you want to quickly get up and running, drop that file in the same directory as this docker-compose.yml
(note, all changes are lost when you restart the container, but it’s good enough to poke around).
version: "3.7"
services:
keycloak:
container_name: keycloak
image: jboss/keycloak
volumes:
- ./profile.properties:/opt/jboss/keycloak/standalone/configuration/profile.properties
restart: always
ports:
- "8080:8080"
environment:
- KEYCLOAK_USER=admin
- KEYCLOAK_PASSWORD=admin
This compose file will also create your admin account for you (password: admin
).
Account API Basics
The basic information that you need to know in order to talk to the Account Management API is as follows:
- The API endpoint base url is:
{{keycloak_url}}/auth/realms/{{realm}}/account/
Where, {{Keycloak_url}}
is your server (e.g. http://localhost:8080
) and {{realm}}
is your realm, (“master
“) in our case.
- Make
GET
orPOST
requests to various endpoints, making sure you tell it that you are expecting JSON in return, by setting the following headers:Accept: application/json
, as well asContent-Type: application/json
when you’re making POST requests. - Pass your access token (see next step) as a
Authorization: Bearer
token. I encourage you to use Postman, as that handles this for you. - You’ll get a
200
response on success, and a4xx
response on failures (e.g.405
for a method not permitted)
Getting your token
Once you’ve set up your Keycloak site (I suggest also creating a regular test user to poke around with), you’ll want to get an access bearer token.
Again, I suggest you use postman for this, and go through the standard OAuth2 handshake.
You’ll need to get the client_secret
from your container’s preconfigured client, and you’ll also want to add https://oauth.pstmn.io/*
to the list of authorised redirect Uris.
By default, Keycloak’s auth tokens expire after 1 minute, which results in a lot of frustration if you’re just poking the API, so if you’re just exploring, you might want to whack this up a bit!
Getting your profile
You can retrieve your logged in user account details in a straightforward way by making a GET
request to the base url. E.g.
curl --location --request GET 'http://localhost:8080/auth/realms/master/account/' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer eyJhbG .... '
Will result in something along the lines of:
{
"username": "marcus",
"firstName": "Marcus",
"lastName": "Povey",
"email": "marcus@example.com",
"emailVerified": true,
"attributes": {
"ExampleAttribute": [
"Example"
],
"AnotherAttribute": [
"blah blah blah"
]
}
}
Updating your profile
Updating your profile is a simple matter of firing the modified profile JSON back as a POST request to the same endpoint.
You can set everything in the profile except the username
, and emailVerified
, and one gotcha is that the new profile will OVERWRITE the existing one… so be sure to not miss out any information you want to keep!
The exception to that, for some reason, is that even if you don’t include it, email doesn’t appear to be overwritten. Names and attributes are however, so probably best just to send the whole thing back sans the username.
E.g.
curl --location --request POST 'http://localhost:8080/auth/realms/master/account/' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer eyJhbG ...' \
--header 'Content-Type: application/json' \
--data-raw '{
"email" : "marcus@example.com",
"firstName": "Marcus",
"lastName": "Povey Updated",
"attributes": {
"ExampleAttribute": [
"blah Updated"
],
"New": [
"Example"
]
}
}'
Updating your password
Probably the next most common thing you’re going to want to do is update your password.
Here, you need to hit the /credentials/password/
endpoint with a post request containing the existing password, and your new password twice:
{
"currentPassword": "oldpassword",
"newPassword": "newpassword",
"confirmation": "newpassword"
}
E.g.
curl --location --request POST 'http://localhost:8080/auth/realms/master/account/credentials/password/' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer eyJhbGc ...' \
--header 'Content-Type: application/json' \
--data-raw '{
"currentPassword": "oldpassword",
"newPassword": "newpassword",
"confirmation": "newpassword"
}'
This has the side effect of cancelling any logged out sessions and voiding your access token, so after you do this you’ll need to go through the authentication process again.
Epilogue
There are a bunch of other API endpoints in the accounts management API (linked accounts, resources, applications etc), but I’ve not quite figured out what they’re for just yet. Besides, this post is already long enough.
Hopefully this will be of use to someone, and I dare say I’ll be posting about those other API endpoints before too long…
UPDATE
For my own memory, and because this might be useful to you. If you’re getting a rather unhelpful unknown error
back from Keycloak when trying to use your access token against the account api, it might be worth dropping it into jwt.io and check that your role has access to the account api.
One of my developers was having a heck of a time trying to get stuff to work, and it turns out that the client_id
he was using wasn’t given a role that had scope to access the account api.
Took us a little while to get to the bottom of, but it looks like the scopes you need are account
profile
and roles
.