Problem Description
The [jaxrs-spec][quarkus] library already emits MicroProfile OpenAPI security scheme annotations (@Securityscheme, @securityrequirement) on API interfaces. However, these annotations have no effect at runtime, and are for documentation-only purpose in Swagger UI.
As a result, the developer must manually add Quarkus security annotations after generation.
This is a proposal to agree the mapping from OpenAPI Security Schemes to Quarkus Authentication/Authorisation mechanisms to emit the annotations during code generation.
Once agreed, I'm happy to contribute the PRs listed below.
Benefits of emitting Authenticaion & Authorisation annotations
- Security by default: generated stubs are immediately deployable with correct access control; developers do not have to remember to add annotations manually
- Spec as the source of truth: when the OpenAPI document changes (e.g., a scope is added or removed), regenerating the client automatically updates the security annotations
- Quarkus-native developer experience: generated code uses idiomatic Quarkus/Jakarta EE security annotations that developers already know, rather than requiring them to understand how to translate OpenAPI security schemes into Quarkus constructs
- Reduced security misconfigurations: missing or incorrect hand-written security annotations are a common source of accidental endpoint exposure; generator-driven annotations eliminate this class of mistake
Solution
Quarkus implements security based on Jakarta EE security annotations jakarta.annotation.security and its own extensions in io.quarkus.security. These annotations are enforced by Quarkus interceptors at request time.
| Annotation |
Package |
Description |
@Authenticated |
io.quarkus.security |
Permits any authenticated user. Equivalent to @RolesAllowed("**"). |
@RolesAllowed({"role1", "role2"}) |
jakarta.annotation.security |
Permits users that have at least one of the listed roles (OR semantics within the annotation). |
@PermitAll |
jakarta.annotation.security |
Permits all users, including unauthenticated ones. Used to mark explicitly public endpoints. |
@DenyAll |
jakarta.annotation.security |
Denies all access regardless of identity. Used to mark endpoints that must never be called directly via HTTP. |
@PermissionsAllowed |
io.quarkus.security |
Fine-grained permission check against SecurityIdentity permissions. Beyond the scope of generator support. |
Annotation placement
Annotations can be placed on JAX-RS interface methods or on implementation classes. Method-level annotations take precedence over class-level annotations. Quarkus supports annotations on the interface when the implementation class is not directly annotated.
Interaction with security configuration in application.yaml
quarkus.http.auth.* configuration-based policies (application.yaml) are evaluated before the security annotations. This means @PermitAll does not bypass HTTP-level security configurations from the application.yaml configuration
The generator focuses on annotation-based security, which is the standard approach for JAX-RS endpoint security in Quarkus.
Mapping table
| OpenAPI security requirement |
Quarkus annotation |
Rationale |
Operation has no security requirement |
@PermitAll |
Explicitly marks the endpoint as public so auditors and tooling can identify intentionally open endpoints. |
http scheme, basic |
@Authenticated |
Basic authentication validates identity; there are no roles or scopes to check. Any valid credential grants access. |
http scheme, bearer |
@Authenticated |
A Bearer token (e.g., JWT) validates identity. Role/scope enforcement is handled by the OIDC/JWT extension separately; the annotation marks the intent. |
apiKey |
@Authenticated |
An API key validates identity only. No role check is applicable. |
oauth2 with empty scopes ([]) |
@Authenticated |
An empty scope list means "any authenticated user" — no specific authorization is required beyond a valid token. |
openIdConnect with empty scopes ([]) |
@Authenticated |
Same reasoning as OAuth2 with empty scopes. |
oauth2 with explicit scopes |
@RolesAllowed({"scope1", "scope2"}) |
In Quarkus, OAuth2/OIDC token scopes are mapped to SecurityIdentity roles. @RolesAllowed receives the scopes as role names. |
openIdConnect with explicit scopes |
@RolesAllowed({"scope1"}) |
Same as OAuth2 with scopes. |
| OR list with at least one empty-scope scheme |
@Authenticated |
The least restrictive alternative dominates: if any scheme allows any authenticated user, the effective gate is authentication only. |
| OR list where all alternatives have scopes |
@RolesAllowed(union_of_all_scopes) |
The union of scopes across all OR-alternatives is passed to @RolesAllowed. Since @RolesAllowed itself uses OR semantics (user needs any listed role), this correctly allows a user to satisfy any one of the OR-alternatives. |
Limitation: OpenAPI OR semantics vs. Quarkus AND annotations
OpenAPI's security array is OR: a request is authorized if any one of the listed security alternatives is satisfied. Quarkus standard annotations use AND semantics when stacked: @Authenticated + @RolesAllowed("admin") requires the user to be both authenticated and have the admin role.
This means the two models cannot always be reconciled. The generator's strategy is to emit the least restrictive annotation that is still correct for the OR group:
- If any alternative has empty scopes →
@Authenticated (any authenticated user passes; specific scopes in other alternatives are not enforced at the annotation level)
- If all alternatives have explicit scopes →
@RolesAllowed(union_of_all_scopes) (user needs any one of the scopes, matching any one alternative)
Never emit both @Authenticated and @RolesAllowed on the same method: Quarkus would apply both interceptors, creating AND semantics stricter than the spec intends.
Delivery Plan
List of PRs to incrementally add the security annotations support:
#1 - Emit @io.quarkus.security.Authenticated` on JAX-RS interface methods and implementation stubs when an operation's security has:
http: basic authentication
apiKey authentication
http: bearer authentication
oauth2 or openIdConnect with empty scopes ([])
- an OR list where at least one alternative has empty scopes
security:
- oauth2_read: [read:items]
- oauth2_admin: []
@io.quarkus.security.Authenticated
@GET
@Path("/items")
Response listItems();
#2 - Emit @RolesAllowed for operations where all OR-alternatives have explicit scopes. Example:
security:
- oauth2_read: [read:items]
- oauth2_admin: [admin]
@jakarta.annotation.security.RolesAllowed({"read:items", "admin"})
@GET
@Path("/items")
Response listItems();
#3 - Emit @PermitAll for operations with no security requirement
@jakarta.annotation.security.PermitAll
@GET
@Path("/health")
Response healthCheck();
Considerations:
@PermissionsAllowed: Quarkus's io.quarkus.security.PermissionsAllowed supports fine-grained permission checks beyond role names. Mapping OpenAPI scopes to permissions (as opposed to roles) requires knowing how the application configures its identity provider. This is out of scope for the generator and left to the developer.
quarkus.security.jaxrs.deny-unannotated-endpoints: The generator could emit a reminder comment or a documentation note suggesting users enable this property alongside the generated annotations for defense-in-depth.
- Multi-tenant OIDC: Quarkus supports multi-tenant OIDC (
quarkus-oidc). The mapping from an OpenAPI openIdConnect scheme to a specific tenant is not expressible in annotations alone; this is a known limitation.
- Global
security overrides: OpenAPI allows a global security block at the document level, with per-operation overrides. The generator should ensure it resolves effective security for each operation (global default minus per-operation override) before applying annotation logic. This needs verification in the existing implementation.
@DenyAll: No mapping is proposed from OpenAPI constructs to @DenyAll. If needed, it could be expressed via a vendor extension (x-quarkus-deny-all: true) for operations that should be unreachable via HTTP.
Alternatives considered
The only alternative is to keep the status quo where developers need to add the Authentication and Authorisation annotations post code generation
Open Questions:
- Should it introduce a new generator flag to enable/disable emitting the security annotations? This avoids breaking changes for teams already manually managing the security annotations separately from the code generation
Labels
enhancement, jaxrs-spec, quarkus, security
Problem Description
The [jaxrs-spec][quarkus] library already emits MicroProfile OpenAPI security scheme annotations (@Securityscheme, @securityrequirement) on API interfaces. However, these annotations have no effect at runtime, and are for documentation-only purpose in Swagger UI.
As a result, the developer must manually add Quarkus security annotations after generation.
This is a proposal to agree the mapping from OpenAPI Security Schemes to Quarkus Authentication/Authorisation mechanisms to emit the annotations during code generation.
Once agreed, I'm happy to contribute the PRs listed below.
Benefits of emitting Authenticaion & Authorisation annotations
Solution
Quarkus implements security based on Jakarta EE security annotations
jakarta.annotation.securityand its own extensions inio.quarkus.security. These annotations are enforced by Quarkus interceptors at request time.@Authenticatedio.quarkus.security@RolesAllowed("**").@RolesAllowed({"role1", "role2"})jakarta.annotation.security@PermitAlljakarta.annotation.security@DenyAlljakarta.annotation.security@PermissionsAllowedio.quarkus.securitySecurityIdentitypermissions. Beyond the scope of generator support.Annotation placement
Annotations can be placed on JAX-RS interface methods or on implementation classes. Method-level annotations take precedence over class-level annotations. Quarkus supports annotations on the interface when the implementation class is not directly annotated.
Interaction with security configuration in
application.yamlquarkus.http.auth.*configuration-based policies (application.yaml) are evaluated before the security annotations. This means@PermitAlldoes not bypass HTTP-level security configurations from theapplication.yamlconfigurationThe generator focuses on annotation-based security, which is the standard approach for JAX-RS endpoint security in Quarkus.
Mapping table
securityrequirement@PermitAllhttpscheme,basic@Authenticatedhttpscheme,bearer@AuthenticatedapiKey@Authenticatedoauth2with empty scopes ([])@AuthenticatedopenIdConnectwith empty scopes ([])@Authenticatedoauth2with explicit scopes@RolesAllowed({"scope1", "scope2"})SecurityIdentityroles.@RolesAllowedreceives the scopes as role names.openIdConnectwith explicit scopes@RolesAllowed({"scope1"})@Authenticated@RolesAllowed(union_of_all_scopes)@RolesAllowed. Since@RolesAlloweditself uses OR semantics (user needs any listed role), this correctly allows a user to satisfy any one of the OR-alternatives.Limitation: OpenAPI OR semantics vs. Quarkus AND annotations
OpenAPI's
securityarray is OR: a request is authorized if any one of the listed security alternatives is satisfied. Quarkus standard annotations use AND semantics when stacked:@Authenticated+@RolesAllowed("admin")requires the user to be both authenticated and have theadminrole.This means the two models cannot always be reconciled. The generator's strategy is to emit the least restrictive annotation that is still correct for the OR group:
@Authenticated(any authenticated user passes; specific scopes in other alternatives are not enforced at the annotation level)@RolesAllowed(union_of_all_scopes)(user needs any one of the scopes, matching any one alternative)Never emit both
@Authenticatedand@RolesAllowedon the same method: Quarkus would apply both interceptors, creating AND semantics stricter than the spec intends.Delivery Plan
List of PRs to incrementally add the security annotations support:
#1 - Emit @io.quarkus.security.Authenticated` on JAX-RS interface methods and implementation stubs when an operation's security has:
http: basicauthenticationapiKeyauthenticationhttp: bearerauthenticationoauth2oropenIdConnectwith empty scopes ([])#2 - Emit
@RolesAllowedfor operations where all OR-alternatives have explicit scopes. Example:#3 - Emit
@PermitAllfor operations with no security requirementConsiderations:
@PermissionsAllowed: Quarkus'sio.quarkus.security.PermissionsAllowedsupports fine-grained permission checks beyond role names. Mapping OpenAPI scopes to permissions (as opposed to roles) requires knowing how the application configures its identity provider. This is out of scope for the generator and left to the developer.quarkus.security.jaxrs.deny-unannotated-endpoints: The generator could emit a reminder comment or a documentation note suggesting users enable this property alongside the generated annotations for defense-in-depth.quarkus-oidc). The mapping from an OpenAPIopenIdConnectscheme to a specific tenant is not expressible in annotations alone; this is a known limitation.securityoverrides: OpenAPI allows a globalsecurityblock at the document level, with per-operation overrides. The generator should ensure it resolves effective security for each operation (global default minus per-operation override) before applying annotation logic. This needs verification in the existing implementation.@DenyAll: No mapping is proposed from OpenAPI constructs to@DenyAll. If needed, it could be expressed via a vendor extension (x-quarkus-deny-all: true) for operations that should be unreachable via HTTP.Alternatives considered
The only alternative is to keep the status quo where developers need to add the Authentication and Authorisation annotations post code generation
Open Questions:
Labels
enhancement,jaxrs-spec,quarkus,security