{"record":{"id":"56028134623b8b55","repo":"quarkusio/quarkus","slug":"only-alice-is-allowed-to-access-this-endpoint","errorCode":null,"errorMessage":"Only Alice is allowed to access this endpoint","messagePattern":"Only Alice is allowed to access this endpoint","errorType":"exception","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OrderResource.java","lineNumber":29,"sourceCode":"import io.quarkus.security.UnauthorizedException;\nimport io.quarkus.security.identity.SecurityIdentity;\nimport io.vertx.core.eventbus.EventBus;\n\n@Path(\"order/bearer\")\npublic class OrderResource {\n\n    @Inject\n    EventBus eventBus;\n\n    @Inject\n    SecurityIdentity identity;\n\n    @POST\n    public void order(String product, @HeaderParam(AUTHORIZATION) String bearer) {\n        if (!\"alice\".equals(identity.getPrincipal().getName())) {\n            // point here is to make sure that identity is resolved and later, when the event is consumed\n            // this identity won't be available as it will be brand-new request context\n            throw new UnauthorizedException(\"Only Alice is allowed to access this endpoint\");\n        }\n        String rawToken = bearer.substring(\"Bearer \".length());\n        eventBus.publish(\"product-order\", new Product(product, 1, rawToken));\n    }\n\n    @GET\n    public String acquiredIdentities() {\n        return String.join(\" \", OrderService.IDENTITY_REPOSITORY);\n    }\n}\n","sourceCodeStart":11,"sourceCodeEnd":40,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OrderResource.java#L11-L40","documentation":"This UnauthorizedException is thrown by the OrderResource POST endpoint when the authenticated principal is not 'alice'. It exists in the test to guarantee the SecurityIdentity is resolved eagerly and to verify that when the corresponding event is later consumed, the identity from the original request context is no longer available (a brand-new request context).","triggerScenarios":"POST /order is called with an authenticated user whose principal name is not 'alice', e.g. 'bob' or any other valid Keycloak/WireMock token subject.","commonSituations":"Testing RBAC with different test users, forgetting to switch from an admin token to alice's token in the test client, or token mapping/claim changes causing the principal name to differ from 'alice'.","solutions":["Authenticate as user 'alice' (obtain a token for alice from Keycloak/WireMock) before calling the endpoint.","Check the Authorization header is set to 'Bearer <alice-token>' and the token is valid/not expired.","Verify the token's preferred_username claim actually resolves to 'alice' in your tenant configuration.","If intentionally testing denial, expect and assert the 401 response in the test instead of treating it as a failure."],"exampleFix":"// before (wrong user)\nString token = getToken(\"bob\");\ngiven().auth().oauth2(token).post(\"/order\"); // 401 Only Alice is allowed\n// after\nString token = getToken(\"alice\");\ngiven().auth().oauth2(token).body(\"product\").post(\"/order\"); // 200","handlingStrategy":"try-catch","validationCode":"String user = identity.getPrincipal().getName();\nif (!\"alice\".equals(user)) {\n    throw new UnauthorizedException(\"Only Alice is allowed to access this endpoint\");\n}","typeGuard":"boolean isAlice(SecurityIdentity identity) {\n    return identity != null && identity.getPrincipal() != null\n            && \"alice\".equals(identity.getPrincipal().getName());\n}","tryCatchPattern":"try {\n    eventBus.publish(\"product-order\", new Product(product, 1, rawToken));\n} catch (UnauthorizedException e) {\n    log.warnv(\"denied order for user {0}\", identity.getPrincipal().getName());\n    throw e; // map to 401 via exception mapper\n}","preventionTips":["Obtain tokens for the correct test user before calling identity-gated endpoints.","Assert the preferred_username claim when debugging principal mismatches.","Prefer @Authorization/@RolesAllowed or programmatic SecurityIdentity checks over ad-hoc string compares in production code.","Remember the identity is bound to the request context when consumed asynchronously on the event bus."],"tags":["authorization","oidc","rest","identity"],"backgroundTag":"unauthorized-user","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"}