{"record":{"id":"d656c3addc1147fd","repo":"quarkusio/quarkus","slug":"retrieving-all-roles-not-supported-when-jax-rs-sec-d656c3","errorCode":null,"errorMessage":"retrieving all roles not supported when JAX-RS security context has been replaced","messagePattern":"retrieving all roles not supported when JAX-RS security context has been replaced","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java","lineNumber":76,"sourceCode":"                @Override\n                public SecurityIdentity apply(SecurityIdentity old) {\n                    Set<Credential> oldCredentials = old.getCredentials();\n                    Set<Permission> oldPermissions = old.getPermissions();\n                    Map<String, Object> oldAttributes = old.getAttributes();\n                    SecurityIdentity newIdentity = new SecurityIdentity() {\n                        @Override\n                        public Principal getPrincipal() {\n                            return modified.getUserPrincipal();\n                        }\n\n                        @Override\n                        public boolean isAnonymous() {\n                            return modified.getUserPrincipal() == null;\n                        }\n\n                        @Override\n                        public Set<String> getRoles() {\n                            throw new UnsupportedOperationException(\n                                    \"retrieving all roles not supported when JAX-RS security context has been replaced\");\n                        }\n\n                        @Override\n                        public boolean hasRole(String role) {\n                            return modified.isUserInRole(role);\n                        }\n\n                        @SuppressWarnings(\"unchecked\")\n                        @Override\n                        public <T extends Credential> T getCredential(Class<T> credentialType) {\n                            for (Credential cred : getCredentials()) {\n                                if (credentialType.isAssignableFrom(cred.getClass())) {\n                                    return (T) cred;\n                                }\n                            }\n                            return null;\n                        }","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java#L58-L94","documentation":"This UnsupportedOperationException is thrown by the synthetic SecurityContext that Quarkus REST (RESTEasy Reactive) installs when application code replaced the JAX-RS SecurityContext via a filter (SecurityContextOverrideHandler). Because the overriding code fully controls identity/role checks, the framework cannot enumerate all roles, so getRoles() is intentionally unsupported. Only hasRole(String) delegation to the replaced context is provided.","triggerScenarios":"Calling SecurityContext.getRoles() (directly or via @RolesAllowed internal role gathering / SecurityIdentity.getRoles()) after a ContainerRequestFilter has called ContainerRequestContext.setSecurityContext() with a custom SecurityContext whose getRoles is not backed by a real role store.","commonSituations":"Custom auth filters that wrap or replace the security context; migrating code from classic RESTEasy where getRoles() worked; using quarkus-security APIs (e.g. isUserInRole-based annotations are fine, but role enumeration or programmatic SecurityIdentity.getRoles() fails); testing security filters that swap contexts.","solutions":["Do not call getRoles() on the replaced SecurityContext; use hasRole(\"role\") for individual checks instead of enumerating roles","If you need role enumeration, have your custom SecurityContext implement getRoles() yourself before installing it via setSecurityContext()","Keep Quarkus-managed SecurityIdentity intact: use IdentityProvider / SecurityIdentityAugmentor to add roles instead of replacing the SecurityContext","If you only replaced the context for a different principal, consider augmenting the existing SecurityIdentity rather than overriding the whole SecurityContext"],"exampleFix":"// before\nSecurityContext sc = ctx.getSecurityContext();\nSet<String> roles = sc.getRoles(); // throws UnsupportedOperationException\n// after\nboolean isAdmin = ctx.getSecurityContext().isUserInRole(\"admin\");\n// or better: augment identity instead of overriding context\npublic class RolesAugmentor implements SecurityIdentityAugmentor {\n    public Uni<SecurityIdentity> augment(SecurityIdentity identity, SecurityIdentityAugmentationContext c) {\n        return Uni.createFrom().item(identity); // add roles here\n    }\n}","handlingStrategy":"fallback","validationCode":"SecurityContext sc = ctx.getSecurityContext();\nSet<String> roles;\ntry {\n    roles = sc.getRoles();\n} catch (UnsupportedOperationException e) {\n    roles = Set.of(); // or derive from SecurityIdentity augmentors\n}\nif (roles.isEmpty() && sc.getUserPrincipal() != null) {\n    // use sc.isUserInRole(role) per expected role instead\n}","typeGuard":"boolean supportsGetRoles(SecurityContext sc) {\n    try { sc.getRoles(); return true; } catch (UnsupportedOperationException e) { return false; }\n}","tryCatchPattern":"try {\n    Set<String> roles = securityContext.getRoles();\n} catch (UnsupportedOperationException e) {\n    // fall back to isUserInRole checks or SecurityIdentity.getRoles()\n}","preventionTips":["Prefer SecurityIdentityAugmentor over replacing the SecurityContext via setSecurityContext()","Use isUserInRole()/hasRole() instead of enumerating all roles","In tests, assert filters do not override the context unless role enumeration is not needed","Document on custom SecurityContext implementations whether getRoles() is supported"],"tags":["jaxrs","security","unsupported-operation","resteasy-reactive"],"backgroundTag":"security-context-roles-unsupported","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"}