I am currently facing an issue with my Vaadin PWA. The fallback offline.html page was working perfectly, but ever since I integrated Spring Security, the offline page is no longer displayed when the app loses connection (or when I simulate offline mode in Chrome DevTools).
Here is my current SecurityConfig:
import com.vaadin.flow.spring.security.VaadinSecurityConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;
@EnableWebSecurity
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/public/**",
"/actuator/prometheus",
"/actuator/health",
"/playbook/**",
"/images/**"
).permitAll()
);
// Configure Vaadin's security using VaadinSecurityConfigurer
http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
// This is important to register your login view to the
// navigation access control mechanism:
//configurer.loginView(LoginView.class);
// You can add any possible extra configurations of your own
// here (the following is just an example):
// configurer.enableCsrfConfiguration(false);
});
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers(
"/offline.html",
"/manifest.webmanifest",
"/sw.js",
"/sw.js.map",
"/robots.txt",
"/sitemap.xml"
);
}
}
When I test the application locally and switch the Network tab in the Developer Tools to “Offline”, I can see that the service worker fails to load the following resources:
My question: Am I doing something wrong in my SecurityConfig setup? I assume these missing resources are the reason the offline page fails to render, but I don’t want to generously whitelist entire directories (like /VAADIN/**) or hardcode individual files that are generated dynamically with hashes during the build process.
Is there a best practice on how to properly configure Spring Security so the Vaadin Service Worker can cache these dynamically generated resources for the offline page?
An educated guess: your security configuration doesn’t define a login view, so the configurer never sets up form login. An unauthenticated GET / returns a 403 with an empty body rather than a page. The service worker pre-caches . (the application shell) as one of its manifest entries; that fetch fails, so the whole workbox install aborts, and nothing (including offline.html) ends up in the cache. The /VAADIN/build/*.js errors you see offline are the consequence, not the cause: those URLs are already permitted by VaadinSecurityConfigurer and return 200. Try to pass your Vaadin login view to the configurer (the commented configurer -> configurer.loginView(LoginView.class)) and the pre-cache should fill correctly.
If this does not work, please provide a full example problem that reproduces the problem.
In fact all my pages are currently open to unauthorized users, and I imported the Springboot-Security-Starter dependency with the SecurityConfig when deploying it to the PROD environment, just to have a better feeling about the application’s security.
I will try your implementation hint when working on the next features. Would be great if the offline.html page will be shown again, it’s a great feature of Vaadin.
Do you might have any idea how to get the offline.html displayed again without any LoginView? I’m also not sure if the default security configuration is really necessary, I just thought it would be a good idea, because of the usage of SpringBoot core libraries.
Yes, I mean VaadinViews with my pages. I use the annotation for unauthorised users, everything is working fine, but thanks for you concerns. Sorry I m currently in the gym and cannot lookup the exact annotation names from my repository.
The reason seems to be this. When the connection is lost, Vaadin does not show offline.html directly. It shows the offline page inside an iframe. By default Spring
Security adds the header X-Frame-Options: DENY to the responses, and this header tells
the browser that the page must not be shown inside an iframe. So the browser blocks it,
and instead of your offline page you see a Chrome error page with the text “localhost
refused to connect.” This would explain why it stopped working when you added Spring
Security.
You can fix it with one line in your SecurityConfig:
SAMEORIGIN still stops other web sites from putting your application in a frame, so
this is safe. If you prefer, you can instead add "/offline-stub.html" to your WebSecurityCustomizer list. I tested both and both work.
Short answers to your other questions:
You do not need to allow /VAADIN/** or any file name with a hash in it. Vaadin
already allows those paths.
The errors for /VAADIN/build/*.js, /lumo/lumo.css and /styles.css are not the
cause. They also appear in an application that works and has no Spring Security, so
you can ignore them.
Your entries for /offline.html, /sw.js and /manifest.webmanifest are not needed,
because Vaadin already allows them, but they do no harm.
I created three tickets with all the technical details:
Note that Vaadin 25.2 also started setting X-Frame-Options: SAMEORIGIN by default if nothing else has been set. SAMEORIGIN should work for this case but it might make debugging complicated when the same value might be introduced from multiple sources.