In this tutorial I’m using a Debian 12 server with the following IP “192.168.30.70” for the Keycloak deployment and another Debian 12 server with the IP “192.168.30.72” for the test application.
Bare Metal Version #
Install Java #
# Download and extract the OpenJDK Archive
cd /tmp && wget https://download.java.net/java/GA/jdk22.0.1/c7ec1332f7bb44aeba2eb341ae18aca4/8/GPL/openjdk-22.0.1_linux-x64_bin.tar.gz &&
tar -xzf openjdk-22.0.1_linux-x64_bin.tar.gz
# Move files
sudo mkdir -p /usr/lib/jvm &&
sudo mv /tmp/jdk-22.0.1 /usr/lib/jvm/
# Set environment variables: For the current user
echo 'export JAVA_HOME=/usr/lib/jvm/jdk-22.0.1' >> ~/.bashrc &&
echo 'export PATH=$PATH:$JAVA_HOME/bin' >> ~/.bashrc
# Apply the changes
source ~/.bashrc
# Verify installation
java -version
Install Keycloak #
# Find latest Keycloak version
https://www.keycloak.org/downloads
# Install Keycloak
cd /tmp && wget https://github.com/keycloak/keycloak/releases/download/24.0.3/keycloak-24.0.3.tar.gz &&
tar -xvzf keycloak-24.0.3.tar.gz &&
sudo mv keycloak-24.0.3 /opt/keycloak
keycloak.conf #
# Open the configuration file
sudo vi /opt/keycloak/conf/keycloak.conf
Let’s Encrypt Version:
# Set the hostname to your FQDN
hostname=keycloak.jklug.work
# Define HTTP port
http-port=8080
# Define HTTPS port
https-port=8443
# Paths to Let's Encrypt certificate
https-certificate-file=/etc/letsencrypt/live/keycloak.jklug.work/fullchain.pem
https-certificate-key-file=/etc/letsencrypt/live/keycloak.jklug.work/privkey.pem
# Enable HTTPS and disable HTTP if desired
https-enabled=true
http-enabled=true
Self Signed Certificate Version (For Testing):
# Allow all hostnames
hostname-strict=false
# Define HTTP port
http-port=8080
# Define HTTPS port
https-port=8443
# Define certificate path
https-certificate-file=${kc.home.dir}/conf/kc.crt.pem
https-certificate-key-file=${kc.home.dir}/conf/kc.key.pem
# Enable HTTPS and disable HTTP if desired
https-enabled=true
http-enabled=true
Certificate #
Let’s Encrypt Certificate #
Note: I’m using a Let’s Encrypt Wildcard Certificate in this tutorial.
# Create certificate directory and place the certificates
sudo mkdir -p /etc/letsencrypt/live/keycloak.jklug.work/
Create a Self Signed Certificate #
Note: Without the self signed certificate it’s not possible to start Keycloak, even if you want to use HTTP only.
# Create self signed certificate
cd /opt/keycloak/conf && openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout kc.key.pem -out kc.crt.pem
System User and Group #
# Create a system user and group for Keycloak
sudo addgroup --system keycloak &&
sudo adduser --system --ingroup keycloak --shell /sbin/nologin keycloak
# Change ownership
sudo chown -R keycloak:keycloak /opt/keycloak
Note: “Not creating `/nonexistent’” means no home directory was created for the user.
Build Configuration #
# Optional: List script options
/opt/keycloak/bin/kc.sh --help
# Start Keycloak & build the configuration files
sudo -E -u keycloak /opt/keycloak/bin/kc.sh start
# Stop Keycloak
Strg + c
-E
This option tells sudo to preserve the environment / environment variables
Verify Configuration file:
# Optional: Verify configuration file
sudo -E -u keycloak /opt/keycloak/bin/kc.sh show-config
Systemd Service Unit #
# Configure Keycloak to run in production mode
sudo vi /etc/systemd/system/keycloak.service
[Unit]
Description=The Keycloak Server
After=syslog.target network.target
Before=httpd.service
[Service]
Environment=LAUNCH_JBOSS_IN_BACKGROUND=1
Environment="JAVA_HOME=/usr/lib/jvm/jdk-22.0.1"
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$JAVA_HOME/bin"
Environment="KEYCLOAK_ADMIN=admin"
Environment="KEYCLOAK_ADMIN_PASSWORD=myadminpw"
User=keycloak
Group=keycloak
LimitNOFILE=102642
PIDFile=/run/keycloak/keycloak.pid
ExecStart=/opt/keycloak/bin/kc.sh start --optimized
StandardOutput=journal
[Install]
WantedBy=multi-user.target
Note: Define the admin user and password for the webinterface.
Start and Enable Service #
# Reload systemd daemon / reload configuration files
sudo systemctl daemon-reload
# Start service and enable after boot
sudo systemctl enable keycloak && sudo systemctl start keycloak
# Check status
sudo systemctl status keycloak
Check Logs #
# Check logs
sudo journalctl -u keycloak
# Check logs: Last 20 lines
sudo journalctl -u keycloak -n 20
Keycloak #
Access the Admin Console #
## Access Keycloak Admin Console: HTTP
http://192.168.30.70:8080/
## Access Keycloak Admin Console: HTTPS
https://keycloak.jklug.work:8443/
# Login with user & pw defined in the service unit
Keycloak CLI #
Set Env Variable #
# Set env variable
echo 'export KC_HOME=/opt/keycloak/bin' >> ~/.bashrc
# Apply the changes
source ~/.bashrc
Connect to the API #
# Login to admin CLI: This prompts for the admin pw
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh config credentials --server http://192.168.30.70:8080 --realm master --user admin --config /opt/keycloak/config/kcadm.config
Create a Realm #
-
Each realm is isolated from the other realms and has its own configuration, applications and users.
-
The name of the realm is used in it’s URL, so don’t use any special characters.
# Create a new realm
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh create realms -s realm=jkw-realm-1 -s enabled=true -o --config /opt/keycloak/config/kcadm.config
Group #
Create Group #
# Create a group
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh create groups -r jkw-realm-1 -s name=group-1 --config /opt/keycloak/config/kcadm.config
# Shell output:
Created new group with id '5c220a73-bfb1-4401-a705-79707005fc98
Retrieve Group ID #
# Retrieve group ID
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh get groups -r jkw-realm-1 --fields id,name --config /opt/keycloak/config/kcadm.config
# Shell output:
[ {
"id" : "5c220a73-bfb1-4401-a705-79707005fc98",
"name" : "group-1"
} ]
Realm Role #
Create Realm Role #
# Create Realm Role
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh create roles -r jkw-realm-1 -s name=jkw-role-1 -s description="jkw role 1" --config /opt/keycloak/config/kcadm.config
# Shell output:
Created new role with id 'jkw-role-1'
Retrieve Realm Role ID #
# Retrieve realm role ID
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh get roles -r jkw-realm-1 --fields id,name --config /opt/keycloak/config/kcadm.config
# Shell output:
[ {
"id" : "c3450c71-6fdf-4060-bd80-ff98fafc8eaf",
"name" : "jkw-role-1"
}, {
"id" : "74274001-f67c-4297-b122-ca0906ed0af8",
"name" : "default-roles-jkw-realm-1"
}, {
"id" : "4b781f46-5f56-4e0e-9077-03bdc7d72c9f",
"name" : "offline_access"
}, {
"id" : "38cedfc6-4ecc-497c-93f5-06735f3c8bf0",
"name" : "uma_authorization"
} ]
User #
Create User #
# Create & and enable a user in a the new realm
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh create users -r jkw-realm-1 -s username=jkw-01 -s enabled=true --config /opt/keycloak/config/kcadm.config
# Shell output:
Created new user with id '69f5d537-2eda-411f-8774-d636651a1a66'
Set User PW #
# Define a password for the user
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh set-password -r jkw-realm-1 --username jkw-01 --new-password 'myuserpw' --config /opt/keycloak/config/kcadm.config
Retrieve User ID #
# Retrieve user ID
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh get users -r jkw-realm-1 --query username=jkw-01 --config /opt/keycloak/config/kcadm.config
# Shell output:
[ {
"id" : "69f5d537-2eda-411f-8774-d636651a1a66",
"username" : "jkw-01",
"firstName" : "testuser",
"lastName" : "bla",
"email" : "juergen@jklug.work",
"emailVerified" : false,
"createdTimestamp" : 1715121272348,
"enabled" : true,
"totp" : false,
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"notBefore" : 0,
"access" : {
"manageGroupMembership" : true,
"view" : true,
"mapRoles" : true,
"impersonate" : true,
"manage" : true
}
} ]
Add User to Group #
# Add user to group: Syntax
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh update users/<userID>/groups/<groupID> -r jkw-realm-1 --config /opt/keycloak/config/kcadm.config
# Add user to group: Example
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh update users/69f5d537-2eda-411f-8774-d636651a1a66/groups/5c220a73-bfb1-4401-a705-79707005fc98 -r jkw-realm-1 --config /opt/keycloak/config/kcadm.config
Assign Realm Role to User #
# Assign realm role to user: Syntax
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh add-roles -r jkw-realm-1 --uusername <username> --rolename <role-name>
# Assign realm role to user: Example
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh add-roles -r jkw-realm-1 --uusername jkw-01 --rolename jkw-role-1 --config /opt/keycloak/config/kcadm.config
Test User / Keycloak Account Console #
# Open the Keycloak Account Console: HTTP
http://192.168.30.70:8080/realms/jkw-realm-1/account
# Open the Keycloak Account Console: HTTPS
https://keycloak.jklug.work:8443/realms/jkw-realm-1/account
# User:
jkw-01
# Password:
myuserpw
Node.js Test Application #
Keycloak is providing example applications in several languages to test the Keycloak authentication, in this tutorial I’m using the Node.js version.
Keycloak Realm #
Import the realm configuration file realm-import.json
from the “Create realm” section in the Admin Console to create a new realm called quickstart,
this also creates the following users and roles:
| Username | Password | Roles | | — | — | | alice | alice | user | | admin | admin | admin |
# Download realm-import.json
https://github.com/keycloak/keycloak-quickstarts/blob/latest/nodejs/resource-server/config/realm-import.json
realm-import.json
{
"realm": "quickstart",
"enabled": true,
"clients": [
{
"clientId": "resource-server",
"enabled": true,
"bearerOnly": false
},
{
"clientId": "test-cli",
"enabled": true,
"publicClient": true,
"directAccessGrantsEnabled": true
}
],
"users" : [
{
"username" : "alice",
"enabled": true,
"email" : "alice@keycloak.org",
"firstName": "Alice",
"lastName": "Liddel",
"credentials" : [
{ "type" : "password",
"value" : "alice" }
],
"realmRoles": [ "user", "offline_access" ],
"clientRoles": {
"account": [ "manage-account" ]
}
},
{
"username" : "admin",
"enabled": true,
"email" : "test@admin.org",
"firstName": "Admin",
"lastName": "Test",
"credentials" : [
{ "type" : "password",
"value" : "admin" }
],
"realmRoles": [ "user","admin" ],
"clientRoles": {
"realm-management": [ "realm-admin" ],
"account": [ "manage-account" ]
}
}
],
"roles" : {
"realm" : [
{
"name": "user",
"description": "User privileges"
},
{
"name": "admin",
"description": "Administrator privileges"
}
]
}
}
Or use the CLI to import the quickstart ream:
# Create the quickstart realm
sudo -E -u keycloak /opt/keycloak/bin/kcadm.sh create realms -f /tmp/realm-import.json --config /opt/keycloak/config/kcadm.config
# Shell output:
Created new realm with id 'quickstart'
Install Prerequisites #
# Install git, Node.js, Node Package Manager
sudo apt install git nodejs npm -y
# Clone git project
cd && git clone https://github.com/keycloak/keycloak-quickstarts.git
# Change directory
cd keycloak-quickstarts/nodejs/resource-server
# Install necessary Node.js dependencies listed in package.json
npm install
keycloak.json #
# Edit the keycloak.json file
vi ~/keycloak-quickstarts/nodejs/resource-server/keycloak.json
Change the IP and port of the Keycloak server:
HTTP Version:
{
"realm": "quickstart",
"bearer-only": true,
"auth-server-url": "http://192.168.30.70:8080",
"ssl-required": "external",
"resource": "resource-server"
}
TLS Version:
{
"realm": "quickstart",
"bearer-only": true,
"auth-server-url": "https://keycloak.jklug.work:8443",
"ssl-required": "all",
"resource": "resource-server"
}
Start the Node.js App #
# Start the application
npm start
Access the Quickstart App #
Test the access to the quickstart application from a browser:
# Requires no authentication
http://192.168.30.72:3000/public
# Can be invoked by users with the user role
http://192.168.30.72:3000/secured
# Can be invoked by users with the admin role
http://192.168.30.72:3000/admin
Create Hosts Entry #
Since I’m using a wildcard certificate and my domain is not accessible from the outside, it’s necessary to create an hosts entry.
# Open the hots file
sudo vi /etc/hosts
# Add an entry for the keycloak server
192.168.30.70 keycloak.jklug.work
Obtain the bearer token #
# Install curl, jq JSON Processor
sudo apt install curl jq -y
# Obtain the bearer token for the user "alice": HTTPS Version
export access_token_alice=$(\
curl -X POST https://keycloak.jklug.work:8443/realms/quickstart/protocol/openid-connect/token \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'client_id=test-cli' \
-d 'username=alice&password=alice&grant_type=password' | jq --raw-output '.access_token' \
)
# Obtain the bearer token for the user "alice": HTTP Version
export access_token_alice=$(\
curl -X POST http://192.168.30.70:8080/realms/quickstart/protocol/openid-connect/token \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'client_id=test-cli' \
-d 'username=alice&password=alice&grant_type=password' | jq --raw-output '.access_token' \
)
# Obtain the bearer token for the user "admin": HTTPS Version
export access_token_admin=$(\
curl -X POST https://keycloak.jklug.work:8443/realms/quickstart/protocol/openid-connect/token \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'client_id=test-cli' \
-d 'username=admin&password=admin&grant_type=password' | jq --raw-output '.access_token' \
)
# Obtain the bearer token for the user "admin": HTTP Version
export access_token_admin=$(\
curl -X POST http://192.168.30.70:8080/realms/quickstart/protocol/openid-connect/token \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'client_id=test-cli' \
-d 'username=admin&password=admin&grant_type=password' | jq --raw-output '.access_token' \
)
Note: The access_token variable holds the token in the memory of the current shell session.
Test the Access #
# Test Public
curl http://localhost:3000/public
# Shell output:
{"message":"public"}
# Test Alice
curl http://localhost:3000/secured \
-H "Authorization: Bearer "$access_token_alice
# Shell output:
{"message":"secured"}
# Test Admin
curl http://localhost:3000/admin \
-H "Authorization: Bearer "$access_token_admin
# Shell output:
{"message":"admin"}
Links #
# Find latest OpenJDK version
https://jdk.java.net/
# Keycloak: Admin CLI
https://www.keycloak.org/docs/latest/server_admin/#admin-cli
# Keycloak: keycloak.conf options
https://www.keycloak.org/server/all-config
# GitHub: Keycloak Quickstart Applications
https://github.com/keycloak/keycloak-quickstarts
# GitHub: Keycloak Node.js Quickstart Application
https://github.com/keycloak/keycloak-quickstarts/blob/latest/nodejs/resource-server/README.md
# GitHub: Keycloak Example Realm JSON Import
https://github.com/keycloak/keycloak-quickstarts/blob/latest/nodejs/resource-server/config/realm-import.json