1

I have a React 18.x with NextJS 12.x application that uses msal-react 1.4.4 (relies on msal-browser 2.28.0) and Azure B2C for authentication. My config is like this:

export const msalConfig: Configuration = {
  auth: {
    clientId: clientId as string,
    authority: `https://${tenant}.b2clogin.com/${tenant}.onmicrosoft.com/${b2cPolicy}`,
    knownAuthorities: [`${tenant}.b2clogin.com`],
    redirectUri: '/openid-redirect',
    postLogoutRedirectUri: '/',
  },
  cache: {
    cacheLocation: 'localStorage',
  },
};

Pages are protected by a <MsalAuthenticationTemplate interactionType={InteractionType.Redirect} ...> component to enforce login via the redirect flow. This all works fine.

In my navigration bar I have a logout button:

const handleLogout = () => {
  msalInstance.logoutRedirect({
    account: msalInstance.getActiveAccount(),
  });
};
<button onClick={() => handleLogout()}>
  Logout
</button>

And the tab itself gets logged out just fine.

All other tabs loose access to the access_token and other information so they are effectively "logged out" and cannot call API's with bearer authentication anymore. However, the UI of that application does not show the user as logged out.

I had expected all other tabs to notice the logout and update accordingly, possibly redirecting users to another page. Or at the least I would've expected the loggerCallback I've configured to show there's an event or notification that some other tab has logged us out.

If I manually do window.addEventListener('storage', evt => console.log(evt)); I do see the other tabs notice storage is being cleared.

I found another related question which is about cross-device logout, which I expect to rely on the OpenID Session Management spec. I guess that solution could work for me, but the other question nor answer contain a working solution for that either.

The relevant MSDN documentation doesn't mention anything about "multiple tabs" or something similar.

How can I configure my application and msal-react to notice sign outs from other tabs?


Workaround

For now we've used the following workaround:

export function useLogoutInOtherTabsListener() {
  const router = useRouter();

  useEffect(() => {
    const handler = (evt: StorageEvent) => {
      if (evt.key === 'logout-event' && evt.newValue === 'started') {
        msalInstance.logoutRedirect({
          onRedirectNavigate: () => false, // No need to do redirects via msal, we'll just route the user to '/' ourselves
        });
        router.push('/');
      }
    };

    window.addEventListener('storage', handler);
    return () => {
      window.removeEventListener('storage', handler);
    };
  }, [router]);
}
export function logoutAllTabs(): void {
  // We'd prefer to use an msal-mechanism, but so far couldn't find any.
  // See also: https://stackoverflow.com/q/73051848/419956
  window.localStorage.setItem('logout-event', 'started');
  window.localStorage.removeItem('logout-event');
  msalInstance.logoutRedirect();
}

And call useLogoutInOtherTabsListener() in _app.tsx.

Jeroen
  • 60,696
  • 40
  • 206
  • 339
  • Are you able to login on all tabs without problems after logout? – Joost Jan 12 '23 at 14:53
  • Hmm, not sure anymore... haven't touched the relevant code in a few months now. I think the production code related to this SO question found a pragmatic solution, and [my sample](https://github.com/jeroenheijmans/sample-nextjs-with-azure-b2c) or this question I've not updated or tweaked in quite a while. – Jeroen Jan 12 '23 at 20:22

3 Answers3

1
  • Clearing the session ids should be able to log out of all the pages. To do this we can use the concept of the front channel logout .

  • In front channel logout we basically load a different page which will do all the logging out process and clear cache and stop local access to the site. Here the page will be loaded in a hidden iframe and perform only the sign-out operation.

  • But for this to work we have to set system.allowRedirectInIframe to true. Also we have to register logout url in the portal.

enter image description here

const msal = new PublicClientApplication({
    auth: {
        clientId: "my-client-id"
    },
    system: {
        allowRedirectInIframe: true
    }
})

// Automatically on page load
msal.logoutRedirect({
    onRedirectNavigate: () => {
        // Return false to stop navigation after local logout
        return false;
    }
});

Refer this following documentation the above code is from there.

Mohit Ganorkar
  • 1,917
  • 2
  • 6
  • 11
  • Thx for your reply. I can't seem to find the settings from your screenshot yet, but perhaps I should first follow the steps in docs you linked. Seems to require a bit of works still though, with separate pages and whatnot, so for the moment I might stick with the workaround I mentioned... – Jeroen Jul 25 '22 at 11:47
0

As an alternative to your workaround I would like to suggest you to use this library:

https://github.com/pubkey/broadcast-channel.

A BroadcastChannel allows you to send data between different browser-tabs or nodejs-processes.

0

Syncing logged in state across tabs and windows

I don't know if this was available at the time OP posted this but it is now:

if you would like to update your UI when a user logs in or out of your app in a different tab or window you can subscribe to the ACCOUNT_ADDED and ACCOUNT_REMOVED events. The payload will be the AccountInfo object that was added or removed.

These events will not be emitted by default. In order to enable these events you must call the enableAccountStorageEvents API before registering your event callbacks:

msalInstance.enableAccountStorageEvents();
msalInstance.addEventCallback((message: EventMessage) => {
    if (message.eventType === EventType.ACCOUNT_ADDED) {
        // Update UI with new account
    } else if (message.eventType === EventType.ACCOUNT_REMOVED) {
        // Update UI with account logged out
    }
});

You can also disable these events if no longer needed by calling

disableAccountStorageEvents:

msalInstance.disableAccountStorageEvents();

source: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/events.md#syncing-logged-in-state-across-tabs-and-windows

Radu Luncasu
  • 975
  • 10
  • 17