quarkus-auth is a library that provides a unified authentication and authorization model for APIs built on OpenID Connect (OIDC). It uses a pair of OIDC clients — a public client and a confidential client — enabling frontend applications to perform secure authentication flows, while the backend API validates access tokens through token introspection using a client secret.
The library requires basic OIDC configuration (client ID, client secret, token/introspection endpoints) and integrates with any OIDC-compliant Identity Provider, such as Keycloak.
For authorization, the library leverages user entitlements, which are retrieved during token introspection. By configuring the entitlement namespace and the hierarchical parent-group structure, the library enables fine-grained access control based on roles and organizational grouping.
In summary, quarkus-auth provides:
- Secure OIDC authentication using paired public + confidential clients.
- Token introspection via the confidential client.
- Authorization based on entitlements with support for nested group hierarchy.
- Flexible configuration compatible with any OIDC provider.
- Simple integration with Quarkus-based APIs.
Before integrating and using the authorization library within the API, the following prerequisites must be met to ensure a fully functional and secure authorization flow.
A fully functional OpenID Connect (OIDC) provider is required. Τhe provider must support:
- Issuance of access tokens to public and confidential clients.
- Token introspection via a confidential client.
- Inclusion of entitlements in the introspection response when the entitlements scope is requested.
The application interacting with the public client must request the entitlements scope during the OAuth2 authorization flow. Without this scope:
- Entitlements will not be returned by the OIDC provider.
- The authorization library will be unable to evaluate user permissions.
- All entitlement-based authorization checks will fail.
⚠️ All properties listed in this section must be added to the consuming API'sapplication.properties.
These settings define the entitlement namespace, the root parent group hierarchy, and the role that grants super admin privileges.
api.auth.entitlements.namespace=urn:mace:grnet.gr:einfra:login-devel
api.auth.entitlements.parent-group=parent-group-name
api.auth.entitlements.super-admin-role=super_admin| Parameter | Description |
|---|---|
api.auth.entitlements.namespace |
The entitlement namespace that the library should match against incoming tokens. |
api.auth.entitlements.parent-group |
The root parent group, which may include multiple hierarchical subgroups. |
api.auth.entitlements.super-admin-role |
The role name assigned to the parent group that grants super admin privileges. Any user belonging to a group with this role is treated as a super admin and is given full administrative access. |
This setting defines which claim in the OIDC token is used as the unique identifier for the authenticated user.
api.auth.oidc.user.unique.id=voperson_id| Parameter | Description |
|---|---|
api.auth.oidc.user.unique.id |
The OIDC token claim used to uniquely identify the authenticated user (e.g. sub, voperson_id). |
These settings configure the connection to the Group Management Client (RCIAM), which is required for resolving/adding group memberships and entitlements at runtime.
api.auth.entitlements.keycloak-group-management-client-url=https://keycloak.example.org/group-management
api.auth.entitlements.keycloak-group-management-client-id=my-client-id
api.auth.entitlements.keycloak-group-management-client-secret=my-client-secret| Parameter | Description |
|---|---|
api.auth.entitlements.keycloak-group-management-client-url |
The base URL of the Group Management Client API (RCIAM). |
api.auth.entitlements.keycloak-group-management-client-id |
The client ID used to authenticate against the group management service. |
api.auth.entitlements.keycloak-group-management-client-secret |
The client secret used to authenticate against the group management service. Should be kept confidential and preferably injected via environment variable or a secrets manager. |
In addition to the properties below, the following Quarkus dependency must be present in the API's
pom.xml:<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-oidc</artifactId> </dependency>
These settings configure the connection to your OpenID Connect (OIDC) Identity Provider. The library uses the Client Credentials Flow for authentication. You may use any OIDC-compliant provider — Keycloak is used internally.
quarkus.oidc.client-id=backend-service
quarkus.oidc.credentials.secret=secret
quarkus.oidc.auth-server-url=https://login-devel.einfra.grnet.gr/auth/realms/einfra
quarkus.oidc.token-path=/protocol/openid-connect/token
quarkus.oidc.authorization-path=/protocol/openid-connect/auth
quarkus.oidc.introspection-path=/protocol/openid-connect/token/introspect| Parameter | Description |
|---|---|
quarkus.oidc.client-id |
The client ID registered with your Identity Provider. |
quarkus.oidc.credentials.secret |
The client secret used to authenticate with your Identity Provider. |
quarkus.oidc.auth-server-url |
The base URL of the OIDC server (realm URL for Keycloak). |
quarkus.oidc.token-path |
Relative path or absolute URL of the token endpoint. |
quarkus.oidc.authorization-path |
Relative path or absolute URL of the OIDC authorization endpoint. |
quarkus.oidc.introspection-path |
Relative path or absolute URL of the RFC 7662 token introspection endpoint. |
All entitlements must follow the expected naming and structural convention:
<namespace>:<parent-group>[/<subgroup>...]:role=<role-name>
This hierarchical model allows entitlements to be associated with:
- The parent group directly.
- Any nested subgroup.
- Arbitrarily deep group structures.
The authorization library resolves entitlements across all subgroup levels under the configured parent-group root.
The library requires:
- A Quarkus-based application.
- Configuration via application.properties or environment variables.
- Java 17+.
Our library uses an authentication model based on a pair of OIDC clients: a public client and a confidential client secured with a client secret. The public client is responsible for requesting access tokens from the Identity Provider. Since it is public, it does not store or use a client secret and is intended for environments such as browser applications. Once the public client initiates authentication, it obtains an access token.
When the public client retrieves an access token and sends it to the API. The API performs a verification step through token introspection. This introspection is carried out by the second client in the pair, the confidential client, which possesses a client secret and is treated as a trusted backend application.
The API forwards the received token to the Identity Provider’s introspection endpoint while authenticating as the confidential client using its client ID and client secret. The Identity Provider responds with information describing whether the token is active, along with its subject, expiration timestamp, scopes and any other relevant attributes. Using this information, the API decides whether access should be granted. If the token has expired or is marked as inactive, the API denies the request.
In order to integrate with our API, you must create this pair of clients in the Identity Provider. The public client handles obtaining access tokens, and the confidential client performs introspection and authorization checks on behalf of the backend. Both clients together establish the authentication mechanism required for accessing the protected API endpoints.
We use Keycloak internally, but you may use any OIDC-compliant provider. Regardless of the IdP, you must first create a pair of OIDC clients: a public client and a confidential client
To create a public client in Keycloak, a user must configure a client that does not use a client secret and supports flows typically intended for applications that cannot safely store secrets.
A user begins by navigating to the “Clients” area of the Keycloak administration console and choosing to create a new client. The client must be set to the OpenID Connect type, and the access type should be configured as public. Since public clients cannot store secrets, Keycloak automatically disables client authentication for them. Redirect URIs and Web Origins must also be configured, to ensure that the client can properly complete authentication flows. Once these settings follow the recommended instructions, the public client is ready to request tokens from Keycloak.
A confidential client in Keycloak is designed to run on the server side and securely store a client secret.
A user starts by creating a new client and selecting OpenID Connect as the client protocol. The access type must be set to confidential, which activates client authentication and enables the generation and use of a client secret. Within the client’s settings, the administrator can review or regenerate the client secret under the “Credentials” tab. Once these settings follow the recommended instructions, the confidential client can be used by backend services to validate tokens through introspection.
Applications that cannot securely store secrets, such as browser-based applications, must use the public client to obtain an access token from Keycloak. The public client is intended for environments where the application runs on the end user’s device and cannot protect confidential credentials. To authenticate, the application initiates the appropriate OIDC flow, typically the Authorization Code flow, and requests an access token using the public client configuration.
The applications is responsible for specifying the scopes required for the access token. Different APIs may require different scopes depending on the information they need to access during token introspection.
For example, our API typically expects scopes such as voperson_id, entitlements, and profile in order to retrieve the relevant user information.
However, the exact set of scopes should be determined by the application according to the specific API endpoints it intends to call.
Once the application receives the access token with the appropriate scopes, it must include the token in the Authorization header using Bearer Authentication when making requests to the API.
The API then performs token introspection and uses the scopes and other token attributes to evaluate access permissions for the requested operation.
Our API uses an authorization model that relies on the user’s entitlements. Each user may have one or more entitlements that represent the permissions or roles assigned to them. When a client sends a request with an access token, the API performs token introspection using the confidential client to validate the token and retrieve the user’s entitlements. These entitlements are then evaluated to determine whether the user is allowed to access the requested resource or perform the requested action. Access to API endpoints is granted only if the user’s entitlements meet the requirements defined for the resource. This model allows fine-grained authorization and ensures that each user can only access the resources for which they have explicit entitlements, providing a secure and flexible way to manage access control across the API.
For the authorization process to be executed correctly, the application interacting with the public client must request the entitlements scope.
Without this scope, the library cannot retrieve the user’s entitlements during token introspection, and therefore the library will be unable to evaluate whether the user is authorized to access the requested resources.
To ensure that the authorization library can correctly interpret and validate user entitlements, it must know which entitlements are relevant for the authorization process. Each entitlement follows a predefined hierarchical structure of the form:
<namespace>:<parent-group>:role=<role-name>
-
namespace: Identifies the broader domain or authority under which the entitlements are issued. -
parent-group: Defines the organizational scope or group hierarchy in which the entitlement is applicable. -
role-name: The specific role assigned to the user within the namespace and group context.
Entitlements determine what resources and operations a user is allowed to access through the API. To ensure that the quarkus-auth library can perform authorization checks, the appropriate entitlements must be assigned to each user through your Identity Provider.
In our environment, entitlements are managed through RCIAM Group Management, which automatically generates entitlements based on the user’s membership in groups and subgroups. More information is available in the official RCIAM documentation.
Whenever a user is added to a group, the corresponding entitlement is issued following the standardized structure:
<namespace>:<parent-group>/optional/subgroups:role=<role-name>
These entitlements will then be included in the user’s OIDC attributes and retrieved through token introspection.
If you are not using RCIAM or wish to assign entitlements manually, you can add them directly to the user’s OIDC attributes through your Identity Provider (e.g., Keycloak). Most IdPs allow configuring a custom attribute (entitlements) which can hold one or more values following the library’s required format.
Once these attributes are set, the quarkus-auth library will automatically read and evaluate them during token introspection, applying your authorization rules accordingly.
This module provides dynamic authorization management for REST endpoints based on RCIAM group membership. It allows you to define secured endpoints declaratively using annotations, expose them dynamically, and map them to user groups and roles stored in a database.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface SecuredEndpoint {
}These annotations define the potential secured endpoints that the system will recognize.
A Quarkus extension scans the application at build time for all @SecuredEndpoint annotations. It iterates over every occurrence of the @SecuredEndpoint annotation, filtering only method-level targets to exclude class-level or interceptor usages. For each qualifying method, it resolves the HTTP verb and constructs the relative endpoint path by combining the class-level and method-level @Path values from jakarta.ws.rs. If a @Operation annotation from org.eclipse.microprofile.openapi.annotations is present, its description field is extracted and associated with the endpoint; otherwise a default placeholder message is used. Each discovered endpoint is assigned a deterministic unique identifier generated by hashing the HTTP method and relative path using SHA-256, ensuring a stable, collision-resistant ID.
All collected metadata is then made available to a dynamic endpoint at:
GET /secured-endpointsThis endpoint returns a JSON list of all potential secured endpoints:
[
{
"action": "GET",
"path": "/v1/tenants/{id}",
"description": "Returns a specific tenant assessment.",
"secured_endpoint_id": "921fb6a55236e237eb7c08fe7b4c645be3611b36a179d7b6d371c267bf834758"
},
{
"action": "PUT",
"path": "/v1/tenants/{id}",
"description": "Updates a specific tenant.",
"secured_endpoint_id": "8bdfcb7f1256ab1710b86b4bad1260882e9943c4348e23e6e7e765495d99f9d5"
},
{
"action": "GET",
"path": "/v1/tenants/{id}/projects",
"description": "Retrieves a list of projects that tenant belongs",
"secured_endpoint_id": "c216f1aae92607adc816e2db64edc42f041f2e57e01cff1fd50e6d868f253e7f"
},
{
"action": "POST",
"path": "/v1/encrypt",
"description": "Encrypts the provided plain text secret using AES PBKDF2 and system key.",
"secured_endpoint_id": "f34641f7515ec0f99198b28f2787c722be07b89c9dc7f8224697b4882c6b3afc"
},
{
"action": "GET",
"path": "/v1/users/profile",
"description": "This endpoint retrieves the user profile information.",
"secured_endpoint_id": "b32ecfacefb21f938fdbb135d02aaaf2782a99558868a87e348157a4788b40e9"
},
{
"action": "POST",
"path": "/v1/users/registration",
"description": "Registers the authenticated user as a platform member. This is a self-registration operation.",
"secured_endpoint_id": "eaffd4d8007a9ba1ed4c8e72ea0c279973dd64718b53f1f2685892cae86f5adb"
},
{
"action": "GET",
"path": "/v1/users/pages",
"description": "Registers the authenticated user as a platform member. This is a self-registration operation.",
"secured_endpoint_id": "6f6107c3cfafe78ff61eeadc23bc84668323bf589e41aad733bad66ef8b968ea"
}
]The user must:
- Be in the specified group
@Path("/v1/admin")
@Authenticated
@SecuredEndpoint
public class AdminEndpoint {
}You have three options to obtain the quarkus-auth library.
Open the Actions page:
🔗 https://github.com/ARGOeu/quarkus-auth/actions
- Select the latest workflow run on branch
devel - Scroll down to Artifacts
- Download the archive (e.g.
quarkus-auth.zip) - Extract it and locate the file:
quarkus-auth-x.x.x.jar
Then install it into your local Maven repository:
mvn install:install-file \
-Dfile= \
-DgroupId=org.grnet \
-DartifactId=quarkus-auth \
-Dversion=x.x.x \
-Dpackaging=jar \
-DgeneratePom=trueThis will place the library under:
~/.m2/repository/org/grnet/quarkus-auth/x.x.x/
gh run download \
$(gh run list --repo ARGOeu/quarkus-auth --branch devel --limit 1 --json databaseId -q ".[0].databaseId") \
--repo ARGOeu/quarkus-auth \
--dir .Then install the JAR the same way as Option A.
The library is published to GitHub Packages and can be pulled automatically by Maven, without manually downloading or installing any JAR.
You need a GitHub token with the read:packages scope.
Go to: GitHub → Settings → Developer settings → Personal access tokens
Add the following server entry to ~/.m2/settings.xml:
<servers>
<server>
<id>github</id>
<username>xxxxxxxx</username>
<password>xxxxxxxxxxx</password>
</server>
</servers>
⚠️ Use your Personal Access Token (not your GitHub password) as the<password>value.
<repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/ARGOeu/quarkus-auth</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
Once configured, Maven will resolve the dependency automatically — no manual JAR installation needed.
In your api module's pom.xml, regardless of the installation method chosen:
<dependency>
<groupId>org.grnet</groupId>
<artifactId>quarkus-auth</artifactId>
<version>x.x.x</version>
</dependency>To use quarkus-auth, you must:
- Obtain the library via one of the three options above
- Add the dependency to
pom.xml - Include all required properties in
application.properties - Use
@SecuredEndpointwhere access control is needed
Your Quarkus project is now fully integrated with central entitlement-based authorization.