Way to detect other browser tabs on same app before close

Hi there,

I’m trying to implement a feature in our app that the client has requested. Basically for non-admin users, they’d like it so that when the browser or tab is closed the user gets logged out.

This works fine using the window.onBeforeUnload event, however it’s problematic when a user has multiple tabs open for the app and they close one (but have others open). This is logging them out, which obviously isn’t ideal.

As I understand it, there is no native way of determining active tabs that I could hook into before firing the event and I don’t believe anything under the Meteor global namespace can help me.

I was just wondering if anyone had any ideas on how they’d handle it? Local storage? Or am I going down a slippery slope here?

Thanks for any thoughts/ideas!

window.onBeforeUnload do a cleanup of the local storage. With no Meteor.loginToken in the local storage, the user is considered not logged in (in all tabs)

I think using local storage to count the openning tabs is a solution.
Every tap openned, you increase the counter, when a tab closed, you decrease the couter and check if there’s other tab(s) openning then decide to logout user.

Thanks, that sounds promising, but presumably by this point any code I execute won’t run as the tab will already have been closed? Or do I need to initiate the “Are you sure?” dialog window? Thanks

Thanks, I was originally thinking along similar lines but not sure how reliable it will be and whether every new tab could be reliably detected and would stay in sync. Have read into the Broadcast API which might be promising but not quite there yet

This is starting to feel like it goes too much against the grain. I may suggest to client that we implement a “logout after inactivity” functionality instead

You should be ok to just clear out the token or all keys in the local storage and the log out will be browser-wide (all tabs)

“The storage is bound to the origin (domain/protocol/port triplet).”

From accounts-base Meteor package:


 _unstoreLoginToken() {
    Meteor._localStorage.removeItem(this.USER_ID_KEY);
    Meteor._localStorage.removeItem(this.LOGIN_TOKEN_KEY);
    Meteor._localStorage.removeItem(this.LOGIN_TOKEN_EXPIRES_KEY);

    // to ensure that the localstorage poller doesn't end up trying to
    // connect a second time
    this._lastLoginTokenWhenPolled = null;
  };


makeClientLoggedOut() {
    // Ensure client was successfully logged in before running logout hooks.
    if (this.connection._userId) {
      this._onLogoutHook.each(callback => {
        callback();
        return true;
      });
    }
    this._unstoreLoginToken();
    this.connection.setUserId(null);
    this._reconnectStopper && this._reconnectStopper.stop();
  }


Your goals are not the same

Yes, thanks @paulishca it’s cool to know it’s possible to logout a user from the server-side but my aim here was to somehow only logout the user on the client side if no other tabs were open.

Having read lots about this on MDN and across the internet it seems like any solution would be quite flaky so think I will go with a client-side inactivity detection feature and then logout users based on that.

Ok, I re-red everything and I think I (only :)) now understand your situation.
What @minhna said seems ok. On load of tab you increment, on leave (onBeforeUnload) you decrement and if 1, just clear the whole localStorage.

I see there is also a package that might help for this: multi-tab-detection - npm

1 Like

Thanks, my main concern with that approach would be the count getting out of sync but this package looks useful, thanks!

Any time a tab crashes your count will go out of sync.

If you don’t mind making a small change to the accounts-base package you could swap out localStorage for sessionStorage. That should give you the desired result.

2 Likes

This is what I was going to suggest.

From what I can see in the MDN Window.sessionStorage documentation, I doubt it would provide the desired result. It states that:

  • Whenever a document is loaded in a particular tab in the browser, a unique page session gets created and assigned to that particular tab. That page session is valid only for that particular tab.

I noticed that yesterday as well :frowning: my understanding of it was wrong. Seems like a weird gap though, persistent storage or single tab storage only. It does work great for some types of login though !

Am I missing something here using session storage?

User opens tab A and login.
User opens tab B and login.

User logs out of tab B.
User is still logged in at tab A.

That is the expected behavior, right?

That is the usecase I want session storage for, but I think not what andregoldstein wants it for, which is to log the user out when the last tab they have open for a specific resume token is closed. This is more similar to how session base cookies work.

One option could be to use a session cookie to store the resume token. There are a lot of caveats around cookies, and you’d need to check that session based cookies persist across tabs as I think they do. Making a cookie readable by js isn’t best practice, but has a similar security impact to local storage. You could in theory manage without making it readable in js but the experience wouldn’t be as seamless

1 Like

As I understand the OP, he wants to logout the user when he closes the tab but do not logout other users logged in on other tabs.

This works fine using the window.onBeforeUnload event, however it’s problematic when a user has multiple tabs open for the app and they close one (but have others open). This is logging them out, which obviously isn’t ideal.

From this, I read they want to log out the user, when they closed their last tab - rather than either leaving them logged in (the default for meteor) or logging them out when any tab is closed (which the onBeforeUnload would give)

Maybe something related:

  1. Package that I did previously use to disconnect non-active tabs:
  1. About navigating away from current page: