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

  1. Set 'quarkus.http.auth.permission."permissions".applies-to=JAXRS' on every HTTP Security Policy that secures the same endpoints as the @Tenant annotation
  2. 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
  3. 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

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

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/87c655cbff222488. Report an issue: GitHub.