‏ ‏ ‎ ‏ ‏ ‎

1. Keycloak Role-Based Access Control (RBAC)

1.1. Create your Quarkus Project

mvn io.quarkus:quarkus-maven-plugin:2.4.2.Final:create \
  -DprojectGroupId=at.htl \
  -DprojectArtifactId=quarkus-openid-connect \
  -Dextensions="oidc, resteasy-jsonb"

1.2. Add the Endpoint (Resource)

UserResource.java
Unresolved directive in keycloak-rbac.adoc - include::../labs/openid-connect-policies/src/main/java/at/htl/UserResource.java[]

1.3. Application configuration

application.properties
Unresolved directive in keycloak-rbac.adoc - include::../labs/openid-connect-policies/src/main/java/at/htl/../../../resources/application.properties[]

1.3.1. Problems, when using Jackson

  • In this example, we serialize not a self-built entity class. We serialize a given interface SecurityIdentity.

    • An error occurs because of lacking getter and setter.

    • So we have to loosen the policy for serializing this interface.

# these properties are necessary because jackson throws an 'InvalidDefinitionException: No serializer found for class'
# https://quarkus.io/guides/rest-json#json
# würde man json-b verwenden, wäre das nicht notwendig
quarkus.jackson.fail-on-unknown-properties=false
quarkus.jackson.fail-on-empty-beans=false

1.4. Make the Initial Commit

cd openid-connect-policies

git init
git add .
git commit -m "inital commit"
git remote add origin https://github.com/<your github-account>/openid-connect-policies.git
git push -u origin master

idea .

1.5. Add a extension

./mvnw quarkus:add-extension -Dextensions="keycloak-authorization"

1.6. Start Keycloak

docker run --name keycloak -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e DB_VENDOR=h2 -p 8180:8080 jboss/keycloak:15.0.2

Open in Browser: http://localhost:8180/auth

1.7. Configure Keycloak

→ Administration Console

  • Add realm

    • Name: quarkus

    • Create

  • Clients

    • Create

      • ClientID: my-backend-service

      • Client Protocol: openid-connect

      • Click Save

  • Users

    • Add user

      • Details

        • Username: admin

        • Email Verified: ON

        • Save

      • Credentials

        • Password: passme

        • Password Confirmation: passme

        • Temporary: OFF

        • Set Password

        • Are you sure you want to set a password for the user?: Set password

Now make a User "user" like "admin".

  • Clients → my-backend-service

    • Settings

      • Access Type: confidential

      • Service Accounts Enabled: ON

      • Authorization Enabled: OFF

      • Valid Redirect URIs: http://localhost:8080/

      • Save

    • Credentials

      • Copy Secret into clipboard

      • Paste the secret in application.properties in the line "quarkus.oidc.credentials.secret"

1.8. Access Keycloak

Now we will access Keycloak for the first time and retrieve the access token.

  1. Create a folder called http-request in the project root.

  2. Create a file called requests.http (the http-ending is important)

  3. Open the file requests.http and click on Add Environmental File → Option Regular

Now a file called http-client.env.json was created:

We define some variables:

http-client.env.json
{
  "dev": {
    "keycloak-host": "http://localhost:8180",
    "quarkus-host": "http://localhost:8080",
    "username": "my-backend-service",
    "password": "97fdb5b3-6fff-4090-966a-0f1c7355d0ba" (1)
  }
}
1 use your secret
requests.http
POST {{keycloak-host}}/auth/realms/quarkus/protocol/openid-connect/token
Authorization: Basic {{username}} {{password}}
Content-Type: application/x-www-form-urlencoded

username=user&password=passme&grant_type=password

The output shows status code 201 and the access token.

1.9. Configure Resources

  • Clients → my-backend-service

    • Authorization

      • Resources

        • Actions - Delete the Default Resource → Confirm the deletion

        • Create

        • Add Resource

          • Name: Users resource

          • Display name: Users resource

          • URI: /api/users/*

          • Save

        • Create

        • Add Resource

          • Name: Admin resource

          • Display name: Admin resource

          • URI: /api/admin/*

          • Save

clients authorization resources

  • Roles

    • Add Role

      • Role Name: user

      • Save

    • Add Role

      • Role Name: admin

      • Save

  • Users

    • View all users

    • Click on ID of user 'user'

      • Role Mappings

        • add Role 'user' to Assigned Roles

    • Click on ID of user 'admin'

      • Role Mappings

        • add Roles 'user' and admin to Assigned Roles

  • Clients → my-backend-service

    • Authorization

      • Policies

        • Create Policy …​ → Role

          • Name: Users policy

          • Description: Ability to use users resources

          • Realm Roles: user

          • Save

        • Create Policy …​ → Role

          • Name: Admin policy

          • Description: Ability to use admins resources

          • Realm Roles: admin

          • Save

      • Permissions

        • Default Permission → Delete → Confirm Deletion

        • Create Permission…​ → Resource-Based

          • Name: Users permission

          • Resources: Users resource

          • Apply Policy: Users policy

          • Save

        • Create Permission…​ → Resource-Based

          • Name: Admins permission

          • Resources: Admins resource

          • Apply Policy: Admins policy

          • Save

clients authorization permissions

1.10. Test the Access to the Resources

1.10.1. Resource admin is unauthorized (w/o any authorization)

Request with variable
GET {{quarkus-host}}/api/admin
Request + Response
GET http://localhost:8080/api/admin

HTTP/1.1 401 Unauthorized
content-length: 0

<Response body is empty>

Response code: 401 (Unauthorized); Time: 644ms; Content length: 0 bytes

1.10.2. Resource users is unauthorized (w/o any authorization)

Request with variable
GET {{quarkus-host}}/api/users
Request + Response
GET http://localhost:8080/api/users

HTTP/1.1 401 Unauthorized
content-length: 0

<Response body is empty>

Response code: 401 (Unauthorized); Time: 54ms; Content length: 0 bytes

1.11. User 'user' access Resource 'users'

1.11.1. Retrieves access token

POST {{keycloak-host}}/auth/realms/quarkus/protocol/openid-connect/token
Authorization: Basic {{username}} {{password}}   (1)
Content-Type: application/x-www-form-urlencoded

username=user&password=passme&grant_type=password

> {% client.global.set("auth_token", response.body.access_token); %}  (2)
1 you have to provide: Authorization: Basic {client-id} {secret}
2 the access-token is saved in a variable auth_token, so the next request can use it

1.11.2. Accesses the users-Resource - 200

GET {{quarkus-host}}/api/users
Authorization: Bearer {{auth_token}}

1.11.3. Accesses the admin-Resource - 403

Request with variable
GET {{quarkus-host}}/api/admin
Authorization: Bearer {{auth_token}}
Request + Response
GET http://localhost:8080/api/admin

HTTP/1.1 403 Forbidden
content-length: 0

<Response body is empty>

1.12. User 'admin' access Resource 'users'

1.12.1. Retrieves access token

POST {{keycloak-host}}/auth/realms/quarkus/protocol/openid-connect/token
Authorization: Basic {{username}} {{password}}  (1)
Content-Type: application/x-www-form-urlencoded

username=admin&password=passme&grant_type=password  (2)

> {% client.global.set("auth_token", response.body.access_token); %}
1 you have to provide: Authorization: Basic {client-id} {secret}
2 the access-token is saved in a variable auth_token, so the next request can use it

1.12.2. Accesses the users-Resource - 200

GET {{quarkus-host}}/api/users
Authorization: Bearer {{auth_token}}

1.12.3. Accesses the admin-Resource - 200

Request with variable
GET {{quarkus-host}}/api/admin
Authorization: Bearer {{auth_token}}
Request + Response
GET http://localhost:8080/api/admin

HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Length: 10
Content-Type: application/json

I am admin