auth-react-router
TypeScript icon, indicating that this package has built-in type declarations

0.4.7 • Public • Published

auth-react-router is a package that wraps over react-router-dom v6 and allows you, to easily define the routes, based on user authorized (isAuth) and role (userRole) state. It provides a simple API for configuring public, private and common routes (React suspense ready) and it has a simple and advance RBAC configuration.

This code and route pattern is used on most of the projects and would probably meet all the routing requirement for an actual react application.

Note: react-router-dom version >= 6 is required

Getting Started

Define your application routes *(easier to maintain if are in separate file)

// routes.tsx

import React from 'react';
import { IRoutesConfig } from 'auth-react-router';
import LoginPage from '../pages/LoginPage.tsx';

// public lazy loaded pages
const LazyPublicPage = React.lazy(() => import('../pages/PublicPage.tsx'));

// private lazy loaded pages
const LazyPrivatePage = React.lazy(() => import('../pages/PrivatePage.tsx'));
const LazyProfilePage = React.lazy(() => import('../pages/ProfilePage.tsx'));

// define some roles if RBAC is needed
export const roles = {
  ADMIN: 'ADMIN',
  OPERATION: 'OPERATION',
  MANAGER: 'MANAGER',
  REGULAR: 'REGULAR',
};

// routes definition
export const routes: IRoutesConfig = {
  publicRedirectRoute: '/profile', // redirect to `/profile` when authorized is trying to access public routes
  privateRedirectRoute: '/login', // redirect to `/login` when unauthorized user access a private route
  defaultFallback: <MyCustomSpinner />,
  // if the role of the user (`userRole` props) is not contained in the route `roles`
  InvalidUserRoleFallback: ({ currentUserRole, routeRequiredRoles }) => <p>USER HAS NO PERMISSION FOR THIS ROUTE, current user role is {currentUserRole}, required roles: {JSON.stringify(routeRequiredRoles)}</p>,
  public: [
    {
      path: '/public',
      component: <LazyPublicPage />
    },
    {
      path: '/login',
      component: <LoginPage />,
    },
  ],
  private: [
    {
      path: '/private',
      component: <LazyPrivatePage />,
      // role based routing
      // user must have ONE of the bellow roles
      roles: [roles.ADMIN, roles.MANAGER] 
    },
    {
      path: '/profile',
      component: <LazyProfilePage /> // any autorized user can access this route
    },
    {
      path: '/admin_and_operation',
      roles: [roles.ADMIN, roles.OPERATION], 
      allRolesRequired: true, // user must have ADMIN and OPERATION role to access this route
      component: <h1>ADMIN and OPERATION Page</h1>,
    },
    // nested routes example
    {
      path: '/posts',
      component: <>
        <h1>Posts Lists Layout</h1>
        <Outlet/> {/* render outlet (the matched paths) */}
      </>,
      children: [
        {
          index: true,
          component: <h1>a list of posts here...</h1>
        },
        {
          path: 'create',
          component: <h1>create new post</h1>
    	},
        {
          path: ':id',
          component: <>
            Single post page layout
            <Outlet/>
          </>,
          children: [
            {
              index: true,
              component: <h1>post details</h1>
            },
            {
              path: 'update',
              component: <h1>update post with dynamic :id</h1>
            }
          ]
        }
      ]
    }
  ],
  common: [
    {
      path: '/',
      component: <p>common</p>,
    },
    {
      path: '*',
      component: <p>page not found 404</p>,
    },
  ],
};

Link the defined above routes using AppRouter component

import { AppRouter, Routes } from 'auth-react-router';
import { BrowserRouter } from 'react-router-dom';
import { routes, roles } from './routes';

export const App = () => {
  const { isAuth } = useAuthProvider();
  return (
    <BrowserRouter>
      {/* `userRole` is optional, use it only if at least one Route has the `roles` property */}
      {/* `userRole` can be an array of roles too, usually you will fetch it from an API and set it here */}
      <AppRouter isAuth={isAuth} routes={routes} userRole={roles.ADMIN}>
        {/* Wrap `Routes` component into a Layout component or add Header */}
        <Routes />
      </AppRouter>
    </BrowserRouter>
  );
};

That is it, super easy!

To add a new route just add it to public, private or common array and it will work.

Check out example directory for a demo application (includes all most of the described features).

$ git clone https://github.com/nichitaa/auth-react-router # clone repo
$ npm i # install library dependencies
$ npm start # build the library `dist` folder

$ cd example
$ npm i # install demo app dependencies 
$ npm run dev # see live changes for demo app

Hooks

useCheckRole

Validate current user role with some given roles. This way is easy to dynamically render the UI, block or allow some of your application functionalities in dependence with user role.

useCheckRole arguments:

requiredRoles: string | string[]; // role/roles to check against 
allRolesRequired?: boolean; // if user must have all roles to have access to resource

useCheckRole return:

interface IUseCheckRoleReturn {
  isAllowed?: boolean; // eighter user has required permission or not
  userRole?: string[] | string; // current user permission
}

usage:

const Component = () => {
  const { isAllowed, userRole } = useCheckRole(roles.OPERATION);
  // render UI based on isAllowed flag
  return <h1>Component {isAllowed && <p>and isAllowed</p>}</h1>;
};
const Component = () => {
  const { isAllowed, userRole } = useCheckRole(['OPERATION', 'ADMIN']); // should have only one of them
  return <h1>Component {isAllowed && <p>and isAllowed</p>}</h1>;
};
const Component = () => {
  const { isAllowed, userRole } = useCheckRole(['OPERATION', 'ADMIN'], true); // should have both roles
  return <h1>Component {isAllowed && <p>and isAllowed</p>}</h1>;
};

Router / Routes basic configuration

AppRouter Provider interface

export interface IRouterContextProps {
  /** routes configuration */
  routes: IRoutesConfig;

  /** authorization state of the user, if not provided only `common` routes will work correspondingly */
  isAuth?: boolean;

  /** current user role that will be validated for accessing a specific route */
  userRole?: string;
}

routes configuration interface

export interface IRoutesConfig {
  /**
   * defaults to `/`
   * authorized users on public routes will be redirected to this route
   */
  privateRedirectRoute?: string;

  /**
   * defaults to `/`
   * unauthorized users on private routes will be redirected to this route
   */
  publicRedirectRoute?: string;

  /** default fallback component for lazy loaded route components */
  defaultFallback?: React.ReactElement;

  /** fallback component in case the user does not have the required role to access the route */
  InvalidUserRoleFallback?: React.ComponentType<any>

  /** private routes are accessible only by authorized users */
  private?: IRoute[];

  /** public routes are accessible only by unauthorized users */
  public?: IRoute[];

  /** common routes are accessible only by authorized and unauthorized users */
  common?: IRoute[];
}

single route interface IRoute

export interface IRoute {
  /** a valid react-router-dom v6 path */
  path?: string;

  /** the component to be rendered under the path */
  component: React.ReactElement;
    
  /** if this is a route definition for index path */
  index?: boolean;
   
  /** used for nested (aka children) routes definition */
  children?: IRoute[];

  /**
   * if route component is lazy loaded using React.lazy() a fallback loading / spinner component can be specified
   * it has higher priority then the `defaultFallback` component
   * */
  fallback?: React.ReactElement;

  /**
   * what roles a user must have in order to view this page,
   * if not provided, then the page can be accessed by every user
   */
  roles?: string[];
    
  /**
   * user must have all roles from `roles` array to access the route,
   * defaults to `false` 
   */
  allRolesRequired?: boolean;
}

Readme

Keywords

none

Package Sidebar

Install

npm i auth-react-router

Weekly Downloads

5

Version

0.4.7

License

MIT

Unpacked Size

115 kB

Total Files

36

Last publish

Collaborators

  • nichittaa