Skip to content
Snippets Groups Projects
PrivateRoute.tsx 1.95 KiB
import React, { useState, useEffect, useContext } from 'react';
import axios from 'axios';
import { Route, Redirect, RouteProps } from 'react-router-dom';
import { NonAuthRoutes } from 'api/routes';
import { AuthContext } from 'components/AuthUser/AuthContext';

/**
 * A wrapper for <Route> that redirects to the login screen if you're not yet authenticated.
 * Every non-public route must be wrapped with this component.
 * */

type Props = {
  Component: React.FC<RouteProps>;
  path: string;
  requiredRoles: string[];
};

/* eslint-disable react/jsx-props-no-spreading */
export const PrivateRoute = ({
  Component,
  path,
  requiredRoles,
}: Props): JSX.Element => {
  const [auth, setAuth] = useState<boolean>(false);
  const [loading, setLoading] = useState<boolean>(false);
  const { role } = useContext(AuthContext);

  useEffect(() => {
    const fetch = async (): Promise<unknown> => {
      const result = await axios('/api/web/login/is_authenticated');
      setAuth(result.data.is_authenticated);
      setLoading(true);
      return null;
    };
    /*
    Check if user is logged in.
    Avoiding this condition would call is\_authenticated every time
    this component state is triggered, falling in unnecessary calls to the
    server.
    */
    if (role) fetch();
  }, [auth]);

  const userHasRequiredRole = requiredRoles.includes(role);
  const message = userHasRequiredRole
    ? 'Please log in to view this page'
    : 'Your role is not allowed';

  return (
    <Route
      exact={false}
      path={path}
      render={(props: RouteProps) =>
        auth && userHasRequiredRole ? (
          <Component {...props} />
        ) : (
          <Redirect
            to={{
              pathname: userHasRequiredRole
                ? `auth/${NonAuthRoutes.signIn}`
                : NonAuthRoutes.unauthorized,
              state: {
                message,
                requestedPath: path,
              },
            }}
          />
        )
      }
    />
  );
};