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 };