{"record":{"id":"47a58a2330ea928a","repo":"quarkusio/quarkus","slug":"an-exception-should-have-been-thrown-because-authe","errorCode":null,"errorMessage":"An exception should have been thrown because authentication happened before Tenant was selected with the @Tenant annotation","messagePattern":"An exception should have been thrown because authentication happened before Tenant was selected with the @Tenant annotation","errorType":"exception","errorClass":"IllegalStateException","httpStatus":500,"severity":"error","filePath":"integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TenantEchoResource.java","lineNumber":60,"sourceCode":"    @Path(\"/hr-classic-and-jaxrs-perm-check\")\n    @GET\n    @Produces(MediaType.TEXT_PLAIN)\n    public String getHrTenantClassicAndJaxRsPermCheck() {\n        return getTenantInternal();\n    }\n\n    @PermissionsAllowed(\"get-tenant\")\n    @Path(\"/hr-identity-augmentation\")\n    @GET\n    @Produces(MediaType.TEXT_PLAIN)\n    public String getHrTenantIdentityAugmentation() {\n        return getTenantInternal();\n    }\n\n    @Path(\"/http-security-policy-applies-all-diff\")\n    @GET\n    public String httpSecurityPolicyAppliesAllDiff() {\n        throw new IllegalStateException(\"An exception should have been thrown because authentication happened\" +\n                \" before Tenant was selected with the @Tenant annotation\");\n    }\n\n    @Path(\"/http-security-policy-applies-all-same\")\n    @GET\n    public String httpSecurityPolicyAppliesAllSame() {\n        return getTenantInternal();\n    }\n\n    private String getTenantInternal() {\n        return OidcUtils.TENANT_ID_ATTRIBUTE + \"=\" + routingContext.get(OidcUtils.TENANT_ID_ATTRIBUTE)\n                + \", static.tenant.id=\" + routingContext.get(\"static.tenant.id\")\n                + \", name=\" + identity.getPrincipal().getName()\n                + \", \" + OidcUtils.TENANT_ID_SET_BY_ANNOTATION + \"=\"\n                + routingContext.get(OidcUtils.TENANT_ID_SET_BY_ANNOTATION);\n    }\n}\n","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TenantEchoResource.java#L42-L78","documentation":"This IllegalStateException is thrown by the TenantEchoResource endpoint /http-security-policy-applies-all-diff as a canary: when a @Tenant annotation is combined with an HTTP SecurityPolicy that applies to all paths, authentication must never happen before the tenant is selected. Reaching this method body proves the ordering guarantee was violated, so the endpoint always fails loudly.","triggerScenarios":"GET /http-security-policy-applies-all-diff is dispatched and Quarkus HTTP security policy authentication runs before the @Tenant annotation-based tenant selection resolves the tenant — i.e. a regression in security-policy vs tenant-interceptor ordering.","commonSituations":"Upgrading Quarkus versions where HTTP SecurityPolicy execution order changed, adding a policy with appliesTo=all while using @Tenant on the same endpoint, or misconfigured policy registration in the application.","solutions":["Verify the HTTP SecurityPolicy is registered so it does not force authentication before tenant selection for @Tenant-annotated endpoints.","Check that the @Tenant interceptor runs prior to authentication in this configuration.","Review recent changes to HTTP SecurityPolicy appliesTo=ALL handling in the Quarkus version under test.","Enable DEBUG logging for io.quarkus.vertx.http.runtime.security and io.quarkus.oidc to inspect ordering."],"exampleFix":"// before (broken ordering — endpoint reached after authentication)\n@Path(\"/http-security-policy-applies-all-diff\")\n@GET\npublic String httpSecurityPolicyAppliesAllDiff() {\n    throw new IllegalStateException(\"authentication happened before @Tenant was selected\");\n}\n// after (correct ordering: tenant selected before auth; method returns normally)\n@Path(\"/http-security-policy-applies-all-diff\")\n@GET\n@Tenant(\"diff\")\npublic String httpSecurityPolicyAppliesAllDiff() {\n    return getTenantInternal();\n}","handlingStrategy":"type-guard","validationCode":"// in a test, assert the endpoint is never reachable with an authenticated identity\nRestAssured.get(\"/http-security-policy-applies-all-diff\")\n    .then().statusCode(500); // must not return 200","typeGuard":"boolean tenantSelectedBeforeAuth(SecurityIdentity identity, Tenant currentTenant) {\n    return currentTenant != null && identity != null && identity.isAnonymous() == false\n            && TENANT_AUTH_ORDER.isTenantFirst(); // custom ordering check\n}","tryCatchPattern":"try {\n    tenantEchoResource.httpSecurityPolicyAppliesAllDiff();\n} catch (IllegalStateException e) {\n    assertTrue(e.getMessage().contains(\"before Tenant was selected\"));\n}","preventionTips":["Keep @Tenant-annotated endpoints out of HTTP SecurityPolicies that force appliesTo=all authentication.","Add ordering regression tests whenever upgrading Quarkus HTTP security versions.","Document policy-vs-tenant interceptor ordering for custom policies.","Verify with DEBUG logs on io.quarkus.vertx.http.runtime.security that tenant resolution precedes authentication."],"tags":["security-policy","tenant","ordering","oidc"],"backgroundTag":"tenant-selected-after-authentication","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}