Extend Session and Redirect to Login When Expired

I was able to implement session expiration after inactivity as described in this topic.

However, the session expiration is not made apparent to the User until they attempt to perform some action. I have the requirement to alert the User that their session is about to expire at least 20 seconds prior to it happening and give them the opportunity to extend it. If they do not extend the session, I need to send them back to the login screen as soon as the session time limit is reached.

I’ve been doing a lot of research on this topic but have yet to find a valid solution. I attempted to implement the UserInactivity Vaadin add-on, but found it introduced errors when opening a new browser tab after closing one.

I also considered using a Timer on the MainWindow to check the UserSessionEntity’s LastUsedTs value and compare it against the current timestamp, but calls to the UserSessionService cause that value to be updated.

With either of these approaches, I still wasn’t sure how I would even handle redirection to the login page.

Does anyone have any experience implementing this type of functionality?

Hi!

There is an example of such notification of expired sessions: https://github.com/cuba-labs/session-expiration-notification

AppExpirationWatcher stores active VaadinSession objects and check if there are expiring sessions: https://github.com/cuba-labs/session-expiration-notification/blob/master/modules/web/src/com/company/demo/web/AppExpirationWatcher.java#L28

If session is about to expire it shows notification:

for (VaadinSession session : activeSessions) {
    // obtain lock on session state
    session.accessSynchronously(() -> {
        if (session.getState() == VaadinSession.State.OPEN) {
            // active app in this session
            App app = App.getInstance();

            // user is logged in and session is expiring
            if (app.getConnection().isAuthenticated()
                    && expiringSessions.contains(app.getConnection().getSessionNN().getId())) {

                // notify all opened web browser tabs
                List<AppUI> appUIs = app.getAppUIs();
                for (AppUI ui : appUIs) {
                    if (!ui.isClosing()) {
                        // work in context of UI
                        ui.accessSynchronously(() -> {
                            new Notification("Your session is about to be closed", Type.TRAY_NOTIFICATION)
                                    .show(ui.getPage());
                        });
                    }
                }
            }
        } else {
            closedSessions.add(session);
        }
    });
}

List of expiring sessions is obtained from the middleware using system session:

// obtain system session with all permissions
// check session timeout on behalf of this session
// it is required due to session prolongation on request
UserSession systemSession;
try {
    systemSession = trustedClientService.getSystemSession(webAuthConfig.getTrustedClientPassword());
} catch (LoginException e) {
    log.error("Unable to get system session");
    return;
}

List<UUID> expiringSessions;

AppContext.setSecurityContext(new SecurityContext(systemSession));
try {
    expiringSessions = sessionExpirationService.getExpiringSessions();
} finally {
    AppContext.setSecurityContext(null);
}

Thus, session access times will not be touched.

Hi Yuriy,
Thank you so much for the information! This looks very promising. I’ll give it a shot as soon as I can and let you know how it goes.

Hi @amandaros

I was wondering how you go on with the Expiring Sessions as I was looking to do something similar.

If you managed to get it all done is there a chance you can share your code?

Hi,

Since 6.9 (and 6.8.9 version) you will be able to use cuba.web.closeIdleHttpSessions application property. See https://github.com/cuba-platform/cuba/issues/857

If it is enabled Web Client closes the UIs and the session after the cuba.httpSessionExpirationTimeoutSec expires after the last non-heartbeat request.

1 Like

Hi @JohnM,
No, unfortunately this change was put on hold due to shifting project priorities. I hope to get back to it soon, at which point I will post an update.

Hi @artamonov,
That is nice, I wish I were able to upgrade to one of those versions, since it seems so much easier to do what I need to do. I had to turn off heartbeat completely just to get my sessions to actually expire in version 6.6.4 (see Session Expiration Not Working).

Hi @artamonov,
It appears that you are using a newer version of the framework than I have. We are using 6.6.4, and it does not have the TrustedClientService. Is there a similar service in this version of CUBA that I should be using instead?

You can use LoginService#loginTrusted() instead.

@artamonov,
Thanks for the info! I used loginService#getSystemSession(), which seemed like a closer match to the TrustedClientService method used in the example code. I also had to recreate UserSessionsAPI#getUserSessionsStream(), which does not exist in this version either.

Unfortunately, I’ve run into another issue: AppStartedEvent is not available in 6.6.4. Do you know of an alternative approach that I can use in order to implement the functionality of AppExpirationWatcher#onAppStart()?

Unfortunately, there is no such functionality in 6.6. Events have been introduced later.

@artamonov,
That’s what I was afraid of… is there no other way to trigger the init functionality when the app starts up? Maybe overriding an OOB class to call the function explicitly instead of relying on events?

I’m operating under a very tight deadline, and my company purchased some support hours after I setup this topic, so I’m going to utilize some of those hours for this topic. Thanks for your help thus far!

Sure, feel free to use consultancy hours. Just create a topic in your dedicated support category