All files / src/features/auth/guards RequireAuth.tsx

100% Statements 87/87
100% Branches 13/13
100% Functions 2/2
100% Lines 87/87

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 881x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7x 7x 7x 7x 7x 5x 5x 7x 1x 1x 4x 4x 7x 1x 1x 3x 3x 7x 1x 1x 2x 2x 2x 2x 1x 1x 1x  
import * as React from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import { Box, CircularProgress } from '@mui/material';
import { useAuth } from '../../../hooks/useAuth';
 
/**
 * @file RequireAuth.tsx
 * @description
 * Authorization gate for routed pages.
 * Ensures only authenticated users can access protected routes.
 * Redirects unauthenticated users to the login page.
 *
 * @enterprise
 * - If `allowDemo` is true, DEMO users may access the route (read-only areas).
 * - Otherwise, DEMO sessions are redirected to /analytics/overview.
 * - Accepts a `fallback` prop to customize the loading indicator while auth state is being determined.
 */
type Props = {
  children: React.ReactElement;
  fallback?: React.ReactNode;
  allowDemo?: boolean;
};
 
/**
 * 
 * @returns React.ReactElement
 * @description
 * Default loading indicator while auth state is being determined.
 * Centered circular progress indicator.
 * @private
 * @enterprise
 * - Consistent styling with MUI components
 * - Minimal and non-blocking UI
 * - Used when no custom `fallback` is provided 
 * @example
 * ```tsx
 * <RequireAuth>
 *  <ProtectedPage />
 * </RequireAuth>
 * ```
 */
const DefaultLoading = () => (
  <Box sx={{ display: 'grid', placeItems: 'center', minHeight: 160 }}>
    <CircularProgress size={22} />
  </Box>
);
 
/**
 * @component
 * @description
 * Authorization gate for routed pages.
 * Ensures only authenticated users can access protected routes.
 * Redirects unauthenticated users to the login page.
 * 
 * @enterprise
 * - If `allowDemo` is true, DEMO users may access the route (read-only areas).
 * - Otherwise, DEMO sessions are redirected to /dashboard.
 * - Accepts a `fallback` prop to customize the loading indicator while auth state is being determined.
 */
const RequireAuth: React.FC<Props> = ({ children, fallback, allowDemo }) => {
  const { user, loading, logoutInProgress } = useAuth();
  const location = useLocation();
 
  // Show loading indicator while auth state is being determined
  if (loading) return (fallback ?? <DefaultLoading />);
 
  // If a logout was just triggered, hold the guard to avoid flashing the login page
  if (!user && logoutInProgress) {
    return (fallback ?? <DefaultLoading />);
  }
 
  // Redirect unauthenticated users to login
  if (!user) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }
 
  // Redirect DEMO users if not allowed
  if (user.isDemo && !allowDemo) {
    return <Navigate to="/dashboard" replace />;
  }
 
  // Allow access to authorized users
  return children;
};
 
export { RequireAuth };
export default RequireAuth;