Sounds good. Reta is pretty smart. It's been a while since I've had my head in the CXF codebase, but if AbstractHTTPDestination is created once per endpoint, then caching the Principal isn't going to work.
It does also cache HttpServletRequest, which on its face isn't valid unless it's a proxy that uses a thread local to resolve the actual HttpServletRequest. I seem to recall this is the case so @Context injection works. I'm not sure that same thing is true for Principal. If Principal can be injected via @Context and has similar thread-local-lookup. If both HttpServletRequest and Principal are both simply proxies that delegate to a thread-local, it could be fine provided the logging is tolerant of the scenario where there is no HTTP request. Course all that is just a guess -- could be off in left field. -David > On Feb 17, 2026, at 11:20 AM, Richard Zowalla <[email protected]> wrote: > > Thanks for your answer, David! > > Actually, the commit in CXF is this one: > https://github.com/apache/cxf/pull/2807/changes#diff-eddf423fac99200bd14e115467dc35809e1855cefdf3bae77f9394b8c050fc94L419 > with a totally different message "CXF-9116: Remove > org.apache.cxf.feature.LoggingFeature“ - don’t think removing logging should > lead to a behavior change. Reta from CXF did comment in his PR for the > logging feature removal for this change. The following > > "some refactoring here: a number of cxf-*-security tests were failing due to > logging change: apparently when DefaultLogEventMapper is called, the > underlying HTTP request is already recycled and fails with NPE > (https://github.com/jetty/jetty.project/issues/12080 ). To mitigate that, at > least for getUserPrincipal() - capturing principal early.“ > > So I think the change was introduced because of test failures in Jetty - I > will leave a ping to our thread there, so we might get some thoughts by Reta > :) > > Gruß > Richard > > >> Am 17.02.2026 um 18:52 schrieb David Blevins <[email protected]>: >> >> Thanks for all this detail. I don't recall the default in MP JWT off the >> top of my head. Would need to consult the spec, possibly TCK if the spec >> didn't have a clear answer. Note if the spec isn't clear, let me know and >> I'll update it once we find the answer. >> >> I can confirm that calling and caching getPrincipal before an HTTP request >> is read is not valid for any HTTP-based security mechanism; basic auth, >> digest auth, jwt, http session based, soap security, etc, etc. It rules out >> everything except SSL/TLS certificate-based client authentication, which is >> connection based. >> >> Even if they waited till the HTTP message was read and cached for the >> duration of just the request, that's still not really possible because of >> these methods: >> >> - HttpServletRequest.login(String username, String password); >> - HttpServletRequest.logout(); >> - HttpServletRequest.authenticate(HttpServletResponse response); >> >> What they actually do, if anything at all, is completely dependent on the >> underlying security framework. As is the concept of how long a security >> identity lasts during a request. >> >> Sounds like a situation where someone is using security framework X which is >> slow, so some well-intentioned code got added that ultimately isn't valid >> outside of that exact user's scenario. >> >> Here's how I would approach resolving this: >> >> - Find the commit that added the caching >> - The commit message might tell on itself (x system is slow so let's cache) >> - Figure out how they imagined the caching would work: when things are >> removed, added, and in what scope they imagined the cached value was valid >> (request, session, connection, globally). >> >> - Analyze a stack trace of the first getPrincipal that's cached. Figure out >> how we possibly got there as it's not valid. Even if caching is removed, >> you still can't call getPrincipal without an HTTP request, so this has to be >> solved and fixed. >> >> >> -David >> >>> On Feb 17, 2026, at 6:18 AM, Richard Zowalla <[email protected]> wrote: >>> >>> I believe the actual issue is related to the caching mechanism in CXF's >>> updated code. Due to this caching, the token validation now happens as soon >>> as getPrincipal() is called, which occurs much earlier in CXF 4.2.0 than in >>> previous versions. >>> >>> As a result, our current TomEE code fails because at that point in the >>> request lifecycle, no Authorization header may be present (e.g., for >>> @PermitAll endpoints), and the token validation simply fails. >>> >>> Additionally, I couldn't find clear guidance in the MP JWT specification >>> regarding the default behavior for non-annotated JAX-RS methods, i.e., >>> methods without @RolesAllowed, @PermitAll, or @DenyAll. >>> >>> It appears this may be vendor-specific. Should the default be: >>> • @PermitAll (allow unauthenticated access)? >>> • @DenyAll (require authentication)? >>> • Something else? >>> >>> Gruß >>> Richard >>> >>> >>> >>>> Am 17.02.2026 um 14:54 schrieb Richard Zowalla <[email protected]>: >>>> >>>> Hi all, >>>> >>>> Due to a change in CXF 4.2.0 [1], specifically caching the principal >>>> instead of looking it up on demand, we are seeing several test failures in >>>> the MP JWT area - I am now wondering if this a thing CXF needs to address >>>> or if we need to update our integration. For this reason and after some >>>> debugging, I would like to get some additional context from the past >>>> regarding the architecture of our CXF/MP JWT integration :) >>>> >>>> The failure in question can be reproduced by running >>>> OrderTest#shouldBeRunning() from the main branch in >>>> examples/mp-rest-jwt-principal, or by executing parts of the MP JWT TCK, >>>> which are also failing. >>>> >>>> The test calls an endpoint that, according to the test assumptions, >>>> shouldn’t be protected. It passes on TomEE 10 / CXF 4.1.5 (which does not >>>> cache the principal). The relevant code on our side is mainly in >>>> MPJWTFilter, especially the MPJWTServletRequestWrapper and its use of >>>> validate(…). >>>> >>>> Since I’m not an active MP JWT user, I have some (maybe dumb?) questions: >>>> >>>> According to [2], an application annotated with @LoginConfig(authMethod = >>>> "MP-JWT") requires MP-JWT Access Control (potentially for all endpoints?). >>>> >>>> If that is the case, the status() method of OrderRestin >>>> https://github.com/apache/tomee/blob/main/examples/mp-rest-jwt-principal/src/main/java/org/superbiz/store/rest/OrderRest.java >>>> should require a JWT, no? >>>> >>>> However, we don’t add a token to the REST client in >>>> https://github.com/apache/tomee/blob/main/examples/mp-rest-jwt-principal/src/test/java/org/superbiz/store/OrderRestClient.java#L35, >>>> so the request is sent without a bearer token and fails with a 401. >>>> >>>> This worked on TomEE 10 / CXF 4.1.5 and earlier. >>>> >>>> I suspect some MP JWT TCK failures are caused by the same issue. See the >>>> build here: >>>> https://ci-builds.apache.org/job/TomEe/job/master-build-full/org.apache.tomee$microprofile-jwt-tck/2071/ >>>> >>>> Question: >>>> >>>> What is the expected behavior? From my reading of the spec, it seems this >>>> endpoint shouldn’t be invocable without a valid token? >>>> >>>> Could anyone with more experience in this area (David, JL, anyone?) >>>> provide some insight? >>>> >>>> Thanks and Gruß >>>> Richard >>>> >>>> [1] >>>> https://github.com/apache/cxf/pull/2807/changes#diff-eddf423fac99200bd14e115467dc35809e1855cefdf3bae77f9394b8c050fc94L419 >>>> [2] >>>> https://download.eclipse.org/microprofile/microprofile-jwt-auth-2.1/microprofile-jwt-auth-spec-2.1.html#_marking_a_jax_rs_application_as_requiring_mp_jwt_access_control >>>> >>> >> >> >
