diff --git a/docs/src/main/asciidoc/security-keycloak-authorization.adoc b/docs/src/main/asciidoc/security-keycloak-authorization.adoc index 275bace269635..2ddeb8377effb 100644 --- a/docs/src/main/asciidoc/security-keycloak-authorization.adoc +++ b/docs/src/main/asciidoc/security-keycloak-authorization.adoc @@ -1,39 +1,62 @@ //// -This guide is maintained in the main Quarkus repository -and pull requests should be submitted there: +This guide is maintained in the main Quarkus repository. +To contribute, submit a pull request here: https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc //// = Using OpenID Connect (OIDC) and Keycloak to centralize authorization include::_attributes.adoc[] :diataxis-type: howto :categories: security -:keywords: sso oidc security keycloak -:topics: security,authentication,authorization,keycloak,sso,oidc -:extensions: io.quarkus:quarkus-oidc,io.quarkus:quarkus-keycloak-authorization +:keywords: sso, oidc, security, keycloak +:topics: security, authentication, authorization, keycloak, sso, oidc +:extensions: io.quarkus:quarkus-oidc, io.quarkus:quarkus-keycloak-authorization -Learn how to enable bearer token authorization in your Quarkus application using link:https://www.keycloak.org/docs/latest/authorization_services/index.html[Keycloak Authorization Services] for secure access to protected resources. +Learn how to enable bearer token authorization in your Quarkus application by using link:https://www.keycloak.org/docs/latest/authorization_services/index.html[Keycloak Authorization Services] for secure access to protected resources. -The `quarkus-keycloak-authorization` extension relies on `quarkus-oidc`. -It includes a policy enforcer that regulates access to secured resources. -Access is governed by permissions set in Keycloak. -Currently, this extension is compatible solely with Quarkus xref:security-oidc-bearer-token-authentication.adoc[OIDC service applications]. +== Overview -It provides a flexible and dynamic authorization capability based on Resource-Based Access Control. +The `quarkus-keycloak-authorization` extension builds on the `quarkus-oidc` extension to offer advanced authorization capabilities. It includes a policy enforcer that dynamically regulates access to secured resources. Access is governed by permissions defined in Keycloak, supporting flexible and dynamic Resource-Based Access Control (RBAC). -Rather than explicitly enforcing access through specific mechanisms such as role-based access control (RBAC), `quarkus-keycloak-authorization` determines request permissions based on resource attributes such as name, identifier, or Uniform Resource Identifier (URI). -This process involves sending a `quarkus-oidc`-verified bearer access token to Keycloak Authorization Services for an authorization decision. +Use the `quarkus-keycloak-authorization` extension only if you are using Keycloak and Keycloak Authorization Services is enabled in your environment to handle authorization decisions. -Use `quarkus-keycloak-authorization` only if you work with Keycloak and have Keycloak Authorization Services enabled to make authorization decisions. -Use `quarkus-oidc` if you do not work with Keycloak or work with Keycloak but do not have its Keycloak Authorization Services enabled to make authorization decisions. +If you are not using Keycloak, or if Keycloak is configured without Keycloak Authorization Services, use the `quarkus-oidc` extension instead. -By shifting authorization responsibilities outside your application, you enhance security through various access control methods while eliminating the need for frequent re-deployments whenever security needs evolve. -In this case, Keycloak acts as a centralized authorization hub, managing your protected resources and their corresponding permissions effectively. +.How it works -For more information, see the xref:security-oidc-bearer-token-authentication.adoc[OIDC Bearer token authentication] guide. -It is important to realize that the Bearer token authentication mechanism does the authentication and creates a security identity. -Meanwhile, the `quarkus-keycloak-authorization` extension applies a Keycloak Authorization Policy to this identity based on the current request path and other policy settings. +The `quarkus-keycloak-authorization` extension centralizes authorization responsibilities in Keycloak, enhancing security and simplifying application maintenance. The extension: -For more information, see https://www.keycloak.org/docs/latest/authorization_services/index.html#_enforcer_overview[Keycloak Authorization Services documentation]. +1. Uses the `quarkus-oidc` extension to verify bearer tokens. +2. Sends verified tokens to Keycloak Authorization Services. +3. Allows Keycloak to evaluate resource-based permissions dynamically, by using attributes such as resource name, identifier, or URI. + +By externalizing authorization decisions, you can: + +- Implement diverse access control strategies without modifying application code. +- Reduce redeployment needs as security requirements evolve. + +.Compatibility + +This extension is compatible only with Quarkus xref:security-oidc-bearer-token-authentication.adoc[OIDC service applications]. It complements explicit mechanisms such as role-based access control with dynamic authorization policies. + +.Key Features + +- **Centralized Management**: Delegate authorization decisions to Keycloak for consistent security policies across applications. +- **Dynamic Permissions**: Define access control dynamically by using resource attributes. +- **Simplified Maintenance**: Reduce the need to update and redeploy applications when access policies change. + +.Setting Up + +Before using this extension, ensure the following: + +1. Keycloak Authorization Services is enabled in your Keycloak instance. +2. Your Quarkus application includes the `quarkus-keycloak-authorization` extension. + +For detailed steps, see the xref:security-oidc-bearer-token-authentication.adoc[OIDC Bearer Token Authentication] guide. + +.Additional resources + +To learn more about Keycloak Authorization Services and the policy enforcer, visit the official documentation: +https://www.keycloak.org/docs/latest/authorization_services/index.html#_enforcer_overview[Keycloak Authorization Services Documentation]. == Prerequisites @@ -42,30 +65,55 @@ include::{includes}/prerequisites.adoc[] * https://stedolan.github.io/jq/[jq tool] * https://www.keycloak.org[Keycloak] + == Architecture -In this example, we build a very simple microservice that offers two endpoints: +This example demonstrates a simple microservice setup with two protected endpoints: * `/api/users/me` * `/api/admin` -These endpoints are protected. -Access is granted only when a client sends a bearer token with the request. -This token must be valid, having a correct signature, expiration date, and audience. -Additionally, the microservice must trust the token. +.Token-based access control + +Access to these endpoints is controlled by using bearer tokens. To gain access, the following conditions must be met: + +- **Valid token**: The token must have a correct signature, a valid expiration date, and the appropriate audience. +- **Trust**: The microservice must trust the issuing Keycloak server. -The bearer token is issued by a Keycloak server and represents the subject for which the token was issued. -For being an OAuth 2.0 Authorization Server, the token also references the client acting on behalf of the user. +The bearer tokens issued by the Keycloak server serve as: -The `/api/users/me` endpoint can be accessed by any user with a valid token. -As a response, it returns a JSON document with details about the user obtained from the information carried on the token. -This endpoint is protected with RBAC, and only users granted with the `user` role can access this endpoint. +- **User identifiers**: Indicating the subject (user) for whom the token was issued. +- **Client references**: Identifying the client application acting on behalf of the user, per OAuth 2.0 Authorization Server standards. -The `/api/admin` endpoint is protected with RBAC, and only users granted the `admin` role can access it. +.Endpoints and access policies -This is a very simple example of using RBAC policies to govern access to your resources. -However, Keycloak supports other policies that you can use to perform even more fine-grained access control. -By using this example, you'll see that your application is completely decoupled from your authorization policies, with enforcement purely based on the accessed resource. +For `/api/users/me`: + +- **Access policy**: Open to users with a valid bearer token and the `user` role. +- **Response**: Returns user details as a JSON object derived from the token. + +Example response: +[source,json] +---- +{ + "user": { + "id": "1234", + "username": "johndoe", + "email": "johndoe@example.com" + } +} +---- + +For `/api/admin`: + +- *Access policy*: Restricted to users with a valid bearer token and the `admin` role. + +.Decoupled authorization + +This example highlights the use of role-based access control (RBAC) policies to protect resources. Key points include: + +- *Policy flexibility*: Keycloak supports various policy types, such as attribute-based and custom policies, enabling fine-grained control. +- *Decoupled application logic*: Authorization policies are managed entirely by Keycloak, allowing your application to focus on its core functionality. == Solution @@ -78,23 +126,22 @@ The solution is in the `security-keycloak-authorization-quickstart` link:{quicks == Creating the project -First, we need a new project. -Create a new project with the following command: +To get started, create a new project by using the following command: :create-app-artifact-id: security-keycloak-authorization-quickstart :create-app-extensions: oidc,keycloak-authorization,rest-jackson include::{includes}/devtools/create-app.adoc[] -This command generates a project, importing the `keycloak-authorization` extension. -This extension implements a Keycloak Adapter for Quarkus applications and provides all the necessary capabilities to integrate with a Keycloak server and perform bearer token authorization. +This command generates a new project with the `keycloak-authorization` extension. The extension integrates a Keycloak Adapter into your Quarkus application, providing the necessary capabilities to interact with a Keycloak server and perform bearer token authorization. -If you already have your Quarkus project configured, you can add the `oidc` and `keycloak-authorization` extensions -to your project by running the following command in your project base directory: +.Adding extensions to an existing project + +If you already have an existing Quarkus project, you can add the `oidc` and `keycloak-authorization` extensions by running the following command in your project’s base directory: :add-extension-extensions: oidc,keycloak-authorization include::{includes}/devtools/extension-add.adoc[] -This adds the following dependencies to your build file: +This command adds the following dependencies to your build file: [source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] .pom.xml @@ -116,8 +163,9 @@ implementation("io.quarkus:quarkus-oidc") implementation("io.quarkus:quarkus-keycloak-authorization") ---- -Let's start by implementing the `/api/users/me` endpoint. -As you can see in the following source code, it is a regular Jakarta REST resource: +.Implementing the `/api/users/me` endpoint + +Start by implementing the `/api/users/me` endpoint. The following code defines a Jakarta REST resource that provides user details: [source,java] ---- @@ -159,7 +207,9 @@ public class UsersResource { } ---- -The source code for the `/api/admin` endpoint is also very simple: +.Implementing the `/api/admin` endpoint + +Next, define the `/api/admin` endpoint. The following code represents a simple Jakarta REST resource protected with authentication: [source,java] ---- @@ -184,49 +234,81 @@ public class AdminResource { } ---- -Be aware that we have not defined annotations such as `@RolesAllowed` to explicitly enforce access to a resource. -Instead, the extension is responsible for mapping the URIs of the protected resources in Keycloak and evaluating the permissions accordingly, granting or denying access depending on the permissions granted by Keycloak. +.Role-based access control with Keycloak + +Notice that explicit annotations such as `@RolesAllowed` are not defined to enforce access control for the resources. Instead, the `keycloak-authorization` extension dynamically maps the URIs of protected resources in Keycloak. + +Access control is managed as follows: -=== Configuring the application +- Keycloak evaluates permissions for each request based on its configured policies. +- The extension enforces these permissions, granting or denying access based on the roles or policies defined in Keycloak. -The OpenID Connect extension allows you to define the adapter configuration by using the `application.properties` file, which is usually located in the `src/main/resources` directory. +This decouples access control logic from the application code, making it easier to manage and update access policies directly in Keycloak. + +== Configuring the application + +You can use the OpenID Connect extension to configure the adapter settings through the `application.properties` file, typically located in the `src/main/resources` directory. Below is an example configuration: [source,properties] ---- # OIDC Configuration -%prod.quarkus.oidc.auth-server-url=https://localhost:8543/realms/quarkus -quarkus.oidc.client-id=backend-service -quarkus.oidc.credentials.secret=secret -quarkus.oidc.tls.verification=none +%prod.quarkus.oidc.auth-server-url=https://localhost:8543/realms/quarkus <1> +quarkus.oidc.client-id=backend-service <2> +quarkus.oidc.credentials.secret=secret <3> +quarkus.oidc.tls.verification=none <4> # Enable Policy Enforcement -quarkus.keycloak.policy-enforcer.enable=true +quarkus.keycloak.policy-enforcer.enable=true <5> -# Tell Dev Services for Keycloak to import the realm file -# This property is not effective when running the application in JVM or native modes -quarkus.keycloak.devservices.realm-path=quarkus-realm.json +# Import the realm file with Dev Services for Keycloak +# Note: This property is effective only in dev mode, not in JVM or native modes +quarkus.keycloak.devservices.realm-path=quarkus-realm.json <6> ---- +<1> Specifies the URL of the Keycloak server and the realm used for authentication. +<2> Identifies the client application within the Keycloak realm. +<3> Defines the client secret for authentication with the Keycloak server. +<4> Disables TLS verification for development purposes. Not recommended for production. +<5> Enables the Keycloak policy enforcer to manage access control based on defined permissions. +<6> Configures Dev Services to import a specified realm file, effective only in dev mode and not in JVM or native modes. -NOTE: Adding a `%prod.` profile prefix to `quarkus.oidc.auth-server-url` ensures that Dev Services for Keycloak launches a container for you when the application is run in dev mode. -For more information, see the <> section. +[NOTE] +==== +Adding the `%prod.` profile prefix to `quarkus.oidc.auth-server-url` ensures that Dev Services for Keycloak automatically launches a container in development mode. For more details, see the <> section. +==== -NOTE: By default, applications that use the `quarkus-oidc` extension are marked as a `service` type application (see `quarkus.oidc.application-type`). -This extension also supports only `web-app` type applications but only if the access token returned as part of the authorization code grant response is marked as a source of roles: `quarkus.oidc.roles.source=accesstoken` (`web-app` type applications check ID token roles by default). +[NOTE] +==== +By default, applications using the `quarkus-oidc` extension are treated as `service` type applications. However, the extension also supports `web-app` type applications under the following conditions: + +- The access token returned during the authorization code grant flow must be the source of roles (`quarkus.oidc.roles.source=accesstoken`). +- Note: For `web-app` type applications, ID token roles are checked by default. +==== == Starting and configuring the Keycloak server -NOTE: Do not start the Keycloak server when you run the application in dev mode. +[NOTE] +==== +Do not start the Keycloak server when you run the application in dev mode. Dev Services for Keycloak launches a container. For more information, see the <> section. +==== To start a Keycloak server, use the following Docker command: [source,bash,subs=attributes+] ---- -docker run --name keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8543:8443 -v "$(pwd)"/config/keycloak-keystore.jks:/etc/keycloak-keystore.jks quay.io/keycloak/keycloak:{keycloak.version} start --hostname-strict=false --https-key-store-file=/etc/keycloak-keystore.jks +docker run --name keycloak \ + -e KEYCLOAK_ADMIN=admin \ + -e KEYCLOAK_ADMIN_PASSWORD=admin \ + -p 8543:8443 \ + -v "$(pwd)"/config/keycloak-keystore.jks:/etc/keycloak-keystore.jks \ + quay.io/keycloak/keycloak:{keycloak.version} \ <1> + start --hostname-strict=false --https-key-store-file=/etc/keycloak-keystore.jks <2> ---- -where `keycloak.version` must be `25.0.6` or later and the `keycloak-keystore.jks` can be found in https://github.com/quarkusio/quarkus-quickstarts/blob/main/security-keycloak-authorization-quickstart/config/keycloak-keystore.jks[quarkus-quickstarts/security-keycloak-authorization-quickstart/config]. +<1> For `keycloak.version`, ensure the version is `25.0.6` or later. +<2> For Keycloak keystore, use the `keycloak-keystore.jks` file located at https://github.com/quarkusio/quarkus-quickstarts/blob/main/security-keycloak-authorization-quickstart/config/keycloak-keystore.jks[quarkus-quickstarts/security-keycloak-authorization-quickstart/config]. + Try to access your Keycloak server at https://localhost:8543[localhost:8543]. @@ -236,52 +318,83 @@ The username and password are both `admin`. Import the link:{quickstarts-tree-url}/security-keycloak-authorization-quickstart/config/quarkus-realm.json[realm configuration file] to create a new realm. For more details, see the Keycloak documentation about how to https://www.keycloak.org/docs/latest/server_admin/index.html#_create-realm[create a new realm]. -After importing the realm you can see the resource permissions: +After importing the realm, you can see the resource permissions: image::keycloak-authorization-permissions.png[alt=Keycloak Authorization Permissions,role="center"] It explains why the endpoint has no `@RolesAllowed` annotations - the resource access permissions are set directly in Keycloak. +.Accessing the Keycloak server + +. Open your browser and navigate to https://localhost:8543[https://localhost:8543]. +. Log in to the Keycloak Administration Console by using the following credentials: + - **Username**: `admin` + - **Password**: `admin` + +.Importing the realm configuration + +To create a new realm, import the link:{quickstarts-tree-url}/security-keycloak-authorization-quickstart/config/quarkus-realm.json[realm configuration file]. For detailed steps on creating realms, refer to the Keycloak documentation: https://www.keycloak.org/docs/latest/server_admin/index.html#_create-realm[Create a new realm]. + +After importing the realm, you can review the resource permissions: + +image::keycloak-authorization-permissions.png[alt=Keycloak Authorization Permissions,role="center"] + +.Role of Keycloak in resource permissions + +The resource access permissions are configured directly in Keycloak, which eliminates the need for `@RolesAllowed` annotations in your application code. This approach centralizes access control management within Keycloak, simplifying application maintenance and security updates. + [[keycloak-dev-mode]] == Running the application in dev mode -To run the application in dev mode, use: +To run the application in development mode, use the following command: include::{includes}/devtools/dev.adoc[] -xref:security-openid-connect-dev-services.adoc[Dev Services for Keycloak] launches a Keycloak container and imports a `quarkus-realm.json`. +xref:security-openid-connect-dev-services.adoc[Dev Services for Keycloak] starts a Keycloak container and imports the `quarkus-realm.json` configuration file. Open a xref:dev-ui.adoc[Dev UI] available at http://localhost:8080/q/dev-ui[/q/dev-ui] and click a `Provider: Keycloak` link in an `OpenID Connect` `Dev UI` card. -When asked to log in to a `Single Page Application` provided by `OpenID Connect Dev UI`: +.Interacting with Dev UI + +. Open the xref:dev-ui.adoc[Dev UI] at http://localhost:8080/q/dev-ui[/q/dev-ui]. +. Click the `Provider: Keycloak` link within the `OpenID Connect` Dev UI card. + +.Testing user permissions + +When prompted to log in to a `Single Page Application` provided by `OpenID Connect Dev UI`, do the following: + +. Log in as `alice` (password: `alice`), who only has a `User Permission` to access the `/api/users/me` resource: +.. Access `/api/admin`, which returns `403`. +.. Access `/api/users/me`, which returns `200`. +. Log out and log in as `admin` (password: `admin`), who has both `Admin Permission` to access the `/api/admin` resource and `User Permission` to access the `/api/users/me` resource: +.. Access `/api/admin`, which returns `200`. +.. Access `/api/users/me`, which returns `200`. + +.Customizing the Keycloak realm - * Log in as `alice` (password: `alice`), who only has a `User Permission` to access the `/api/users/me` resource: - ** Access `/api/admin`, which returns `403`. - ** Access `/api/users/me`, which returns `200`. - * Log out and log in as `admin` (password: `admin`), who has both `Admin Permission` to access the `/api/admin` resource and `User Permission` to access the `/api/users/me` resource: - ** Access `/api/admin`, which returns `200`. - ** Access `/api/users/me`, which returns `200`. +If you started Dev Services for Keycloak without importing a realm file such as link:{quickstarts-tree-url}/security-keycloak-authorization-quickstart/config/quarkus-realm.json[quarkus-realm.json], create a default `quarkus` realm without Keycloak authorization policies: -If you have started xref:security-openid-connect-dev-services.adoc[Dev Services for Keycloak] without importing a realm file such as link:{quickstarts-tree-url}/security-keycloak-authorization-quickstart/config/quarkus-realm.json[quarkus-realm.json] that is already configured to support Keycloak Authorization, create a default `quarkus` realm without Keycloak authorization policies. -In this case, you must select the `Keycloak Admin` link in the `OpenId Connect` Dev UI card and configure link:https://www.keycloak.org/docs/latest/authorization_services/index.html[Keycloak Authorization Services] in the default `quarkus` realm. +. Select the `Keycloak Admin` link from the `OpenID Connect` Dev UI card. +. Log in to the Keycloak admin console. The username and password are both `admin`. +. Follow the instructions at link:https://www.keycloak.org/docs/latest/authorization_services/index.html[Keycloak Authorization Services documentation] to enable authorization policies in the `quarkus` realm. The `Keycloak Admin` link is easy to find in Dev UI: image::dev-ui-oidc-keycloak-card.png[alt=Dev UI OpenID Connect Card,role="center"] -When logging into the Keycloak admin console, the username and password are both `admin`. +.Adding custom JavaScript policies -If your application uses Keycloak authorization configured with link:https://www.keycloak.org/docs/latest/authorization_services/index.html#_policy_js[JavaScript policies] that are deployed in a JAR file, you can set up Dev Services for Keycloak to transfer this archive to the Keycloak container. -For instance: +If your application uses Keycloak authorization configured with link:https://www.keycloak.org/docs/latest/authorization_services/index.html#_policy_js[JavaScript policies] that are deployed in a JAR archive, Dev Services for Keycloak can transfer this archive to the Keycloak container. Use the following properties in `application.properties` to configure the transfer: [source,properties] ---- +# Alias the policies archive quarkus.keycloak.devservices.resource-aliases.policies=/policies.jar <1> +# Map the policies archive to a specific location in the container quarkus.keycloak.devservices.resource-mappings.policies=/opt/keycloak/providers/policies.jar <2> ---- -<1> `policies` alias is created for the `/policies.jar` classpath resource. -Policy archive can also be located in the file system. -<2> The policies archive is mapped to the `/opt/keycloak/providers/policies.jar` container location. +<1> Creates a `policies` alias for the `/policies.jar` classpath resource. The policies archive can also be located on the file system. +<2> Maps the policies archive to the `/opt/keycloak/providers/policies.jar` location inside the Keycloak container. == Running the application in JVM mode