That’s to be expected. If you want the user to be redirected do the session tracking yourself and e.g. redirect them with a heartbeat listener where you check the inactivity and respond accordingly where you still have a http request / response.
There is also a OIDC logout mechanism that does not need the client - this could also work. Then you just redirect the user before session timeout to a public page of your choice
Thanks Christian, just to confirm you suggest that I implement the session tracking logic myself and not use the build-in Spring Security one?
I am surprised that there is no “standard” - more common approach to handle what I am trying to do. Is it that niche of a use-case that Java/SpringSecurity/Vaadin does not have a build-in mechanism that will Invalidate the HTTP session when the user is inactive for a set time and I have to implement custom logic?
Also, can someone share am example of what that user session tracking could look like?
There is a misunderstanding - all those frameworks have working solutions for their specific use-case. But your requirement does not fit into a server side / async model.
Once Spring or Vaadin notify you about their session end, there is no active HTTP communication with the client to allow them to reroute the user. That’s just how web application work.
So you either go the route to track the user inactivity in e.g. a heartbeat listener of Vaadin and redirect them on their last heartbeat before the session ends - or -
You combine Vaadin’s session timeout url from the system messages to redirect a user to a static page once their come back and do Backchannel logout in the http session end listener (without user interaction)
Both comes with different ups/downs and it’s up to you to decide what you really want and want to implement and maintain.
Thanks Christian, appreciate your comment. Here’s what I did in the end.
I added a Logout endpoint:
@Controller
public class LogoutController {
@Value("${okta.oauth2.issuer}")
private String oktaDomain;
@Value("${okta.post.logout.redirect.uri}")
private String oktaLogoutUrl;
@GetMapping("/logout")
public String logout(HttpServletRequest request, HttpServletResponse response) {
var idTokenObj = request.getSession().getAttribute("idToken");
if (idTokenObj == null) {
// Redirect to OKTA logout endpoint
return "redirect:/envdata";
}
var idToken = idTokenObj.toString();
// Invalidate the HTTP session
request.getSession().invalidate();
// Clear the security context
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
new SecurityContextLogoutHandler().logout(request, response, auth);
}
String logoutUrl = oktaDomain + "/v1/logout" +
"?id_token_hint=" + URLEncoder.encode(idToken, StandardCharsets.UTF_8) +
"&post_logout_redirect_uri=" + oktaLogoutUrl;
// Redirect to OKTA logout endpoint
return "redirect:" + logoutUrl;
}
}
Changed my SessionExpiredMessageInitServiceListener class to redirect to that endpoint - /logout after the user clicks on the “Session Expired” dialog (or presses ESC):
@Component
public class SessionExpiredMessageInitServiceListener implements VaadinServiceInitListener {
@Override
public void serviceInit(ServiceInitEvent event) {
event.getSource().setSystemMessagesProvider(new SystemMessagesProvider() {
@Override
public CustomizedSystemMessages getSystemMessages(SystemMessagesInfo systemMessagesInfo) {
CustomizedSystemMessages messages = new CustomizedSystemMessages();
messages.setSessionExpiredCaption("Session expired");
messages.setSessionExpiredMessage(
"Your session has expired. Press ESC or click anywhere in this window to continue."
);
// If you have a static page or route for session-expired:
messages.setSessionExpiredURL("/logout");
messages.setSessionExpiredNotificationEnabled(true);
return messages;
}
});
}
}
With the above 2 additional changes the HTTP session is successfully invalidated and and the user is required to re-login using OKTA and JSESSIONID cookie is refreshed as I wanted.