Authentication, Authorization & Security!

Last Updated : Oct 2026


Authentication is the process of verifying the identity of a user or service. It ensures that only authorized users or services can access certain resources or perform certain actions.

Authorization is the process of granting or denying access to a user or service based on their permissions or roles. It determines what actions a user or service can perform on a resource.

Security is the process of preventing unauthorized access to resources or data. It includes measures such as encryption, access controls, and authentication.

In the context of web development, authentication, authorization, and security are often used together to ensure that users can access and interact with resources on a website or web application.

So For

  • Data Encryption, we use crypto or bcryptjs module in Node.js.
  • Stateless Authentication, we use jwt module in Node.js.
  • Authorization, we use custom middleware.

For Understanding, we make a simple Authentication System.Where a user can register, login,, see profile, delete account.

  • Login, Register, are not protected routes.
  • Profile, is protected route.
  • Delete Account, is protected route and only admin can access.

So let's get started and instal following modules:

npm install bcryptjs jsonwebtoken dotenv

Data Encryption (bcryptjs)

For better Structure, you can make a file 'utils/passwordHash.js'

import bcrypt from "bcryptjs";

// 1. Hash Password before saving to DB
export const hashPassword = async (plainPassword) => {
  // Salt rounds (Cost factor): 10-12 is industry standard balance of security & speed
  const salt = await bcrypt.genSalt(10);
  const hashedPassword = await bcrypt.hash(plainPassword, salt);
  return hashedPassword;
};

// 2. Compare Plain Password with Stored Hash during Login
export const comparePassword = async (plainPassword, hashedPassword) => {
  return await bcrypt.compare(plainPassword, hashedPassword);
};

Stateless Authentication (jwt)

JWTs allow stateless authentication. When a user logs in, the server generates a signed token and sends it back. The client attaches this token to subsequent HTTP requests in the Authorization header Bearer <token>.

Add Secret Key for JWT on .env

JWT_SECRET=YOUR_SECRET_KEY

For better Structure, you can make a file 'utils/jwt.js'

import jwt from "jsonwebtoken";

const JWT_SECRET = process.env.JWT_SECRET || "super_secret_key_123";

// Generate Token
export const generateToken = (userId, role) => {
  return jwt.sign({ id: userId, role }, JWT_SECRET, {
    expiresIn: "1h", // Token expires in 1 hour (Optional)
  });
};

// Verify Token
export const verifyToken = (token) => {
  return jwt.verify(token, JWT_SECRET);
};

Protecting Routes (custom middleware)

For better Structure, you can make a file 'middleware/auth.js'

import { verifyToken } from "../lib/jwt.js";

export const protect = (req, res, next) => {
  let token;

  // 1. Check for token in Authorization header
  if (
    req.headers.authorization &&
    req.headers.authorization.startsWith("Bearer")
  ) {
    token = req.headers.authorization.split(" ")[1];
  }

  if (!token) {
    return res.status(401).json({ error: "Access denied. Token missing." });
  }

  try {
    // 2. Verify token payload
    const decoded = verifyToken(token);

    // Attach decoded user info to the request object
    req.user = decoded;
    next(); // Pass control to the next route handler
  } catch (error) {
    return res.status(401).json({ error: "Invalid or expired token." });
  }
};

Role-Based Access Control (custom middleware)

To restrict endpoints to specific roles (e.g., admin), build a middleware wrapper in middleware/auth.js:

export const authorize = (...allowedRoles) => {
  return (req, res, next) => {
    if (!req.user || !allowedRoles.includes(req.user.role)) {
      return res.status(403).json({
        error: "Forbidden: You do not have permission to perform this action.",
      });
    }
    next();
  };
};

Controllers

Authentication

For better Structure, you can make a file 'controllers/auth.js'

import { hashPassword, comparePassword } from "../lib/passwordHash.js";
import { generateToken } from "../lib/jwt.js";

// Mock User DB

export const usersDB = [];

export const register = async (req, res) => {
  const { name, email, password, role } = req.body;

  const hashedPassword = await hashPassword(password);
  const newUser = {
    id: usersDB.length + 1,
    name,
    email,
    password: hashedPassword,
    role: role || "user",
  };

  usersDB.push(newUser);
  res
    .status(201)
    .json({ message: "User registered successfully", userId: newUser.id });
};

export const login = async (req, res) => {
  const { email, password } = req.body;

  const user = usersDB.find((user) => user.email === email);

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

  const isPasswordValid = await comparePassword(password, user.password);

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

  const token = generateToken(user.id, user.role);
  res.status(200).json({ token });
};

User Controller

For better Structure, you can make a file 'controllers/user.js'

import { usersDB } from "../controllers/auth.js";

export const getUserProfile = (req, res) => {
  const user = usersDB.find((user) => user.id === req.user.id);
  if (!user) {
    return res.status(404).json({ error: "User not found." });
  }
  res.status(200).json(user);
};

export const deleteUser = (req, res) => {
  const userIndex = usersDB.findIndex((user) => user.id === req.user.id);
  if (userIndex === -1) {
    return res.status(404).json({ error: "User not found." });
  }
  usersDB.splice(userIndex, 1);
  res.status(200).json({ message: "User deleted successfully." });
};

API Routes

For better Structure, you can make a file 'routes/api.js'

import express from "express";
import { protect, authorize } from "../middleware/auth.js";
import { getUserProfile, deleteUser } from "../controllers/user.js";
import { register, login } from "../controllers/auth.js";

const router = express.Router();

// Not protected routes
router.post("/register", register);
router.post("/login", login);

// Protected routes
router.get("/profile", protect, getUserProfile);
router.delete("/delete", protect, authorize("admin"), deleteUser);

export default router;

Server

For better Structure, you can make a file 'server.js'

import express from "express";
import router from "./routes/api.js";

const app = express();

app.use(express.json());

app.use((req, res, next) => {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
  res.setHeader("Access-Control-Allow-Headers", "Content-Type");
  next();
});

app.use("/api", router);

app.use((err, req, res, next) => {
  res.status(err.status || 500).json({
    error: err.message,
  });
});

app.listen(3000, () => {
  console.log("Server started on port 3000");
});

Running the App

node --watch server.js

Liked This Tutorial

Scan QR Code To Leave A Review Or

Click Here To Leave A Review
Scan QR

;