Introduction
This document presents you a set of labs (and/ with references) related to Keycloak and UAA that you can do to increase your knowledge in these security tools.
The Keycloak labs here are configured to use the following versions: 3.4.3.Final (docs) and 4.8.3.Final (docs).
The UAA labs are configured to use the following versions: 4.28.0.
These labs were created and tested in a macOS environment. But, it can be easily adapted to run in other environments like Linux or Windows (with some Bash support).
Lab A: Setup the JDK local development environment
sdk (SDKMAN) is a command line too used in this tutorial to set up the JDK version used. To proceed the next steps you must install it.
|
List available JDK versions:
$ sdk list java
See outputs/a1.txt.
Install JDK 8:
$ sdk install java 8u161-oracle
Install JDK 11:
$ sdk install java 11.0.2-open
Check the version:
$ java -version
See outputs/a2.txt.
Set default JDK to version 8:
$ sdk default java 8u161-oracle $ java -version
See outputs/a3.txt.
Lab B: Keycloak versions download and install
Lab B1: Keycloak download
You can skip the following steps if you want to build keycloak. After that, return to Lab B2. |
Download the versions of Keycloak used in this tutorial:
$ wget -c https://downloads.jboss.org/keycloak/3.4.3.Final/keycloak-3.4.3.Final.tar.gz $ wget -c https://downloads.jboss.org/keycloak/4.8.3.Final/keycloak-4.8.3.Final.tar.gz
Lab B2: Keycloak install
Extract both Keycloak versions:
$ tar xvfz keycloak-3.4.3.Final.tar.gz $ tar xvfz keycloak-4.8.3.Final.tar.gz
Let’s Source the file scripts/bashrc (and include it in ~/.bash_profile
).
We’ll use this file to define some variables and functions simplifying our life!
$ source $PWD/scripts/bashrc $ echo "!!" >> ~/.bash_profile
Lab C: Keycloak versions build
This is an optional lab if you followed the steps in Lab B2. Skip directly to Lab D if you dont’t want details about how to build Keycloak. |
Download Keycloak source code:
$ git clone https://github.com/keycloak/keycloak $ cd keycloak
Check the last 2 versions available for Keycloak version 3:
$ git tag | grep '^3.*Final' | tail -2
See outputs/c1.txt.
Compile version 3.4.3.Final:
$ git checkout 3.4.3.Final $ mvn install -Pdistribution -DskipTests
You’ll get errors if you try to compile 3.4.3.Final using JDK 11.0.2-open version. So, pay attention if you configured your current JDK version to 8u161-oracle version. |
Check the built files:
$ ls -l distribution/server-dist/target/*.{tar.gz,zip}
Save the built distribution to KEYCLOAK_LAB
:
$ cp distribution/server-dist/target/keycloak-3.4.3.Final.tar.gz ..
Keycloak version 3.4.3.Final is used in RHSSO 7.2 (this means more stability). Keycloak version 4.8.3.Final is currently used in RHSSO 7.3. |
Check the last 2 versions available for Keycloak version 4:
$ git tag | grep '^4.*Final' | tail -2
See outputs/c2.txt.
Compile version 4.8.3.Final:
$ git checkout 4.8.3.Final $ mvn -Pdistribution -pl distribution/server-dist -am -Dmaven.test.skip clean install
See outputs/c3.txt.
As the same case as in Keycloak 3, if you try to compile Keycloak 4 using JDK version 11.0.2-open you will get errors. |
$ ls -l distribution/server-dist/target/*.{tar.gz,zip}
Save the built distribution to KEYCLOAK_LAB
:
$ cp distribution/server-dist/target/keycloak-4.8.3.Final.tar.gz ..
Go back to the KEYCLOAK_LAB
directory:
$ cd ..
Follow the steps in Lab B2.
Lab D: Running keycloak
Run Keycloak:
$ keycloak-start
Open http://localhost:8180 and configure the user and password to access de Administration Console
.
Lab E: Running quick start applications
Download keycloak-quickstarts
Leave Keycloak running and open another shell.
Clone keycloak-quickstarts:
$ git clone https://github.com/keycloak/keycloak-quickstarts.git $ cd keycloak-quickstarts $ git checkout 4.8.3.Final $ git apply ../patches/keycloack-quickstarts/4.8.3.Final/pom.xml
Running app-authz-rest-springboot
The app-authz-rest-springboot quickstart demonstrates how to protect a Spring Boot REST service using Keycloak Authorization Services.
This quickstart tries to focus on the authorization features provided by Keycloak Authorization Services, where resources are protected by a set of permissions and policies defined in Keycloak and access to these resources are enforced by a policy enforcer (PEP) that intercepts every single request sent to the application to check whether or not access should be granted.
In this application, there are three paths protected by specific permissions in Keycloak:
-
/api/{resource}
, where access to this resource is based on the evaluation of permissions associated with a resource Default Resource in Keycloak. Basically, any user with a role user is allowed to access this resource. Examples of resource that match this path pattern are:/api/resourcea
and/api/resourceb
. -
/api/premium
, where access to this resource is based on the evaluation of permissions associated with a resource Premium Resource in Keycloak. Basically, only users with a role user-premium is allowed to access this resource. -
/api/admin
, where access to this path is based on the evaluation of permissions associated with a resource Admin Resource in Keycloak. Basically, any user can access this resource as long as a specific request parameter is set.
We can use two distinct users to access this application:
Username | Password | Roles |
---|---|---|
alice |
alice |
user |
jdoe |
jdoe |
user, user-premium |
Let’s change to the application directory:
$ cd app-authz-rest-springboot
We need to import the file config/quickstart-realm.json
.
To do this, click in Add realm
button:
Then import the file by clicking on Select file
button:
We can click on View details to see more information about the realm that we are adding.
|
We need to click Save
.
Now, let’s run the Spring Boot app:
$ mvn spring-boot:run
Backing to the dir $KEYCLOAK_LAB
, let’s obtain the OAuth2 access token for user alice
:
$ keycloak-lab $ curl -X POST \ http://localhost:8180/auth/realms/spring-boot-quickstart/protocol/openid-connect/token \ -H 'Authorization: Basic YXBwLWF1dGh6LXJlc3Qtc3ByaW5nYm9vdDpzZWNyZXQ=' \ -H 'content-type: application/x-www-form-urlencoded' \ -d 'username=alice&password=alice&grant_type=password' \ | jq -r .access_token > alice.access_token
|
Let’s install a Node.js JSON Web Token (JWT) decoder (jwt-cli) in order to inspect the contents of the access_token
:
$ npm install -g jwt-cli
Now let’s use it:
$ jwt $(cat alice.access_token)
There are many other alternatives tools to decode a JWT. Here are some links: |
By inspecting the contents of the alice.access_token
we can see that it will be valid only for 5 min (fields iat
and exp
).
If we try to use it again after this period, we will see an error appearing in the console of the Spring Boot Application:
ERROR 5729 --- [nio-8080-exec-6] o.k.a.BearerTokenRequestAuthenticator : Failed to verify token org.keycloak.exceptions.TokenNotActiveException: Token is not active
We can configure the value of Access Token Lifespan
field if we want to increase this period.
So, let’s update the this max time to 10 minutes.
After that, we run the following command to get he OAuth2 access token for the user jdoe
:
$ curl -X POST \ http://localhost:8180/auth/realms/spring-boot-quickstart/protocol/openid-connect/token \ -H 'Authorization: Basic YXBwLWF1dGh6LXJlc3Qtc3ByaW5nYm9vdDpzZWNyZXQ=' \ -H 'content-type: application/x-www-form-urlencoded' \ -d 'username=jdoe&password=jdoe&grant_type=password' \ | jq -r .access_token > jdoe.access_token
Now, we can check the value for the fields iat
and exp
for the received token using the following command:
$ jwt $(cat jdoe.access_token) | grep -e iat: -e exp:
Accessing Protected Resources using an OAuth2 Access Token
Let’s try access the api/resourcea
using the token received for alice
:
$ curl -v -X GET http://localhost:8080/api/resourcea -H "Authorization: Bearer $(cat alice.access_token)"
We expect the following response: Access Granted
.
|
Using the token received for jdoe
we can also access the /api/premium
resource:
$ curl -v -X GET http://localhost:8080/api/premium -H "Authorization: Bearer $(cat jdoe.access_token)"
Running app-authz-springboot
The app-authz-springboot quickstart demonstrates how to write a SpringBoot Web application where both authentication and authorization aspects are managed by Keycloak.
This application tries to focus on the authorization features provided by Keycloak Authorization Services, where resources are protected by a set of permissions and policies defined in Keycloak itself and access to these resources are enforced by a policy enforcer that intercepts every single request to the application.
In this application, there are three paths protected by specific permissions in Keycloak:
-
/protected
, where access to this page is based on the evaluation of permissions associated with a resource Protected Resource in Keycloak. Basically, any user with a role user is allowed to access this page. -
/protected/premium
, where access to this page is based on the evaluation of permissions associated with a resource Premium Resource in Keycloak. Basically, only users with a role user-premium is allowed to access this page. -
/protected/alice
, where access to this page is based on the evaluation of permissions associated with a resource Alice Resource in Keycloak. Basically, only user alice is allowed to access this page.
The home page (home.ftl
) also demonstrates how to use a AuthorizationContext
instance to check for user`s permissions and hide/show things in a page. Where the AuthorizationContext
encapsulates all permissions granted by a Keycloak server and provides methods to check these permissions.
We can use the same users registered in the previous lab with the same password and roles.
Configuration in Keycloak
We need to delete the previously configured realm: spring-boot-quickstart
.
Then we need to recreate the realm:
-
In the top left corner dropdown menu that is titled Master, click Add Realm. If you are logged in to the master realm this dropdown menu lists all the realms created.
-
Click on Select File and import the file
keycloak-quickstarts/app-authz-springboot/config/quickstart-realm.json
. -
Click Create.
Build and Run
First, stop the execution for the last lab (app-authz-rest-springboot) if it is already running!
Then, start the microservice for this lab:
$ cd $KEYCLOAK_LAB/keycloak-quickstarts/app-authz-springboot/ $ mvn spring-boot:run
Open http://localhost:8080.
Test the app using the usernames provided (alice
and jdoe
).
Running service-springboot-rest
The service-springboot-rest quickstart demonstrates how to write a RESTful service with SpringBoot that is secured with Keycloak.
Start it by running the tests:
$ mvn test -Pspring-boot
Read ProductServiceTest.java in order to understanding how to test a Keycloak app.
Lab F: Developing Spring Boot applications integrated with Keycloak (from scratch)
Lab F1: Executing Sebastien Blanc tutorial
Read the article Easily secure your Spring Boot applications with Keycloak (and see the referenced videos: 1 and 2).
In order to simply run the code showed in this article you can do the following steps:
Stop previous running instances of Keycloak.
Configure keycloak instance to use 3.4.3.Final and start it:
$ keycloak-use 3 $ keycloak-start
Clone the project:
$ keycloak-lab $ git clone https://github.com/paulojeronimo/spring-boot-keycloak-tutorial $ cd spring-boot-keycloak-tutorial
Create a new realm on Keycloak by importing the file springdemo.json
.
Run:
$ mvn spring-boot:run
Access http://localhost:8080/products (User: sebi
, Password: sebi
).
To switch to Spring Security version, stop (Ctrl+C) the running app and do the following commands:
$ git branch -a $ git checkout remotes/origin/spring-security
Compare this branch with the master branch:
$ git difftool master...origin/spring-security
Run:
$ mvn clean spring-boot:run
Access http://localhost:8080/products.
The GitHub repository sebastienblanc/spring-boot-keycloak-tutorial has some useful forks (some with more features added):
These most active forks were discovered by using this tool. |
Lab F2: Creating a resource server (backend) using kcadm, gradle and spring-security
Stop previous Keycloak instances.
Configure your environment to use Keycloak 4.8.3.Final, reinstall and start it:
$ keycloak-use 4 $ keycloak-install $ keycloak-start
Access http://localhost:8180 and create the admin
with password admin
.
Do the following steps:
Change to the labs dir:
$ keycloak-lab
Create the sample by using Spring Initializr:
$ rm -rf samples/keycloak-resource-server-demo/ $ mkdir -p samples/ && cd $_ $ curl https://start.spring.io/starter.tgz \ -d bootVersion=2.1.3.RELEASE \ -d dependencies=web,security \ -d type=gradle-project \ -d baseDir=keycloak-resource-server-demo \ | tar -xzvf -
Do your first commit:
$ cd keycloak-resource-server-demo $ git init $ git add -A $ git commit -m 'Initial commit'
Apply the following patch to configure keycloack support on build.gradle
:
$ git apply ../../patches/keycloak-resource-server-demo/build.gradle.diff
Create REST endpoints that will be secured:
$ d=src/main/java/com/example/demo $ cp ../../starts/keycloak-resource-server-demo/$d/HelloEndpoint.java $d/
Configure the Keycloak security:
$ cp ../../starts/keycloak-resource-server-demo/$d/KeycloakSecurityConfigurer.java $d/
See starts/keycloak-resource-server-demo/src/main/java/com/example/demo/KeycloakSecurityConfigurer.java
Login into Keycloak as an administrator through the command line:
$ kcadm.sh config credentials --server http://localhost:8180/auth --realm master --user admin --password admin
Create a REALM:
$ kcadm.sh create realms -s realm=spring-security-example -s enabled=true
Create the clients:
$ CID1=$(kcadm.sh create clients -r spring-security-example -s clientId=curl -s enabled=true -s publicClient=true -s baseUrl=http://localhost:8080 -s adminUrl=http://localhost:8080 -s directAccessGrantsEnabled=true -i) $ CID2=$(kcadm.sh create clients -r spring-security-example -s clientId=spring-security-demo-app -s enabled=true -s baseUrl=http://localhost:8080 -s bearerOnly=true -i)
Add some roles:
$ kcadm.sh create clients/$CID2/roles -r spring-security-example -s name=admin -s 'description=Admin role' $ kcadm.sh create clients/$CID2/roles -r spring-security-example -s name=user -s 'description=User role'
Get the client configuration to know how to configure your application.properties:
$ kcadm.sh get clients/$CID2/installation/providers/keycloak-oidc-keycloak-json -r spring-security-example { "realm" : "spring-security-example", "bearer-only" : true, "auth-server-url" : "http://localhost:8180/auth", "ssl-required" : "external", "resource" : "spring-security-demo-app", "verify-token-audience" : true, "use-resource-role-mappings" : true, "confidential-port" : 0 }
Configure the application.properties
:
$ cat > src/main/resources/application.properties <<'EOF' keycloak.realm=spring-security-example keycloak.bearer-only=true keycloak.auth-server-url=http://localhost:8180/auth keycloak.ssl-required=external keycloak.resource=spring-security-demo-app keycloak.use-resource-role-mappings=true keycloak.confidential-port=0 EOF
Create the user joe_admin
and some add roles:
$ joe=$(kcadm.sh create users -r spring-security-example -s username=joe_admin -s enabled=true -i) $ kcadm.sh update users/$joe/reset-password -r spring-security-example -s type=password -s value=admin -s temporary=false -n $ kcadm.sh add-roles -r spring-security-example --uusername=joe_admin --cclientid spring-security-demo-app --rolename admin
Create the user jim_user
and some add roles:
$ jim=$(kcadm.sh create users -r spring-security-example -s username=jim_user -s enabled=true -i) $ kcadm.sh update users/$jim/reset-password -r spring-security-example -s type=password -s value=admin -s temporary=false -n $ kcadm.sh add-roles -r spring-security-example --uusername=jim_user --cclientid spring-security-demo-app --rolename user
Start the application:
$ ./gradlew bootRun
Test the application with user joe_admin
:
$ export JOE_TOKEN=`curl -ss --data "grant_type=password&client_id=curl&username=joe_admin&password=admin" http://localhost:8180/auth/realms/spring-security-example/protocol/openid-connect/token | jq -r .access_token` $ jwt $JOE_TOKEN $ curl -H "Authorization: bearer $JOE_TOKEN" http://localhost:8080/admin/hello Hello Admin $ curl -H "Authorization: bearer $JOE_TOKEN" http://localhost:8080/user/hello {"timestamp":1544591383960,"status":403,"error":"Forbidden","message":"Access is denied","path":"/user/hello"}
Test the application with user jim_user
:
$ export JIM_TOKEN=`curl -ss --data "grant_type=password&client_id=curl&username=jim_user&password=admin" http://localhost:8180/auth/realms/spring-security-example/protocol/openid-connect/token | jq -r .access_token` $ curl -H "Authorization: bearer $JIM_TOKEN" http://localhost:8080/admin/hello {"timestamp":1544607993019,"status":403,"error":"Forbidden","message":"Access is denied","path":"/admin/hello"} $ curl -H "Authorization: bearer $JIM_TOKEN" http://localhost:8080/user/hello Hello User
Test the application without any user:
$ curl http://localhost:8080/guest/hello Hello Guest
Do a commit:
$ git add -A $ git commit -m 'Added Keycloak support'
Stop the Spring Boot application.
Modifiy your application with the following commands:
$ git apply ../../patches/keycloak-resource-server-demo/src/main/java/com/example/demo/HelloEndpoint.java.diff $ git apply ../../patches/keycloak-resource-server-demo/src/main/resources/application.properties.diff
See what was changed:
$ git difftool
Restart the application:
$ ./gradlew bootRun
Test application againg:
$ export JOE_TOKEN=`curl -ss --data "grant_type=password&client_id=curl&username=joe_admin&password=admin" http://localhost:8180/auth/realms/spring-security-example/protocol/openid-connect/token | jq -r .access_token` $ curl -H "Authorization: bearer $JOE_TOKEN" http://localhost:8080/admin/hello
Do another commit:
$ cat > README.adoc <<'EOF' = keycloak-resource-server-demo This application was created using the steps described in https://github.com/paulojeronimo/keycloak-spring-boot-tutorial#lab-f2[keycloak-spring-boot-tutorial]. EOF $ git add -A $ git commit -m 'Added support to show principal name'
Lab F3: Connecting to Keycloak using Spring Oauth2 OpenID Connector
Under construction …
Lab H: Migrating Spring Boot microservices security from UAA to Keycloak
Lab H1: Spring Boot microservices app secured by UAA
Setup the JDK to use version 8 (otherwise UAA will not compile on version 4.28.0):
$ sdk default java 8u161-oracle
Install and start the UAA server through the functions currently loaded (by scripts/bashrc) in your shell:
$ uaa-install $ uaa-start
Open another shell and clone the oauth-uaa-sample into the samples
dir:
$ keycloak-lab $ mkdir -p samples && cd $_ $ git clone https://github.com/paulojeronimo/oauth-uaa-sample $ cd oauth-uaa-sample
Follow the steps in oauth-uaa-sample/README.adoc to run the application.
Postman configuration samples
-
Keycloak sample:
-
Token Name: keycloak-bearer-token
-
Grant Type: Authorization Code
-
Callback URL: http://localhost:8085
-
Auth URL: http://localhost:8180/auth/realms/dev/protocol/openid-connect/auth
-
Access Token URL: http://localhost:8180/auth/realms/dev/protocol/openid-connect/token
-
Client ID: employee-service
-
Client Secret: 9252a605-1568-4d00-867b-b70ae3d3940c
-
Scope: openid
-
State: 12345
-
Client Authentication: Send as Basic Auth header
-
-
Keycloak other URLs:
-
token_introspection_endpoint: http://localhost:8080/auth/realms/dev/protocol/openid-connect/token/introspect
-
userinfo_endpoint: http://localhost:8080/auth/realms/dev/protocol/openid-connect/userinfo
-
-
UAA sample:
-
Token Name: uaa-bearer-token
-
Callback URL: http://localhost:8085
-
Auth URL: http://localhost:8080/uaa/
-
Access Token URL: http://localhost:8080/auth/outh/token
-
Client ID: client1
-
Client Secret: client1
-
Scope: openid
-
State: 12345
-
Client Authentication: Send as Basic Auth header
-