Logout all tabs with msal-react when using localStorage

Viewed 265

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.

1 Answers
  • 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.

Related