Synchronizing sessions across multiple web apps

Problem

I’m trying to synchronize single sign-on sessions across various apps and browser tabs in my architecture and was having a lot of trouble searching for any solution to this problem. It’s as if Auth0 doesn’t have a solution even in the enterprise plan.

If someone loads up one of the web apps and logs out, how will other open tabs know that the user has logged out on that machine? Am I supposed to either poll Auth0 or poll the local storage?

Solutions

Using checkSession

I was using checkSession to synchronize sessions, but realized it’s only meant to be called when the access token expires. If I called it every 10 seconds or so, it would keep all my browser tabs in sync, but doesn’t give me any information if the token expired or if the user logged out.

LocalStorage

Because I’ve got a micro-app architecture, there’s no way to share that across domains. Although, I could use an iframe with postMessage to share across domains, I don’t know if this is a best-practice or how Auth0 intends for you to do this.

WebSocket

I was hoping Auth0 had a WebSocket solution I could hook into where all clients are able to know if that specific user account has logged in or logged out. Then I thought about it more; this would only allow for syncing logouts because logins require authenticating again. The issue is all browser tabs share the same session for a certain domain, but the WebSocket connection won’t have the same identifiers from each connected client.

Conclusion

From what I’m seeing, it looks like localStorage with an iframe is the only way to do this. Is that really how I should be handling synchronizing sessions? Is it going to be computationally safe to poll localStorage every second or every 5 seconds for changes?

I’m really looking for a best-practice or Auth0-recommended method for doing realtime login synchronization.

2 Likes

A federated logout will clear the session at the authentication server and the IdP:

A session can also be cleared without clearing the IdP by not including the federated query string parameter.

To clear a session you have to redirect to the authZ server (just clearing local storage doesn’t do that).

Does that help?

Hi Jeremy! I appreciate you spending the time to send me the information on logging out; although, I already know how to do that using the Auth0-js client package.

I’m talking about synchronized sessions. You’re in a bunch of browser tabs with one Auth0 login session. When one tab gets logged out via the Auth0 library either through user input or automatically when the token expires. The other browser tabs need to respond to this change immediately.

In the same vein, they also need to know when the browser has been logged in as well. No reason to sit on the login screen in multiple tabs if you already logged in from a different tab.

I’ve since created a solution to handle this using a micro app in an iframe and localStorage (it’s a method of sharing localStorage access among apps, but I still don’t know if I even need to do this at all or if there’s already an Auth0 solution in place to handle it.

Every person I work with understands the problem and finds it hard to believe there’s not already a solution for this problem from Auth0.

Hi there,

There are a few things associated to session management when multiple parties are involved. In most cases this depends on the type of application, for example a logout for an SPA that uses Auth0 issued accessTokens to call an API is very different from a logout for say a classical web application like wordpress which manages its own session.

The proper solution for such scenarios is a back-channel logout where the IdP has specific integration with each relying party or service provider to force revoking the user sessions. We are currently working on this, and the best way to do this on various applications.

At the moment, depending on the type of application you are using and the way the application handles sessions will change how this process is going to work.

For example if you are using an SPA, with a smaller token lifetime (we recommend ~5-10 minutes) logging out of one browser tab will simply logout all other applications the next time they’ll try to refresh token they will be automatically logged out.

The problem here arises from how sessions are represented, since we are issuing JWTs, each JWT has implicit expiry which gives it a trade-off b/w performance as you don’t have to call the IdP to validate the token on each call and certain features.

Your proposed solution will work fine. However, I’m curious how do you detect logout on the micro application?

The second part of detecting if you are logged in can be achieved either using getssodata on the hosted login page or using checkSession to poll in the embedded application. As either will return appropriately as soon as a session is created on the server. Which I think is exactly what you are doing at the moment.

Thinking out loud, this is an ideal case for a shared service worker to maintain session across tabs for purely client side applications etc. Which as I understand is exactly what your iFrame backed solution achieves.

We do understand the issue and working on improving the session story in future, for now I have forwarded this to the product team :slight_smile: .

I was using checkSession to synchronize sessions, but realized it’s only meant to be called when the access token expires. If I called it every 10 seconds or so, it would keep all my browser tabs in sync, but doesn’t give me any information if the token expired or if the user logged out.

checkSession will throw an error if the user is logged out. Token expiry is a purely application side construct and checkSession does not handle that use-case.

2 Likes

Thanks for responding so quickly and sending it over to the product team!

In this use case, all of the micro apps (similar to the micro services concept) are SPAs using username-password authentication. There’s no IdP to worry about. We also use a custom login form instead of the Auth0 Lock widget.

How it Works

Per your question, the prototype I’ve created is pretty simple; although, I’m about to rearchitect it so the iframe app handles Auth0’s checkSession and not each parent micro app, but this is the original working design:

  1. Have the iframe loaded in HTML when the page renders.
  2. The iframe will setup an interval that checks localStorage for the current authentication state (true or false)
  3. Create a message listener in the parent app and specify the correct origin.
  4. Send a message to the iframe asking for the current authentication state. This also sets up event.source.postMessage in the iframe and allows it to send authentication updates.

At this point, nothing’s changing. As soon as you login, this is what happens:

  1. Send isAuthenticated: true over to the iframe.
  2. The iframe receives a state change message and immediately writes it to localStorage.
  3. On each interval, the iframe in each tab is checking for state changes. Because the state changed, a message is sent back to the parent app in each tab with the new authentication state, and they can react accordingly.

If you logout in one tab, it’s the same thing, but you send isAuthenticated: false instead.

This implementation can be expanded to sync timers between tabs for lastUserActionTime if you have a logout timer and want to keep it in sync between tabs. This way you don’t logout users in one tab because they were active in another tab.

I also agree this would be perfect for a Service Worker! Since they require a valid SSL cert (not even self-signed), they’re extremely difficult to use locally especially with a large team. The iframe method is much simpler if not significantly slower in terms of load times :confused:. I haven’t figure out a good solution for this problem, but if Auth0 eventually creates this solution, I won’t have to worry about it in the long term.

Other Notes

I was originally under the impression a small token lifetime was 5-10 seconds, and I used that range for testing my synchronization code originally. After reading the Auth0 docs again, I raised the token lifetime and instead, posted this question. It makes sense to use checkSession when the session expires, just not when synchronizing authentications.

After reading through more docs, I noticed checkSession does silent authentication, but also only tells me if you’re session is expired, not if you’ve logged out. I could check for login_required on the error object, but I’m not certain that solves the other issues I’m having:

  1. Checking your session every 5-10 seconds or less is not the intended use case.
  2. It does a silent authentication and give me a new token each time. This extra functionality isn’t required for synchronization, only token refresh.
  3. I have no way of knowing if the session was logged out already or if it just expired. Only one browser tab should handle token expiry by calling Auth0’s logout function. Other tabs should simply redirect back to the login portal and provide a method of instantly redirecting back when any one of the tabs logs back in.

I was starting to wonder if it’s really important to keep these tabs in sync within 1-10 seconds of each other and came to the conclusion yes, this is still important.

From my perspective, it’s a terrible user experience to be logged into our system of apps and be logged out in one tab, but not in another. When one tab’s logged out but another is still logged in, all-of-a-sudden you might receive a bunch of 401s or stop receiving messages over WebSockets. A user won’t realize what’s going on. If the apps all instantly log you out at the same time, you don’t even need to handle the case where you’re getting multiple 401s in your app as they’ll be non-existent.

It’s possible I’m completely misunderstanding how this works though. It could even be that as soon as a 401 comes in, I’m supposed to do a checkSession and figure out a way to handle it from there. I really don’t know.

I looked into the getSSOData() function, and it looks like checkSession() is the recommended method; although, I could use getSSOData to specifically check only the sso property for true or false like I’m doing now with my iframe solution. Is this method meant to be called ever 1-10 seconds from various browser tabs on the same client?

2 Likes

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.