quarkusio/quarkus · error · io.quarkus.security.AuthenticationFailedException
The '%1$s' selected with the @Tenant annotation must be used
Error message
The '%1$s' selected with the @Tenant annotation must be used to authenticate the request but it was already authenticated with the '%2$s' tenant. It can happen if the '%1$s' is selected with an annotation but '%2$s' is resolved during authentication required by the HTTP Security Policy which is enforced before the JAX-RS chain is run. In such cases, please set the 'quarkus.http.auth.permission."permissions".applies-to=JAXRS' to all HTTP Security Policies which secure the same REST endpoints as the ones where the '%1$s' tenant is resolved by the '@Tenant' annotation.
What it means
Quarkus OIDC throws this when a JAX-RS endpoint selects a tenant via the @Tenant annotation, but the request was already authenticated with a different tenant. This happens because HTTP Security Policies run authentication before the JAX-RS chain, resolving a different tenant. The framework detects the mismatch between the pre-authenticated tenant and the annotation-selected tenant and rejects the request.
Source
Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java:109
}
public Function<String, Consumer<RoutingContext>> tenantResolverInterceptorCreator() {
return new Function<String, Consumer<RoutingContext>>() {
@Override
public Consumer<RoutingContext> apply(String tenantId) {
return new Consumer<RoutingContext>() {
@Override
public void accept(RoutingContext routingContext) {
OidcTenantConfig tenantConfig = routingContext.get(OidcTenantConfig.class.getName());
if (tenantConfig != null) {
// authentication has happened before @Tenant annotation was matched with the HTTP request
String tenantUsedForAuth = tenantConfig.tenantId().orElse(null);
if (tenantId.equals(tenantUsedForAuth)) {
// @Tenant selects the same tenant as already selected
return;
} else {
// @Tenant selects the different tenant than already selected
throw new AuthenticationFailedException(
"""
The '%1$s' selected with the @Tenant annotation must be used to authenticate
the request but it was already authenticated with the '%2$s' tenant. It
can happen if the '%1$s' is selected with an annotation but '%2$s' is
resolved during authentication required by the HTTP Security Policy which
is enforced before the JAX-RS chain is run. In such cases, please set the
'quarkus.http.auth.permission."permissions".applies-to=JAXRS' to all HTTP
Security Policies which secure the same REST endpoints as the ones
where the '%1$s' tenant is resolved by the '@Tenant' annotation.
"""
.formatted(tenantId, tenantUsedForAuth));
}
}
LOG.debugf("@Tenant annotation set a '%s' tenant id on the %s request path", tenantId,
routingContext.request().path());
routingContext.put(OidcUtils.TENANT_ID_SET_BY_ANNOTATION, tenantId);
routingContext.put(OidcUtils.TENANT_ID_ATTRIBUTE, tenantId);View on GitHub (pinned to e1c734241f)
Solutions
- Set 'quarkus.http.auth.permission."permissions".applies-to=JAXRS' on every HTTP Security Policy that secures the same endpoints as the @Tenant annotation
- Ensure the tenant resolved by the HTTP Security Policy matches the tenant selected with @Tenant, or remove the policy's early authentication for those paths
- Review custom TenantResolver implementations so they resolve consistently at both the policy and JAX-RS phases
Example fix
// before quarkus.http.auth.permission.authenticated.paths=/api/* quarkus.http.auth.permission.authenticated.policy=authenticated // after quarkus.http.auth.permission.authenticated.paths=/api/* quarkus.http.auth.permission.authenticated.policy=authenticated quarkus.http.auth.permission.authenticated.applies-to=JAXRS
Defensive patterns
Strategy: validation
Validate before calling
// Verify policies covering @Tenant endpoints use applies-to=JAXRS
List.of("quarkus.http.auth.permission." + name + ".applies-to")
.forEach(p -> check(config.getProperty(p).equals("JAXRS"), p + " must be JAXRS")); Try / catch
try { callApi(); } catch (AuthenticationFailedException e) { log.error("Tenant mismatch: set applies-to=JAXRS on matching HTTP policies", e); throw new WebApplicationException(401); } Prevention
- Always set applies-to=JAXRS on policies securing @Tenant-annotated endpoints
- Audit HTTP Security Policy paths against @Tenant endpoint paths each release
- Write an integration test asserting @Tenant endpoints authenticate with the annotated tenant
When it happens
Trigger: A request matches an HTTP Security Policy that forces authentication (resolving tenant '%2$s' via the default or custom tenant resolver) before the JAX-RS chain runs; later the endpoint's @Tenant annotation resolves tenant '%1$s', which differs from the already-authenticated tenant, so OidcRecorder's RoutingContext consumer throws AuthenticationFailedException.
Common situations: Multi-tenant OIDC apps where quarkus.http.auth.permission policies secure REST endpoints without applies-to=JAXRS; mixing @Tenant-annotated endpoints with global HTTP auth policies; adding a new security policy that unintentionally triggers early authentication for tenant-scoped endpoints.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Authentication has happened before the '@AuthenticationConte
- OIDC tenants '%s' and '%s' share the same resource metadata
- Extra steps left over
- Unsupported value type: %s
- Unable to fully read json value
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/87c655cbff222488.
Report an issue: GitHub.