Quarkus Integration
Wire authorization-core into Quarkus 3 using CDI interception, MicroProfile Config, and the @Startup bean for resource registration.
Edit this page on GitHubThe Quarkus integration uses CDI @Interceptor and MicroProfile Config. Add the dependency, configure application.properties, and annotate your JAX-RS endpoints.
1. Add the dependency#
<dependency>
<groupId>io.gitlab.ctu-iotlab</groupId>
<artifactId>com.authorization.core</artifactId>
<version>0.1.5</version>
</dependency>CDI discovery is enabled by the META-INF/beans.xml shipped inside the jar:
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee" bean-discovery-mode="all"/>The following CDI beans are registered automatically:
QuarkusApplicationConfigProvider—@ApplicationScoped, reads config viaConfigProvider(MicroProfile Config)QuarkusHeaderExtractor—@RequestScoped, extracts headers from the current JAX-RSContainerRequestContextQuarkusResourceCollector—@ApplicationScoped, scans CDI beans for@ResourcemethodsQuarkusInitialize—@Singleton @Startup, runs@PostConstructfor resource registrationResourceInterceptor—@Interceptor @Resource, CDI@AroundInvokeinterceptor
2. Configure application.properties#
# Required
ctu.iotlab.resource-config.url=https://api.permix.dev
ctu.iotlab.resource-config.service-name=report-service
ctu.iotlab.resource-config.enabled=true
# OIDC server URL (used by TokenProvider to build the token exchange URL)
quarkus.oidc.auth-server-url=https://keycloak.example.com/realms/myrealm# Environment variables (token exchange credentials)
export ADMIN_CLIENT_ID=iotlab-admin
export ADMIN_CLIENT_SECRET=your-client-secretDisable enforcement in dev mode (Quarkus sets the dev profile automatically with quarkus dev):
# No property needed — dev mode is detected from Quarkus profile automatically.
# To also disable runtime checks in dev:
%dev.ctu.iotlab.resource-config.enabled=false3. Annotate your JAX-RS resource#
@Path("/reports")
@ApplicationScoped
public class ReportResource {
@Inject
ReportService reportService;
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
@Resource(
name = "report:read",
displayName = "Read Report",
defaultRoles = {"analyst", "admin"}
)
public Response getReport(@PathParam("id") String id) {
return Response.ok(reportService.findById(id)).build();
}
@DELETE
@Path("/{id}")
@Resource(
name = "report:delete",
displayName = "Delete Report",
defaultRoles = {"admin"}
)
public Response deleteReport(@PathParam("id") String id) {
reportService.delete(id);
return Response.noContent().build();
}
}You can also annotate at the class level:
@Resource(name = "reports", defaultRoles = {"admin"})
@Path("/reports")
@ApplicationScoped
public class ReportResource { ... }4. How the CDI interceptor works#
ResourceInterceptor is bound to @Resource via @InterceptorBinding. It runs at Priority.APPLICATION:
@Resource
@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
public class ResourceInterceptor {
@Inject HeaderExtractor headerExtractor;
@Inject QuarkusApplicationConfigProvider configProvider;
@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception {
// Check method annotation first, then class annotation
Resource resource = ctx.getMethod().getAnnotation(Resource.class);
if (resource == null) {
resource = ctx.getTarget().getClass().getAnnotation(Resource.class);
}
if (resource != null) {
try {
ResourceHandler.getInstance(configProvider)
.handle(resource, headerExtractor);
} catch (SecurityException e) {
throw new ForbiddenException(
"Resource access denied: " + resource.name()
);
}
}
return ctx.proceed();
}
}ForbiddenException maps to HTTP 403 Forbidden in Quarkus/RESTEasy.
5. Header forwarding#
QuarkusHeaderExtractor is a @RequestScoped CDI bean that reads all headers from the JAX-RS ContainerRequestContext. Like the Spring integration, headers are sanitized by HeaderSanitizer before being forwarded to the Permix:
Stripped headers: connection, host, content-length, expect, upgrade, transfer-encoding
All other headers — including Authorization (JWT), X-Correlation-ID, and custom tenant headers — are forwarded transparently.
6. Config provider#
QuarkusApplicationConfigProvider uses MicroProfile Config (ConfigProvider.getConfig()). This means all standard MicroProfile Config sources work out of the box:
application.propertiesinsrc/main/resources- Environment variables (e.g.
CTU_IOTLAB_RESOURCE_CONFIG_URL) - System properties
- Any custom
ConfigSourceyou register
// Reading a config value internally:
ConfigProvider.getConfig()
.getOptionalValue("ctu.iotlab.resource-config.url", String.class)
.orElse(null);7. Startup registration#
QuarkusInitialize is annotated @Singleton @Startup and runs @PostConstruct after the application is fully booted:
- Checks
ctu.iotlab.resource-config.enabled— exits early if not"true". - Checks service name is not
ctu-resource-management-service. QuarkusResourceCollector.collect()scans all CDI beans for@Resource-annotated methods.ResourceInitializer.init(resources)performs theclient_credentialstoken exchange and callsPOST /resources/list.- Logs resource count and duration (skipped in dev mode).
Dev mode detection: QuarkusEnvironmentUtil.isDevMode() calls ConfigUtils.getProfiles() and checks for "dev". Quarkus quarkus dev sets this automatically — no config needed.
Sample startup output:
2025-09-10 08:00:00,123 INFO [com.ctu.iotlab] (main) Resource sync process started. Activated profile prod.
2025-09-10 08:00:00,456 INFO [com.ctu.iotlab] (main) Scanned 5 resources.8. beans.xml#
The library ships its own META-INF/beans.xml with bean-discovery-mode="all". If your application already has a beans.xml, ensure it does not restrict CDI scanning:
<!-- Good — allows the SDK beans to be discovered -->
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
bean-discovery-mode="all">
</beans>If your beans.xml uses bean-discovery-mode="annotated", make sure to add @Dependent or another scope annotation on any beans you expect to be discovered.
Full application.properties example#
# ── Authorization SDK ──────────────────────────────────────────────
ctu.iotlab.resource-config.url=https://api.permix.dev
ctu.iotlab.resource-config.service-name=report-service
ctu.iotlab.resource-config.enabled=true
# ── OIDC (Keycloak issuer — used to derive token URL) ─────────────
quarkus.oidc.auth-server-url=https://keycloak.example.com/realms/myrealm
# ── OIDC client for inbound token validation ──────────────────────
quarkus.oidc.client-id=report-service
quarkus.oidc.credentials.secret=oidc-secret
# ── Dev overrides (quarkus dev sets %dev profile automatically) ───
%dev.ctu.iotlab.resource-config.enabled=false# Token exchange credentials (env vars)
ADMIN_CLIENT_ID=iotlab-admin
ADMIN_CLIENT_SECRET=super-secretQuarkus native image
The SDK uses java.net.http.HttpClient and Jackson for HTTP calls, both of which work in Quarkus native builds. No additional reflection configuration is required for the SDK itself. Ensure your own @Resource-annotated classes are included in the native build.