Implementing Role-Based Access Control in React: A Deep Dive into useRoleManagement Hook

Veaceslav - Jul 14 - - Dev Community

Intorduction
Managing user roles and permissions is a critical aspect of web application security and functionality. It ensures that users have appropriate access to different parts of the application based on their roles. In this article, I'll walk you through the implementation of a custom useRoleManagement hook in React, which decodes a JWT token to manage user permissions.

Setting Up
Before diving into the implementation, let's set up the necessary tools and libraries. You'll need:

  • React
  • jwt-decode for decoding JWT tokens
  • react-router-dom for managing routing and extracting the current path
npm install react-router-dom jwt-decode
Enter fullscreen mode Exit fullscreen mode

Implementation
Let's start by defining the interfaces for our permissions and JWT payload.

import { JwtPayload, jwtDecode } from 'jwt-decode';
import { useLocation } from 'react-router-dom';

// Realm is for authentication mechanism like Keycloak

interface Realm extends JwtPayload {
  realm_access: {
    roles: string[];
  };
}

// Interface representing the user's permissions

interface Permissions {
  add: boolean;
  view: boolean;
  edit: boolean;
  confirm?: boolean;
  include?: boolean;
  deleteRow?: boolean; 
}

// Interface representing the user's permissions for various pages

interface UserPermissions {
  partners?: Permissions;
  projects?: Permissions;
 // More pages can be added here as needed
}

type PermissionKeys = keyof UserPermissions;
Enter fullscreen mode Exit fullscreen mode

Next, we define the permissions for different roles:

const permissions = {
  manager: {
    partners: {
      add: false,
      view: false,
      edit: false,
      deleteRow: true,
    },
    projects: {
      add: false,
      view: false,
      edit: false,
      deleteRow: false,
    },
    // Define more pages
  },
  analyst: {
    // Define permissions for analyst role
  },
  director: {
    // Define permissions for director role
  },
};

Enter fullscreen mode Exit fullscreen mode

Check if Role Exists
We need a function to check if a role exists and return the associated permissions:

function ifRoleExists(roles: string[]) {
  return roles
    .map((role) => {
      return permissions[role as keyof typeof permissions];
    })
    .filter(Boolean)[0];
}

Enter fullscreen mode Exit fullscreen mode

Get Permissions for the Role
This function retrieves the permissions for a given role and path:

function getPermissions(roleExists: UserPermissions, pathName: string) {
  const withoutSlash = pathName.replace('/', '') as PermissionKeys;
  return roleExists?.[withoutSlash] ?? pathName;
}

Enter fullscreen mode Exit fullscreen mode

useRoleManagement Hook
Finally, we implement the useRoleManagement hook:

export function useRoleManagement() {
  const { pathname } = useLocation();

  const token = localStorage.getItem('token');

  let decodedToken: Realm | null = null;

  try {
    if (token) {
      decodedToken = jwtDecode<Realm>(token);
    }
  } catch (error) {
    console.error('Invalid token:', error);
  }

  const roles = decodedToken?.realm_access?.roles ?? [];

  const roleExists = ifRoleExists(roles);

  const rolePermissions = getPermissions(
    roleExists,
    pathname !== '/' ? pathname : '/partners',
  );

  if (!rolePermissions) {
    return {};
  }

  const {
    add,
    view,
    edit,
    deleteRow,
    confirm,
    include,
  } = rolePermissions as Permissions;

  return {
    add,
    view,
    edit,
    deleteRow,
    confirm,
    include,
  };
}

Enter fullscreen mode Exit fullscreen mode

Usage Example
Here's how you can use the useRoleManagement hook in a React component:

import React from 'react';
import { useRoleManagement } from './useRoleManagement';

const Dashboard = () => {
  const { add, view, edit, deleteRow } = useRoleManagement();

  return (
    <div>
      {view && <p>You can view this content.</p>}
      {add && <button>Add Item</button>}
      {edit && <button>Edit Item</button>}
      {deleteRow && <button>Delete Item</button>}
    </div>
  );
};

export default Dashboard;

Enter fullscreen mode Exit fullscreen mode

Conclusion
In this article, we've implemented a custom useRoleManagement hook in React to manage user permissions based on their roles. This approach ensures that your application remains secure and that users have the appropriate access based on their roles. Feel free to customize the permissions and roles according to your application's requirements.

I hope you found this guide helpful. If you have any questions or suggestions, please leave a comment below!

.
Terabox Video Player