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
.