120 lines
No EOL
4 KiB
TypeScript
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}</>;
|
|
}; |