ui-nyla/src/providers/auth/ProtectedRoute.tsx
2025-12-01 17:01:25 +01:00

120 lines
No EOL
4 KiB
TypeScript

import { useMsal } from "@azure/msal-react";
import { Navigate, useLocation } from "react-router-dom";
import { ReactNode, useEffect, useState } from "react";
interface ProtectedRouteProps {
children: ReactNode;
redirectPath?: string;
}
export const ProtectedRoute = ({
children,
redirectPath = "/login"
}: ProtectedRouteProps) => {
const { accounts } = useMsal();
const location = useLocation();
const [isChecking, setIsChecking] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
const checkAuthentication = async () => {
try {
// Check for MSAL authentication
const hasMsalAccount = accounts.length > 0;
// Check for backend authentication via API call
let hasBackendAuth = false;
try {
// Check for authentication authority (httpOnly cookies are handled automatically)
const authAuthority = sessionStorage.getItem('auth_authority');
console.log('🔍 Checking auth authority:', authAuthority);
if (authAuthority) {
hasBackendAuth = true;
console.log('✅ Authenticated with backend (httpOnly cookies), authority:', authAuthority);
} else {
hasBackendAuth = false;
console.log('❌ No authentication authority found');
}
} catch (error) {
console.log('❌ Backend authentication failed:', error);
hasBackendAuth = false;
}
// User is authenticated if either method is valid
const isAuth = hasMsalAccount || hasBackendAuth;
setIsAuthenticated(isAuth);
console.log('🔐 Authentication status:', {
hasMsalAccount,
hasBackendAuth,
isAuthenticated: isAuth,
authAuthority: sessionStorage.getItem('auth_authority')
});
if (hasBackendAuth) {
console.log('✅ Authenticated with backend cookies');
} else if (hasMsalAccount) {
console.log('✅ Authenticated with MSAL');
} else {
console.log('❌ No valid authentication found');
}
} catch (error) {
console.error('❌ Error checking authentication:', error);
setIsAuthenticated(false);
} finally {
setIsChecking(false);
}
};
// Small delay to ensure MSAL is initialized and localStorage is updated
const timer = setTimeout(() => {
checkAuthentication();
}, 200);
return () => clearTimeout(timer);
}, [accounts]);
// Re-check authentication when component mounts or accounts change
// This handles cases where auth_authority is set after initial mount
useEffect(() => {
if (!isChecking) {
// Double-check authentication state periodically when not initially loading
const recheckTimer = setTimeout(() => {
const authAuthority = sessionStorage.getItem('auth_authority');
const hasMsalAccount = accounts.length > 0;
const hasBackendAuth = !!authAuthority;
const isAuth = hasMsalAccount || hasBackendAuth;
// Only update if authentication state actually changed
if (isAuth !== isAuthenticated) {
console.log('🔄 Authentication state changed, updating...', {
previous: isAuthenticated,
current: isAuth,
authAuthority,
hasMsalAccount,
hasBackendAuth
});
setIsAuthenticated(isAuth);
}
}, 300);
return () => clearTimeout(recheckTimer);
}
}, [isChecking, isAuthenticated, accounts]);
// If still checking, show loading
if (isChecking) {
return <div>Checking authentication...</div>;
}
// Check if user is authenticated through either method
if (!isAuthenticated) {
console.log("No valid authentication found, redirecting to login");
return <Navigate to={redirectPath} state={{ from: location }} replace />;
}
console.log("User is authenticated, rendering protected content");
return <>{children}</>;
};