Photo by Nick Morrison on Unsplash
Role-Based Access Control (RBAC) implementation in React: A Practical Guide - Part 2
React Implementation Guide
In our previous post, we discussed the backend implementation of Role-Based Access Control (RBAC). Now, let's dive into how we can implement RBAC in a React application. We'll focus on creating a flexible and maintainable system for managing user permissions and routes.
1. Creating a Permission Context
First, let's create a context to manage our permissions:
import React, { createContext, useContext, useState } from 'react';
const PermissionContext = createContext();
export const PermissionProvider = ({ children }) => {
const [userPermissions, setUserPermissions] = useState([]);
const hasPermission = (permission) => userPermissions.includes(permission);
return (
<PermissionContext.Provider value={{ userPermissions, setUserPermissions, hasPermission }}>
{children}
</PermissionContext.Provider>
);
};
export const usePermission = () => useContext(PermissionContext);
2. Creating a Protected Route Component
Next, let's create a ProtectedRoute
component that checks for permissions:
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { usePermission } from './PermissionContext';
const ProtectedRoute = ({ permission, component: Component, ...rest }) => {
const { hasPermission } = usePermission();
return (
<Route
{...rest}
render={(props) =>
hasPermission(permission) ? (
<Component {...props} />
) : (
<Redirect to="/unauthorized" />
)
}
/>
);
};
export default ProtectedRoute;
3. Defining Routes with Permissions
Now, let's define our routes with associated permissions:
import React from 'react';
import { Switch } from 'react-router-dom';
import ProtectedRoute from './ProtectedRoute';
import Dashboard from './pages/Dashboard';
import ProductList from './pages/ProductList';
import ProductCreate from './pages/ProductCreate';
import ProductEdit from './pages/ProductEdit';
const AppRoutes = () => (
<Switch>
<ProtectedRoute exact path="/" permission="view_dashboard" component={Dashboard} />
<ProtectedRoute exact path="/products" permission="view_products" component={ProductList} />
<ProtectedRoute path="/products/new" permission="create_product" component={ProductCreate} />
<ProtectedRoute path="/products/:id/edit" permission="edit_product" component={ProductEdit} />
</Switch>
);
export default AppRoutes;
4. Implementing the Permission Hook in Components
We can use our custom hook to check permissions within components:
import React from 'react';
import { usePermission } from './PermissionContext';
const ProductActions = ({ productId }) => {
const { hasPermission } = usePermission();
return (
<div>
{hasPermission('edit_product') && (
<button onClick={() => handleEdit(productId)}>Edit</button>
)}
{hasPermission('delete_product') && (
<button onClick={() => handleDelete(productId)}>Delete</button>
)}
</div>
);
};
export default ProductActions;
5. Setting Up the App with the Permission Provider
Finally, let's set up our main App component:
import React, { useEffect } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { PermissionProvider, usePermission } from './PermissionContext';
import AppRoutes from './AppRoutes';
const App = () => {
return (
<Router>
<PermissionProvider>
<AppContent />
</PermissionProvider>
</Router>
);
};
const AppContent = () => {
const { setUserPermissions } = usePermission();
useEffect(() => {
// Simulating API call to fetch user permissions
const fetchPermissions = async () => {
const response = await fetch('/api/user/permissions');
const permissions = await response.json();
setUserPermissions(permissions);
};
fetchPermissions();
}, [setUserPermissions]);
return <AppRoutes />;
};
export default App;
Conclusion
This approach to implementing RBAC in React offers several benefits:
It uses React's Context API for global permission state management.
It employs custom hooks for easy permission checks throughout the application.
It separates permission logic from route definitions, improving maintainability.
It allows for dynamic permission updates without requiring a full app reload.
Remember, while this implementation handles frontend permissions, it's crucial to also implement proper backend validation to ensure complete security. In the next part of our series, we'll explore strategies for syncing frontend and backend permissions and handling real-time permission updates.