0

I am new to nextJS, and I want to build a nextJS application with role-based authentication, In order to do role-based authentication, I have used the Higher-Order Component that component checks whether the user has access to access the pages. In my scenario, I want to check the user's access to the dynamic page. In order to do that I create a route.ts file this file contains all static route paths based on the user's access, But I didn't put the dynamic path in this file because I don't know the dynamic route what's look like, in order to fix that I had created the dynamicPath key into every user, that holds base URL and dynamic URL.

In order to handle the dynamic route in the role-based authentication, I have gat the params Id from the Higher-Order component, and then I concat that to the dynamicPath's base URL, and then I check that into the dynamicPath's dynamic URL. If it will true the user would access the page, if not the user would be redirected to the permission-denied page. It works fine, but I don't know this approach is correct or not, because I looked up the documents but I could not find any approach to how to handle the dynamic route in the role-based authentication.

Please let me know if the approach I have done to handle the dynamic route for role-based authentication is right or wrong and if it is the wrong approach what should I do.

Higher-Order-Component

import React, { useEffect } from "react";
import { useRouter, usePathname } from "next/navigation";
import { roles, rolesAccess } from "@/utils/route"; // import the roles and rolesAccess from route.ts file
import { isLoginReducer } from "@/redux/features/authSlice";
import { useDispatch, useSelector } from "react-redux";
import Loader from "@/components/elements/Loader";

const UseRoleHook = (Component: React.ElementType) => {
  return function ProtectedRoute({
    params,
  }: {
    params: { [key: string]: string | number };
  }) {
    const dispatch = useDispatch();
    const { role, isLogin } = useSelector(
      (state: { authPersistedReducer: { role: string; isLogin: boolean } }) =>
        state.authPersistedReducer
    );

    const pathname = usePathname();
    const router = useRouter();
    const paramKey = params && Object.keys(params)[0];

    const hasPermission = false;

    useEffect(() => {
      if (!isLogin) {
        // If the user is not logged in, check if their role is included in the valid roles
        if (roles.includes(role)) {
          dispatch(isLoginReducer(true));
          router.push("/");
        } else {
          router.push("/login");
        }
      } else {
        // If the user is logged in
        if (
          // If it's a static route
          (!paramKey && !rolesAccess[role]?.path.includes(pathname)) ||
          // If it's a dynamic route
          (paramKey &&
            !rolesAccess[role]?.dynamicPaths?.some((route) => {
              return route.dynamic === route.base.concat(`[${paramKey}]`);
            }))
        ) {
          // If the user doesn't have permission for the current route, throw an error
          throw new Error("Permission Denied");
        } else {
          // If the user has permission, navigate to the current route
          router.push(pathname);
        }
      }
    }, [isLogin, role, pathname]);

    if (
      // If it's a static route and the user doesn't have permission
      (!paramKey && !rolesAccess[role]?.path.includes(pathname)) ||
      // If it's a dynamic route and the user doesn't have permission
      (paramKey &&
        !rolesAccess[role]?.dynamicPaths?.some((route) => {
          return route.dynamic === route.base.concat(`[${paramKey}]`);
        }))
    ) {
      return (
        <div className="w-full h-screen flex justify-center items-center">
          <Loader />
        </div>
      );
    }

    // If the user has permission, render the protected component
    return <Component />;
  };
};

export default UseRoleHook;

route.ts

interface Role {
  path: string[];
  dynamicPaths?: [
    {
      dynamic: string;
      base: string;
    }
  ];
}

interface Roles {
  [key: string]: Role; // different key name
}
const roles: string[] = ["admin", "superAdmin", "user"];
const rolesAccess: Roles = {
  superAdmin: {
    path: ["/", "/admin", "/superadmin", "/user"],
    dynamicPaths: [{ dynamic: "/admin/test/[id]", base: "/admin/test/" }],
  },
  admin: {
    path: ["/", "/admin", "/user", "/admin/test"],
  },
  user: {
    path: ["/", "/user"],
    dynamicPaths: [{ dynamic: "/admin/test/[userId]", base: "/admin/test/" }],
  },
};

export { roles, rolesAccess };
Gibson
  • 82
  • 7

0 Answers0