Building Role Based Access Control in Node.js Apps with JWT Authentication

Building Role-Based Access Control in Node.js Apps with JWT Authentication

In modern applications, security is paramount. Role-Based Access Control (RBAC) is a powerful way to manage access to resources by assigning roles to users. Coupled with JSON Web Token (JWT) authentication, RBAC becomes a seamless and secure method for protecting routes in your Node.js application.

1. What is Role-Based Access Control?

Role-Based Access Control (RBAC) restricts access based on users’ roles. For example:

  • Admin: Can manage all resources.
  • Editor: Can modify content but not delete it.
  • Viewer: Can only view content.

RBAC ensures users can only perform actions permitted for their role, reducing security vulnerabilities.

2. Why Use JWT for Authentication?

JWT (JSON Web Token) is a compact, URL-safe token for securely transmitting information between parties. JWT is widely used for its simplicity and stateless nature. It encodes user data and serves as a mechanism for authorization and authentication.

3. Setting Up the Node.js Application

Start by setting up a basic Node.js application with express for handling routes and jsonwebtoken for JWT.

Step 1: Initialize the Project

				
					mkdir rbac-nodejs
cd rbac-nodejs
npm init -y
npm install express jsonwebtoken bcryptjs body-parser dotenv

				
			

Step 2: Create Basic Structure

Your folder structure should look like this:

				
					rbac-nodejs/
│
├── .env
├── server.js
├── routes/
│   ├── auth.js
│   └── user.js
└── middleware/
    ├── authenticate.js
    └── authorize.js

				
			

Step 3: Configure server.js

Create a simple server setup:

				
					require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");

const app = express();
app.use(bodyParser.json());

// Routes
app.use("/auth", require("./routes/auth"));
app.use("/user", require("./routes/user"));

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
});

				
			

4. Implementing JWT Authentication

JWT consists of three parts: Header, Payload, and Signature. Let’s implement login and token generation.

Create the auth.js Route

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

const router = express.Router();
const users = [
    {
        id: 1,
        username: "admin",
        password: bcrypt.hashSync("admin123", 10),
        role: "admin",
    },
    {
        id: 2,
        username: "editor",
        password: bcrypt.hashSync("editor123", 10),
        role: "editor",
    },
];

// Login Endpoint
router.post("/login", (req, res) => {
    const { username, password } = req.body;
    const user = users.find((u) => u.username === username);

    if (!user || !bcrypt.compareSync(password, user.password)) {
        return res.status(401).json({ message: "Invalid credentials" });
    }

    const token = jwt.sign({ id: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: "1h" });
    res.json({ token });
});

module.exports = router;

				
			

5. Adding RBAC to Your Application

Middleware for Authentication

Create authenticate.js to verify the JWT.

				
					const jwt = require("jsonwebtoken");

function authenticate(req, res, next) {
    const token = req.headers["authorization"];
    if (!token) return res.status(403).json({ message: "No token provided" });

    jwt.verify(token.split(" ")[1], process.env.JWT_SECRET, (err, decoded) => {
        if (err) return res.status(401).json({ message: "Unauthorized" });
        req.user = decoded;
        next();
    });
}

module.exports = authenticate;

				
			

Middleware for Authorization

Create authorize.js to restrict access based on roles.

				
					function authorize(roles) {
    return (req, res, next) => {
        if (!roles.includes(req.user.role)) {
            return res.status(403).json({ message: "Access forbidden" });
        }
        next();
    };
}

module.exports = authorize;

				
			

6. Protecting Routes

Create the user.js Route

Add endpoints that use RBAC for access control.

				
					const express = require("express");
const authenticate = require("../middleware/authenticate");
const authorize = require("../middleware/authorize");

const router = express.Router();

// Open to all authenticated users
router.get("/profile", authenticate, (req, res) => {
    res.json({ message: `Welcome, user ${req.user.id}!`, role: req.user.role });
});

// Admin-only route
router.delete("/delete", authenticate, authorize(["admin"]), (req, res) => {
    res.json({ message: "User deleted successfully!" });
});

// Editor and Admin route
router.post("/edit", authenticate, authorize(["editor", "admin"]), (req, res) => {
    res.json({ message: "Content edited successfully!" });
});

module.exports = router;

				
			

7. Testing and Securing the App

  1. Generate a Token: Use the /auth/login endpoint to obtain a JWT by providing valid credentials.
  2. Test Routes: Use a tool like Postman to access the endpoints with and without the token.
  3. Secure Your App:
    • Use HTTPS in production.
    • Store JWT secrets securely using dotenv or a similar tool.
    • Implement token blacklisting if necessary.

8. Conclusion

RBAC and JWT together provide a scalable and secure way to manage access in Node.js applications. With this setup, you can dynamically manage user roles and permissions, ensuring secure access to your application resources.

Leave a ReplyCancel reply

Exit mobile version