Friday, November 28, 2025

thumbnail

Role-Based Access Control in MERN Stack

 Role-Based Access Control (RBAC) in the MERN Stack


Role-Based Access Control ensures that only authorized users can access specific routes, components, or actions based on their role (e.g., Admin, User, Editor).


Common roles:


Admin – full access


Editor – can modify some content


User – basic access


Guest – very limited access


RBAC improves security, prevents unauthorized access, and keeps your app scalable.


1. Designing Roles in MongoDB


A typical user document in MongoDB might look like:


{

  "_id": "12345",

  "name": "Alice",

  "email": "alice@example.com",

  "password": "...hashed...",

  "role": "admin"     // or "user" or "editor"

}



You can store:


a single role ("admin")


multiple roles (["editor", "reviewer"])


role + permissions model (advanced)


2. Adding Role Field in User Schema (Mongoose)

// models/User.js

import mongoose from "mongoose";


const userSchema = new mongoose.Schema({

  name: String,

  email: { type: String, unique: true },

  password: String,

  role: {

    type: String,

    enum: ["user", "editor", "admin"],

    default: "user"

  }

});


export default mongoose.model("User", userSchema);


3. Assigning Roles on Signup / Admin Panel


Example signup route:


// default role = "user"

const newUser = await User.create({

  name,

  email,

  password: hashedPassword,

  role: "user"

});



Admins can later update roles:


await User.findByIdAndUpdate(id, { role: "editor" });


4. Generating JWT with Role


When the user logs in, include the role in the JWT payload:


import jwt from "jsonwebtoken";


const token = jwt.sign(

  {

    id: user._id,

    role: user.role

  },

  process.env.JWT_SECRET,

  { expiresIn: "1d" }

);



This allows the backend to check roles on each request.


5. Middleware: Protect Routes + Check Roles

A. Authentication Middleware


Verify token and attach user info to req.user.


// middleware/auth.js

import jwt from "jsonwebtoken";


export const auth = (req, res, next) => {

  const token = req.headers.authorization?.split(" ")[1];

  if (!token) return res.status(401).json({ message: "Unauthorized" });


  try {

    const decoded = jwt.verify(token, process.env.JWT_SECRET);

    req.user = decoded; // contains id + role

    next();

  } catch (error) {

    res.status(401).json({ message: "Invalid token" });

  }

};


B. Role Authorization Middleware

// middleware/authorize.js

export const authorize = (...allowedRoles) => {

  return (req, res, next) => {

    if (!allowedRoles.includes(req.user.role)) {

      return res.status(403).json({ message: "Forbidden: Access denied" });

    }

    next();

  };

};



Usage:


router.post("/admin-only", auth, authorize("admin"), handler);

router.put("/edit-post", auth, authorize("editor", "admin"), handler);


6. Protecting Backend Routes


Example:


import express from "express";

import { auth } from "../middleware/auth.js";

import { authorize } from "../middleware/authorize.js";


const router = express.Router();


router.get("/users", auth, authorize("admin"), async (req, res) => {

  const users = await User.find();

  res.json(users);

});


export default router;



Only admin can access /users.


7. Frontend (React) Role Handling


Store role after login, typically in:


React Context


Redux store


localStorage (short-term; avoid storing sensitive data)


Example:


localStorage.setItem("role", user.role);


Protecting React Routes

Route Wrapper

// components/ProtectedRoute.jsx

import { Navigate } from "react-router-dom";


export default function ProtectedRoute({ children, role, allowedRoles }) {

  if (!role) return <Navigate to="/login" />;


  if (!allowedRoles.includes(role)) return <Navigate to="/not-authorized" />;


  return children;

}


Usage:

<ProtectedRoute role={role} allowedRoles={["admin"]}>

  <AdminDashboard />

</ProtectedRoute>


8. Showing/Hiding UI Elements Based on Role

{role === "admin" && (

  <button onClick={deleteUser}>Delete User</button>

)}



or more complex:


const allowed = ["editor", "admin"].includes(role);


9. Best Practices for RBAC in MERN

Security


✔ Never trust frontend-only role checking

✔ Always verify role again on the backend

✔ Avoid storing JWTs in localStorage if possible (use httpOnly cookies)


Scalability


✔ Use role + permission system for large apps

✔ Keep roles in config files, not hardcoded everywhere

✔ Add logging for unauthorized access attempts


Maintainability


✔ Centralize RBAC logic in middleware

✔ Keep user roles consistent (ENUM or separate Role table)


10. Example Folder Structure

backend/

  models/

  routes/

  middleware/

    auth.js

    authorize.js

  controllers/

  app.js


frontend/

  src/

    components/

    pages/

    context/

    utils/


Summary


To implement RBAC in a MERN app:


Store roles in MongoDB (User model).


Include role in JWT during login.


Use auth middleware for authentication.


Use authorize middleware to restrict routes.


Protect React routes and hide UI based on roles.


Secure backend as the ultimate authority.


This makes your MERN app secure, scalable, and easy to manage.

Learn MERN Stack Training in Hyderabad

Read More

Preventing XSS & CSRF in MERN

Securing Your MERN Stack App

Optimizing MongoDB Queries

Load Testing Node.js APIs

Visit Our Quality Thought Training Institute in Hyderabad

Get Directions 


Subscribe by Email

Follow Updates Articles from This Blog via Email

No Comments

About

Search This Blog

Powered by Blogger.

Blog Archive