Table of Contents
If you want to know how to set up and configure Keycloak as an authorization server (for OAuth 2.0 framework) with Docker this article is for you.
This blog post is a second part of my series on OAuth 2.0 framework. In previous article I’ve briefly described what OAuth 2.0 is, so if you’re not familiar with it I would advise to check it first, before diving into Keycloak setup.
My previous article was very theoretical, but this time it will change. This one and two upcoming are more practical because we will build a small project in which we will define all key actors of OAuth 2.0 framework.
A target project will consists of three pieces:
authorization server (Keycloak) - an application which is used to issue access and identity tokens,
protected resource (Java, Spring Boot, backend application) - an application that serves information using REST API, but requires a valid access token for security reasons,
client (Angular, frontend application) - a website, which requires user to be logged in order to be able to get access token and query the backend app.
In this article I’ll focus on the authorization server, how to create a basic
docker-compose.yaml file and then configure a Keycloak instance.
Running Keycloak instance #
Before adding any project-specific configuration I’ve configured my OS’s hosts file, so that I’ll have a convenient URL address for testing. The location of this file depends on the OS:
Once you found it add following line:
After that instead of
http://localhost we will be able to use the
http://keycloak URL. Be mindful, that it’s not necessary step, you can skip it and use the
localhost instead of
keycloak as I will thru this blog post.
Having it we can move the setting up Keycloak. First thing to do is to create a Docker Compose file where will be included entire Keycloak configuration. Looking into the documentation on Docker Hub we can find out that jboss/keycloak Docker image supports multiple databases, like H2, MySQL, PostgreSQL, MariaDB, Oracle or Microsoft SQL Server.
I’ve decided to go with PostgreSQL, therefore here is a simple definition of postgres component in docker-compose.yaml.
version: "3.8" services: postgres: image: postgres:13.0-alpine container_name: postgres ports: - 5432:5432 environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres volumes: - postgres:/var/lib/postgresql/data - ./infra/postgres:/docker-entrypoint-initdb.d volumes: postgres:
Apart from a standard definition, like an
ports mapping I’ve decided to set up a user and password to the database as
postgres using the environment variable, which is documented on Docker Hub for postgres image.
Next thing worth to mention here is that I’ve added a
volume, first one is just to persist container data on my local machine and the latter is for adding initial sql script, so it will be executed during first start up and it’ll create a database and a user which will be used by Keycloak.
CREATE USER keycloak WITH ENCRYPTED PASSWORD 'keycloak'; CREATE DATABASE keycloak; GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak;
This script is located in the
.infra/postgres folder on a host machine and it’s mapped to a directory inside postgres Docker container
After that we can move on to defining Keycloak component.
keycloak: image: jboss/keycloak:11.0.2 container_name: keycloak ports: - 8080:8080 environment: - KEYCLOAK_USER=admin - KEYCLOAK_PASSWORD=admin - DB_VENDOR=postgres - DB_ADDR=postgres - DB_DATABASE=keycloak - DB_USER=keycloak - DB_PASSWORD=keycloak depends_on: - postgres
Again there is no rocket science here. Apart from the standard
ports mapping we can see only
environemnt variables that are used to tune Keycloak to be connected to the
postgres database and to set a default username and password for an admin user.
The resulting docker-compose.yaml file looks as follows.
version: "3.8" services: keycloak: image: jboss/keycloak:11.0.2 container_name: keycloak ports: - 8080:8080 environment: - KEYCLOAK_USER=admin - KEYCLOAK_PASSWORD=admin - DB_VENDOR=postgres - DB_ADDR=postgres - DB_DATABASE=keycloak - DB_USER=keycloak - DB_PASSWORD=keycloak depends_on: - postgres postgres: image: postgres:13.0-alpine container_name: postgres ports: - 5432:5432 environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres volumes: - postgres:/var/lib/postgresql/data - ./infra/postgres:/docker-entrypoint-initdb.d volumes: postgres:
Now go to terminal, navigate to a folder where your docker-compose.yaml file is located and run following command:
> docker-compose up -d keycloak Creating volume "keycloak-security-example_postgres" with default driver Creating volume "keycloak-security-example_prometheus" with default driver Pulling postgres (postgres:13.0-alpine)... 13.0-alpine: Pulling from library/postgres 188c0c94c7c5: Pull complete 56f1d1b70e7f: Pull complete 9b4f01476d2b: Pull complete 16419214bc02: Pull complete 4886fc567835: Pull complete 9026d4fbeafa: Pull complete 001c336294eb: Pull complete 8abc6d154e9f: Pull complete Digest: sha256:d26ddee3648a324a9747b3257236322141920d5f9a82ca703def6bff1cca7067 Status: Downloaded newer image for postgres:13.0-alpine Pulling keycloak (jboss/keycloak:11.0.2)... 11.0.2: Pulling from jboss/keycloak 0fd3b5213a9b: Pull complete aebb8c556853: Pull complete ed3ae09abceb: Pull complete 85e3448ea914: Pull complete 9f0e9c75b3b9: Pull complete Digest: sha256:8cdd41cb4a0b210ed3f07df5d18306762e1755bb8d6c1ffc5e083c080528783d Status: Downloaded newer image for jboss/keycloak:11.0.2 Creating postgres ... done Creating keycloak ... done
With above command we have pulled (downloaded) both Docker images and started new containers.
To check if everything is all right run another Docker command to see running containers:
> docker ps CONTAINER ID IMAGE CREATED STATUS PORTS NAMES f9a67e3b9756 jboss/keycloak:11.0.2 35 seconds ago Up 33 seconds 0.0.0.0:8080->8080/tcp, 8443/tcp keycloak 5d781b2b8d6f postgres:13.0-alpine 41 seconds ago Up 34 seconds 0.0.0.0:5432->5432/tcp postgres
The status is
Up so everything look fine :). If for some reasons it’s not the case for you, check if you do not have any kind of a typo.
Adding realm, client, roles and users #
As we have a confirmation that Keycloak is running, let’s login to it. Therefore, in your browser go to http://keycloak:8080, it will lead you to a home page.
Select Administration Console, it will redirect you to the login page, where you need to provide an admin credentials which are (same as they were definited in docker-compose.yaml file):
- username: admin
- passowrd: admin
After login you should now be inside of Keycloak’s Master realm.
But what is realm? It’s purely a Keycloak thing and it’s used to for grouping resources like users, clients etc. Realms are isolated from each other so users from one realm will not be visible in another one, so these realms could be treated as separte instances of authorization servers.
To create a new realm click on the dropdown list located in the top left corner on the name of a current realm Master. Then click a button Add Realm.
Next page is fairly simple, only thing to do is to provide a realm name here. I’m going with a name test.
After hitting Create button a new realm is successfully created.
Create a client credentials #
Because we’re building an authentication & authorization based on OAuth 2.0 and OpenID we’ll have two different applications - protected resource that is a backend application that servers some data and a client application, a frontend that will want to access data from protected resource. To do that frontend application will need to have an access token. And to obtain that a client needs to be registered (has client id and client secret) on the authorization server side, Keycloak in our case.
Creation of a client for a frontend app I’ll cover in relevant article. Right now I’ll create a client just for testing Keycloak setup.
Therefore go to the Clients page (it’s located in the left menu) and click Create button located on a right.
In a new view we need to provide only a client name, which will be test_client.
After hitting the Save button a detailed page for newly created client will show up. To finish configurating frontend client we need first set an Access Type to confidential and then switch on Service Accounts Enabled and Authorization Enabled. And finally we need to provide a Redirect URI which in our case will be http://localhost:80, http://localhost:4200 which are base URL of frontend application (will be build in upcoming article).
Let’s now test if we’re be able to receive tokens using using
client_credentials OAuth 2.0 grant type. In short, this grant type is used when there is no resource owner (person, user), but a client application needs to get to the protected resource using an access token. In this flow a client application is providing only its credentials and scope to the authorization server and as a result it returns valid tokens.
To test it I’m using Postman, but if you prefer different tool, like
curl, go ahead with your favourite tool.
In Postman create a request tab and provide following information:
- Method: POST
- Url: http://keycloak:8080/auth/realms/test/protocol/openid-connect/token
The token Url is a standard Keycloak one. If you name your realm differently than me, you will need to replace
test in above path to your name.
Then in a Body tab select
x-www-form-urlencoded option and in a table provide following data.
If you’re set up your own Keycloak instance a
client_secret might be different for you. To check it out go back to the
test_client client page in Keycloak and select
Credentials tab. There you should find your secret.
And finally, after clicking
Send button you should get a following response with access, identity and refresh token:
Awesome! 🤟 If you want, you can all these tokens on JWT.io website, to find out what data all these tokens holds.
Create a user and roles #
Once we registered a client in Keycloak, we need to create users (resource owners) and give them different roles to differentiate what actions on protected resource they can perform.
First step would be to define two roles -
ADMIN. Their naming and actual meaning is not that important, at least for this demo project.
Therefore to define a
VISITOR open Keycloak realm page and go to Roles available on the left panel. Then click Add Role button. On a new screen provide the Role Name and some Description and hit Save.
ADMIN role do the same.
Now we move to registering a new user. Therefore click on the Users button available on the left panel and then click the Add User button. On a first page we need to provide some basic information about the user, like username, email etc.
After hitting the Save button
luke’s detailed page will appear. To assign just created role we need to go the Role Mappings tab, select the role (in my case
VISITOR) and then click the Add selected button.
And finally we need to set up a password for this user, so go to the Credentials tab and provide it (in my case its
password). Make sure that Temporary toggle is switched off.
If you want, you can create a second user which will be assigned to a second role.
To test if everything is set up correctly again I’ll use Postman. As it was previously I would like to get tokens, but this time with different OAuth 2.0 grant type. This time I’ll use the
password grant type, in which we are asked to provide both client and resource owner credentials.
The HTTP method and Url does not change and they are:
- Method: POST
- Url: http://keycloak:8080/auth/realms/test/protocol/openid-connect/token
Also the content type remain the same -
x-www-form-urlencoded. What is different is the body of the request (but not entirely):
As a result again, we get set of tokens. If we then copy-paste an access token to the JWT.io to decode it we can see that it holds basic information about the user and assigned roles.
Great! We’ve got a fully operation authorization server!
Single-click set up #
It’s awesome that we’ve got authorization up and running, but here is the problem - how to avoid these manual steps in future? Nothing more simple, we can export realm that we have just created!
One remark, before making hands dirty - please be aware that this approach is very good for development purposes, but I would advice not to use it for production, chiefly because we will export information about users and its credentials, which is not secure.
If you have still running Keycloak instance, stop it. Then go to the docker-compose.yaml file and add a new volume for
keycloak service -
./:/temp (I’m ignoring the env variable to keep it tight).
keycloak: image: jboss/keycloak:11.0.2 container_name: keycloak ports: - 8080:8080 volumes: - ./:/temp depends_on: - postgres
Now run again Keycloak with
docker-compose up -d keycloak command. Having it running we can now run the export commnad inside running Docker container using a console (based on solution described here):
> docker exec -it keycloak /opt/jboss/keycloak/bin/standalone.sh -Djboss.socket.binding.port-offset=100 -Dkeycloak.migration.action=export -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.realmName=test -Dkeycloak.migration.usersExportStrategy=REALM_FILE -Dkeycloak.migration.file=/tmp/realm-test.json
As a result we’ll get the realm-test.json file in root directory of the project. Great, now let’s set up importing this JSON file.
Go back now to the docker-compose.yaml file where we need to change a temporary volume, add additional command and new environment variable:
keycloak: image: jboss/keycloak:11.0.2 container_name: keycloak ports: - 8080:8080 environment: - KEYCLOAK_IMPORT=/tmp/realm-test.json volumes: - ./infra/keycloak/realm-test.json:/tmp/realm-test.json command: ["-Dkeycloak.profile.feature.upload_scripts=enabled"] depends_on: - postgres
Next we need to copy realm-test.json to a new folder /infra/keycloak (or wherever you would like to put it).
Now if you remove all Docker containers and volumes and start Keycloak again you will see the same data that you have provided before!
That was a second part of my series on OAuth 2.0. If you liked it, please let me know in comments. And if you’re interested in more content on this topic go check my other articles:
- Introduction to OAuth 2.0
- Create and configure Keycloak OAuth 2.0 authorization server (this one)
- Implementing OAuth 2.0 access token validation with Spring Security (soon)
- Step-by-step guide how integrate Keycloak with Angular application (soon)
As usual, you can find full code in my GitHub repository: