No Vaadin session after successful login

My Vaadin 24.2 Spring Boot app should display a view after login that depends on the role of the logged in user. The dynamic registration of a route in the spring security authSuccessHandler fails because no Vaadin session exists at this time and therefore the call to RouteConfiguration.forSessionScope().setRoute("", RoleXYZView.class);
fails with Null session is not supported for session route registry

I also cannot register the route in the VaadinServiceInitListener / sessionInit because this method is called BEFORE the user is authenticated, e.g. when the first page, the login page, is started by the browser. If the subsequent login request is then handled by a different thread than the one that created the session, then despite the previous session init (in thread 1) there is no Vaadin session in the authSuccessHandler (thread 2) and no route can be registered in the sessionScope.

The logs demonstrate the case:

[http-nio-8080-exec-2] DEBUG d.h.h.z.a.ApplicationServiceInitListener - ### new session 
[http-nio-8080-exec-2] DEBUG d.h.h.z.a.ApplicationServiceInitListener - ### no auth info available
[http-nio-8080-exec-10] DEBUG d.h.h.z.s.SecurityConfiguration - Successful login
[http-nio-8080-exec-10] DEBUG d.h.h.z.s.SecurityConfiguration - ###  session is null

The Vaadin docs in https://vaadin.com/docs/latest/routing/dynamic shows HOW the registration is made, but not WHEN … at least if Spring Security is used.

So how can dynamically register a route after the user is authenticated in a spring boot / spring security application ?

I implement a workaround using a view that extends from RouteNotFoundError but in my eyes, this seems to be a bigger problem: why a session XYZ always created within thread A isn’t available within Thread B ? The JSESSIONID cookie is send properly so whatever thread the request serves, the vaadin session should be available.

My recommendation is to perform dynamic registration of routes in your MainLayout bookstore-flow-ee/bookstore-starter-flow-ui/src/main/java/com/vaadin/samples/MainLayout.java at v24 · TatuLund/bookstore-flow-ee · GitHub

This happens both after user being logged in and session being initialized.

Hey @yummy-rhino thank you for your response. Unfortunately, this approach doesn’t work in my case due to the following reasons:

  • in my app, the default route is not accessible for certain user roles so therefore I want to redirect this roles to another route/view
  • MainLayout.onAttach is therefor never called due to the NoRouteFound error happens before the main layout is attached

But with @quirky-zebra 's help we’ve got a solution for the auth success handler:
Get the vaadin session via the http request, build the dynamic route config with a manual created session registry, all while locked the vaadin session.

public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {  

Optional<VaadinSession> vaadinSession = VaadinSession.getAllSessions(request.getSession()).stream().findFirst();
if (vaadinSession.isPresent()) {
    vaadinSession.get().lock();
    RouteRegistry sessionRouteRegistry = SessionRouteRegistry.getSessionRegistry(vaadinSession.get());
    RouteConfiguration.forRegistry(sessionRouteRegistry).setRoute("", AboutAdminView.class, MainLayout.class);
    vaadinSession.get().unlock();
}

This approch has one small pitfall: as long as we want to use our own custom authSuccessHandler, we have to re-use the one created by Vaadin called VaadinSavedRequestAwareAuthenticationSuccessHandler. This one is created by the parent class of our SecurityConfig, the com.vaadin.flow.spring.security.VaadinWebSecurity but by a private method getVaadinSavedRequestAwareAuthenticationSuccessHandler which is not accessible by our inherited class. Therefor I’ve filed a issue https://github.com/vaadin/flow/issues/18071 in order to made this method at least protected and therfore accessible by a derived class.

You should always call unlock in a finally block

Hehe, I told him the same thing in the german chat :grin:

hey guys, mission accomplished:

if (vaadinSession.isPresent()) {
    try {
        vaadinSession.get().lock();
        RouteRegistry sessionRouteRegistry = SessionRouteRegistry.getSessionRegistry(vaadinSession.get());
        RouteConfiguration.forRegistry(sessionRouteRegistry).setRoute("", AboutAdminView.class, MainLayout.class);
    } catch(Exception ex) {
        log.error("Fehler bei der dyn. Routen Definition", ex);
    } finally {
        vaadinSession.get().unlock();
    }
}