Hello,
We have hit a WebDAV problem on Jmix 3.0.1 that we cannot work around ourselves, and
we would be grateful for your guidance.
When a user opens a WebDAV document from Microsoft Word, it never opens. The server
sends a Digest challenge, Word answers it, and Spring Security logs the authentication
as successful. But the authorization check that runs immediately afterwards - same
thread, same request, about a millisecond later - sees an AnonymousAuthenticationToken
instead of the user. AuthorizationFilter denies the request, ExceptionTranslationFilter
calls the entry point again, a new challenge goes out, Word answers that one too, and
the same thing happens again. It loops forever, and Word eventually shows “Access Denied”.
The important part is this: the same application code, the same configuration, the same
certificate and the same Microsoft Office installation work perfectly on Jmix 1.x and on
Jmix 2.x. Only Jmix 3.0.1 fails.
That comparison rules out most of the obvious explanations. Jmix 2 is already flowui, so
the UI framework is not the difference. Jmix 2 is already on Spring Security 6, where
SecurityContextHolderFilter and the explicit SecurityContextRepository save model were
introduced, and digest works there - so that change is not the difference either. What
is left between our last working version and the first broken one is the Spring Boot 3
to 4 / Spring Security 6 to 7 step, or a change in how the Jmix 3 WebDAV starter builds
its filter chain.
Here is what the log shows. First, authentication succeeding:
DEBUG [nio-8443-exec-4] o.s.s.w.a.w.DigestAuthenticationFilter
: Digest Authorization header received from user agent:
Digest username="admin",realm="webdav",...
DEBUG [nio-8443-exec-4] o.s.s.w.a.w.DigestAuthenticationFilter
: Extracted username: 'admin'; realm: 'webdav'; ...
DEBUG [nio-8443-exec-4] o.s.s.w.a.w.DigestAuthenticationFilter
: Authentication success for user: 'admin'
with response: '9b624b9cf6af37eb3b391703a49f74c9'
Then, on the same thread and the same request:
TRACE [nio-8443-exec-4] RequestMatcherDelegatingAuthorizationManager
: Authorizing OPTIONS /webdav/document/<document-id>/
TRACE [nio-8443-exec-4] RequestMatcherDelegatingAuthorizationManager
: Checking authorization on OPTIONS /webdav/document/<document-id>/
using AuthenticatedAuthorizationManager@2cb4e04
TRACE [nio-8443-exec-4] o.s.s.w.a.ExceptionTranslationFilter
: Sending AnonymousAuthenticationToken [Principal=anonymousUser,
Credentials=[PROTECTED], Authenticated=true,
Details=WebAuthenticationDetails [...],
Granted Authorities=[ROLE_ANONYMOUS]] to authentication entry point
since access is denied
org.springframework.security.authorization.AuthorizationDeniedException: Access Denied
at org.springframework.security.web.access.intercept.AuthorizationFilter
.doFilter(AuthorizationFilter.java:99) ~[spring-security-web-7.0.6.jar:7.0.6]
And then a fresh challenge, which starts the loop again:
DEBUG [nio-8443-exec-4] s.s.w.a.w.DigestAuthenticationEntryPoint
: WWW-Authenticate header sent to user agent: Digest realm="webdav", ...
The filter order for that request, taken from the stack trace, outermost first, is:
SecurityContextHolderFilter -> ... -> LogoutFilter -> DigestAuthenticationFilter
-> RequestCacheAwareFilter -> SecurityContextHolderAwareRequestFilter
-> AnonymousAuthenticationFilter -> ExceptionTranslationFilter -> AuthorizationFilter
AnonymousAuthenticationFilter only populates the anonymous token when the context is
empty, so it looks as though the principal set by DigestAuthenticationFilter is not
visible to the filters that come after it.
Our configuration is:
jmix.webdav.url-prefix=https://<host>:8443/webdav
jmix.webdav.authentication-method=digest
jmix.webdav.send-document-events=true
We use jmix-webdav-starter, jmix-webdav-flowui-starter and jmix-webdav-rest-starter, on
JDK 21 with embedded Tomcat 11.0.22, HTTPS on port 8443, with a certificate whose CN and
SAN match the host in url-prefix. Passwords are {noop} for the seeded admin account and
BCrypt for users created through the UI - exactly as in our Jmix 1.x and 2.x applications
that work.
We have already ruled out the following. The URL and certificate are correct, and a
browser reaches the same URL and is challenged normally. The credentials are correct -
the log says “Authentication success”. Password storage is not the issue, since the same
storage works on 1.x and 2.x. Jmix roles are not the issue, because the denial comes from
AuthenticatedAuthorizationManager, which only asks whether anyone is authenticated at
all, before any resource role is consulted. And the client is not the issue, because the
same Office installation on the same workstation opens documents from our Jmix 1.x and
2.x applications.
We would like to ask three things.
First, is this a known regression in the Jmix 3 WebDAV starter, and is a fix planned?
Second, is there a supported way to make the authentication set by
DigestAuthenticationFilter survive to AuthorizationFilter - for example by contributing
our own SecurityFilterChain for /webdav/** with an explicit SecurityContextRepository?
If that is a sanctioned workaround we would much rather apply it than wait for a release.
But this application is a GxP-regulated quality management system, and we are not willing
to hand-roll security configuration without your confirmation that it is the right
approach.
Third, if digest is simply no longer supported on Jmix 3, could the add-on documentation
say so? Basic is not a usable alternative for us. Microsoft 365 Apps block Basic
authentication sign-in prompts by default, and the only remedy on the client side is a
per-workstation registry or Group Policy change, which our regulated customers will not
accept.
We are happy to send the full trace log, put together a minimal reproduction project, or
join a screen-share, whichever is most useful to you.
Thank you for your help.