Paramclasses Academy - Blog

Tutorials Details

←
Post 29 of 40
→

Role-Based Access Control in Node.js APIs: Admin, Manager and User Roles

Role-Based Access Control in Node.js APIs: Admin, Manager and User Roles

October 30, 2026

✦ Role-Based Access Control in Node.js APIs: Admin, Manager and User Roles

Modern web applications often have different types of users. For example, an e-commerce website may have an Admin who manages the entire system, a Manager who handles products and orders, and a User who can view products and manage their own profile.

If every logged-in user can access every API endpoint, the application becomes difficult to manage and less secure. This is why developers use Role-Based Access Control (RBAC).

In this practical tutorial, you will learn how to implement Role-Based Access Control in Node.js APIs using Express.js, MySQL, JWT, and middleware.

We will create three roles:

  • Admin: Can access all administrative APIs.

  • Manager: Can manage products and view reports.

  • User: Can access basic user APIs and their own profile.

✦ What Is Role-Based Access Control (RBAC)?

Role-Based Access Control is a security method that gives permissions to users according to their assigned roles.

Instead of checking individual permissions for every user, the application checks the user's role before allowing access to a protected resource.

For example:

Role Permission
Admin Manage users, products and reports
Manager Manage products and view reports
User View own profile

RBAC is commonly used in:

  • School Management Systems

  • Hospital Management Systems

  • E-commerce Websites

  • CRM Applications

  • Employee Management Systems

  • Admin Dashboards

  • Banking and Business Applications

✦ How RBAC Works in Node.js

A typical RBAC authentication process works like this:

  1. The user logs in using email and password.

  2. The server verifies the user's credentials.

  3. The server generates a JWT containing the user's ID and role.

  4. The client sends the JWT with API requests.

  5. JWT middleware verifies the token.

  6. Role middleware checks whether the user's role is allowed.

  7. The API executes only when the role has permission.

For example, if a normal user tries to access an Admin API, the server returns a 403 Forbidden response.

✦ Step 1: Create a Node.js Project

Create a project folder:

mkdir nodejs-rbac
cd nodejs-rbac
npm init -y

Install the required packages:

npm install express mysql2 jsonwebtoken bcrypt dotenv

We will use:

  • Express.js for API development.

  • MySQL for storing users and roles.

  • JWT for authentication.

  • bcrypt for password hashing.

  • dotenv for environment variables.

✦ Step 2: Create the MySQL Database

Open phpMyAdmin or MySQL and execute:

CREATE DATABASE rbac_db;

USE rbac_db;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(150) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    role ENUM('admin', 'manager', 'user')
        NOT NULL DEFAULT 'user',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

The role column identifies the type of user.

For this tutorial, we are using three roles: admin, manager, and user.

Important: In a real application, public signup must never allow users to select or submit their own Admin role. Assign privileged roles through a trusted administrative process.

✦ Step 3: Configure Environment Variables

Create a .env file:

PORT=3000

DB_HOST=localhost
DB_USER=root
DB_PASSWORD=
DB_NAME=rbac_db

JWT_SECRET=replace_with_a_long_random_secret
JWT_EXPIRES_IN=15m

Use a strong, randomly generated JWT secret. Do not share it publicly or upload the .env file to GitHub.

Create a .gitignore file:

node_modules/
.env

✦ Step 4: Create the MySQL Connection

Create a folder named config and add db.js:

const mysql = require("mysql2/promise");

const pool = mysql.createPool({
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    waitForConnections: true,
    connectionLimit: 10
});

module.exports = pool;

This connection pool will be used by our APIs to communicate with MySQL.

✦ Step 5: Create JWT Authentication Middleware

Create a folder named middleware.

Inside it, create authMiddleware.js:

const jwt = require("jsonwebtoken");

function authenticateToken(req, res, next) {

    const authHeader = req.headers.authorization;

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

    const parts = authHeader.split(" ");

    if (parts.length !== 2 || parts[0] !== "Bearer") {
        return res.status(401).json({
            message: "Invalid authorization format"
        });
    }

    const token = parts[1];

    jwt.verify(
        token,
        process.env.JWT_SECRET,
        (err, decoded) => {

            if (err) {
                return res.status(403).json({
                    message: "Invalid or expired token"
                });
            }

            req.user = decoded;

            next();
        }
    );
}

module.exports = authenticateToken;

Understanding the code:

  • jwt.verify() checks the JWT signature and expiration.

  • req.user stores the verified token payload.

  • next() allows the request to continue.

  • If the token is invalid, the request is rejected.

This middleware verifies the user's identity. We will create another middleware to check the user's role.

✦ Step 6: Create Role-Based Authorization Middleware

Inside the middleware folder, create roleMiddleware.js:

function authorizeRoles(...allowedRoles) {

    return (req, res, next) => {

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

            return res.status(403).json({
                message: "Access denied. You do not have permission."
            });
        }

        next();
    };
}

module.exports = authorizeRoles;

How does it work?

The authorizeRoles() function accepts one or more permitted roles.

For example:

authorizeRoles("admin")

Only Admin users can access the route.

authorizeRoles("admin", "manager")

Both Admin and Manager users can access the route.

The middleware checks the role stored in the verified JWT payload.

✦ Step 7: Create Login API with JWT Role

Create server.js in the project root:

require("dotenv").config();

const express = require("express");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");

const pool = require("./config/db");

const authenticateToken =
    require("./middleware/authMiddleware");

const authorizeRoles =
    require("./middleware/roleMiddleware");

const app = express();

app.use(express.json());


// LOGIN API
app.post("/api/login", async (req, res) => {

    try {

        const { email, password } = req.body;

        if (!email || !password) {
            return res.status(400).json({
                message: "Email and password are required"
            });
        }

        const [users] = await pool.execute(
            "SELECT id, name, email, password, role FROM users WHERE email = ?",
            [email]
        );

        if (users.length === 0) {
            return res.status(401).json({
                message: "Invalid email or password"
            });
        }

        const user = users[0];

        const isMatch = await bcrypt.compare(
            password,
            user.password
        );

        if (!isMatch) {
            return res.status(401).json({
                message: "Invalid email or password"
            });
        }

        // Generate JWT with user role
        const token = jwt.sign(
            {
                id: user.id,
                email: user.email,
                role: user.role
            },
            process.env.JWT_SECRET,
            {
                expiresIn: process.env.JWT_EXPIRES_IN || "15m"
            }
        );

        res.json({
            message: "Login successful",
            token: token,
            user: {
                id: user.id,
                name: user.name,
                email: user.email,
                role: user.role
            }
        });

    } catch (error) {

        console.error(error);

        res.status(500).json({
            message: "Internal server error"
        });
    }
});


// ADMIN ONLY API
app.get(
    "/api/admin/dashboard",
    authenticateToken,
    authorizeRoles("admin"),
    (req, res) => {

        res.json({
            message: "Welcome to Admin Dashboard",
            role: req.user.role
        });
    }
);


// ADMIN AND MANAGER API
app.get(
    "/api/manager/products",
    authenticateToken,
    authorizeRoles("admin", "manager"),
    (req, res) => {

        res.json({
            message: "Product management access granted",
            role: req.user.role
        });
    }
);


// ALL LOGGED-IN USERS API
app.get(
    "/api/user/profile",
    authenticateToken,
    authorizeRoles("admin", "manager", "user"),
    (req, res) => {

        res.json({
            message: "User profile access granted",
            user: req.user
        });
    }
);


// Server Start
const PORT = process.env.PORT || 3000;

async function startServer() {

    try {

        await pool.query("SELECT 1");

        console.log("MySQL connected successfully");

        app.listen(PORT, () => {
            console.log(`Server running on port ${PORT}`);
        });

    } catch (error) {

        console.error("Database connection failed:", error.message);

        process.exit(1);
    }
}

startServer();

Our application now has three protected endpoints:

API Endpoint Allowed Roles
/api/admin/dashboard Admin
/api/manager/products Admin, Manager
/api/user/profile Admin, Manager, User

Note: For simplicity, the profile endpoint demonstrates access for all logged-in roles. In a real application, fetch the profile using req.user.id from the database and ensure users can only view their own records unless an explicit permission allows otherwise.

✦ Step 8: Add Test Users in MySQL

We need users with different roles to test our application.

First, generate bcrypt password hashes using Node.js.

Run this command in your project terminal:

node -e "require('bcrypt').hash('123456', 12).then(console.log)"

Copy the generated hash and use it in the following SQL query:

INSERT INTO users
(name, email, password, role)
VALUES
(
    'Admin User',
    'admin@example.com',
    'PASTE_BCRYPT_HASH_HERE',
    'admin'
),
(
    'Manager User',
    'manager@example.com',
    'PASTE_BCRYPT_HASH_HERE',
    'manager'
),
(
    'Normal User',
    'user@example.com',
    'PASTE_BCRYPT_HASH_HERE',
    'user'
);

For this example, all three accounts can use the password 123456 because the same password hash is being inserted. Use unique, strong passwords for real accounts.

✦ Step 9: Run the Node.js Application

Start the server:

node server.js

Expected output:

MySQL connected successfully
Server running on port 3000

Your Node.js RBAC API is now ready for testing.

✦ Step 10: Test Role-Based Access Using Postman

Open Postman and test the following APIs.

1. Login as Admin

Method: POST

http://localhost:3000/api/login

Body → raw → JSON:

{
    "email": "admin@example.com",
    "password": "123456"
}

Copy the JWT token returned by the server.

2. Access Admin Dashboard

Method: GET

http://localhost:3000/api/admin/dashboard

Authorization → Bearer Token → paste the Admin token.

Expected response:

{
    "message": "Welcome to Admin Dashboard",
    "role": "admin"
}

3. Login as Manager

Use:

{
    "email": "manager@example.com",
    "password": "123456"
}

Copy the Manager token and access:

http://localhost:3000/api/manager/products

The Manager should receive access.

But if the Manager tries to access:

http://localhost:3000/api/admin/dashboard

The server should return HTTP 403 Forbidden.

4. Login as Normal User

Use:

{
    "email": "user@example.com",
    "password": "123456"
}

The User can access:

http://localhost:3000/api/user/profile

But cannot access the Admin Dashboard or Manager Products API.

✦ Authentication vs Authorization

These two concepts are important in backend development.

Authentication Authorization
Verifies who the user is Checks what the user can access
Uses login credentials and tokens Uses roles and permissions
Example: User login Example: Admin dashboard access
Usually happens first Happens after authentication

In simple words:

Authentication = Who are you?

Authorization = What are you allowed to do?

✦ Important RBAC Security Best Practices

Follow these practices when implementing RBAC in real applications:

  1. Never allow public users to assign themselves Admin or Manager roles.

  2. Use strong JWT secrets and short access-token expiration times.

  3. Always verify JWT tokens before checking roles.

  4. Validate all incoming request data.

  5. Use HTTPS in production.

  6. Never expose passwords or secret keys in API responses.

  7. Keep role assignment restricted to trusted administrative operations.

  8. Fetch sensitive user information from the database rather than trusting client-submitted data.

  9. Consider token revocation or short expiration when a user's role changes.

  10. Keep authorization rules consistent across all protected routes.

Important: A JWT containing a role can become outdated if an administrator changes the user's role in the database. For applications requiring immediate role changes, check the current role from the database or use a suitable token-version/revocation strategy.

✦ Frequently Asked Questions

Q1. What is RBAC in Node.js?

RBAC stands for Role-Based Access Control. It allows developers to restrict API access according to user roles such as Admin, Manager, and User.

Q2. What is the difference between JWT and RBAC?

JWT is used to authenticate a user through a signed token. RBAC is used to authorize the user's access based on their role.

Q3. Can we create multiple roles in Node.js?

Yes. You can create roles such as Admin, Manager, Editor, Teacher, Student, Doctor, and User according to your application requirements.

Q4. Can Express.js middleware check user roles?

Yes. Custom Express middleware can verify a user's role before allowing access to a protected API endpoint.

Q5. Is RBAC useful for school and hospital management systems?

Yes. RBAC is useful for managing permissions for administrators, teachers, students, doctors, staff, and other users.

✦ Conclusion

Role-Based Access Control is an important part of secure backend application development. It helps developers control access to APIs and sensitive resources according to user roles.

In this tutorial, we created an RBAC system using Node.js, Express.js, MySQL, JWT, and custom middleware. We implemented Admin, Manager, and User roles and tested their permissions using Postman.

By learning JWT authentication and RBAC together, you can build more secure and professional REST APIs.

Learn practical Node.js, Express.js, MySQL, REST API Development, and Backend Programming with Param Classes.

←
Post 29 of 40
→