fix: stop MSAL-triggered navigation to dashboard while on session detail page

Root cause: MSAL fires handleRedirectPromise twice on page load, causing
isAuthenticated to briefly flip false between the two settlements. This
triggered ProtectedRoute to redirect to '/', which then re-authenticated
and redirected to '/dashboard', causing the dashboard page to mount and
unmount periodically while the user was viewing a session.

Two fixes:
1. navigateToLoginRequestUrl: false — prevents MSAL from navigating to
   the stored login request URL after redirect settlement, eliminating
   the spurious navigation that caused the dashboard to mount.
2. ProtectedRoute unauthenticated redirect now waits 500ms before
   executing, giving MSAL time to complete its double-settlement cycle
   before we conclude the user is truly unauthenticated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Oliver Kieselbach
2026-02-27 15:40:32 +01:00
parent 24862ca1de
commit 97ca48f56a
2 changed files with 9 additions and 3 deletions
@@ -26,8 +26,14 @@ export function ProtectedRoute({ children, requireGalacticAdmin = false }: Prote
useEffect(() => {
if (!isLoading && !isAuthenticated && !wasAuthenticated.current) {
// Never been authenticated in this session -> redirect to landing
router.push("/");
// Delay redirect slightly to let MSAL finish its double handleRedirectPromise
// settling cycle before deciding the user is truly unauthenticated.
const t = setTimeout(() => {
if (!wasAuthenticated.current) {
router.push("/");
}
}, 500);
return () => clearTimeout(t);
}
}, [isAuthenticated, isLoading, router]);
@@ -16,7 +16,7 @@ export const msalConfig: Configuration = {
authority: "https://login.microsoftonline.com/organizations", // Multi-tenant
redirectUri: process.env.NEXT_PUBLIC_ENTRA_REDIRECT_URI || "http://localhost:3000",
postLogoutRedirectUri: process.env.NEXT_PUBLIC_ENTRA_POST_LOGOUT_REDIRECT_URI || "http://localhost:3000",
navigateToLoginRequestUrl: true,
navigateToLoginRequestUrl: false,
},
cache: {
cacheLocation: "sessionStorage", // Using sessionStorage for better XSS protection