Skip to content

Middleware Authentication Patterns — Complete Implementation Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Middleware Authentication Patterns. We cover key concepts, practical examples, and best practices to help you master this topic.

Middleware authentication patterns validate user identity and permissions before allowing access to protected routes, forming the security foundation of every production web application.

What You'll Learn

By the end of this tutorial, you will build authentication middleware that validates JWT tokens, checks user roles, handles expired sessions, and protects API routes.

Why It Matters

Authentication middleware is the gatekeeper of your application. Without it, anyone can access protected data. DodaTech's services authenticate every API call through middleware before reaching business logic.

Real-World Use

Doda Browser's bookmark sync API uses JWT authentication middleware that verifies tokens, checks expiry, and attaches user context to every request before passing it to route handlers.

Auth Middleware Learning Path

flowchart LR
  A[Logging Middleware] --> B[Auth Middleware]
  B --> C[JWT Validation]
  C --> D[Role-Based Access]
  B --> E{You Are Here}
  style E fill:#f90,color:#fff

Basic Token Authentication

The simplest authentication middleware checks for a token in the request headers and validates it before allowing access.

const express = require("express");
const app = express();

const VALID_TOKENS = new Set(["token-abc-123", "token-xyz-789"]);

function authMiddleware(req, res, next) {
  const authHeader = req.headers.authorization;

  if (!authHeader) {
    return res.status(401).json({ error: "Missing authorization header" });
  }

  const token = authHeader.replace("Bearer ", "");

  if (!VALID_TOKENS.has(token)) {
    return res.status(401).json({ error: "Invalid token" });
  }

  req.user = { id: 1, name: "Authenticated User" };
  next();
}

app.get("/protected", authMiddleware, (req, res) => {
  res.json({ message: "Access granted", user: req.user });
});

app.get("/public", (req, res) => {
  res.json({ message: "Public endpoint" });
});

app.listen(3000);

Expected output for GET /protected without token:

{"error": "Missing authorization header"}

Expected output for GET /protected with Bearer token-abc-123:

{"message": "Access granted", "user": {"id": 1, "name": "Authenticated User"}}

JWT Authentication Middleware

JWT (JSON Web Token) authentication validates cryptographically signed tokens that contain user claims. This is the industry standard for stateless authentication.

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

const SECRET = "your-secret-key-change-in-production";

function jwtAuth(req, res, next) {
  const authHeader = req.headers.authorization;

  if (!authHeader) {
    return res.status(401).json({ error: "No token provided" });
  }

  const token = authHeader.split(" ")[1];

  try {
    const decoded = jwt.verify(token, SECRET);
    req.user = { id: decoded.userId, role: decoded.role };
    next();
  } catch (err) {
    if (err.name === "TokenExpiredError") {
      return res.status(401).json({ error: "Token expired" });
    }
    return res.status(401).json({ error: "Invalid token" });
  }
}

app.get("/profile", jwtAuth, (req, res) => {
  res.json({ profile: { id: req.user.id, role: req.user.role } });
});

app.listen(3000);

Expected output for a valid JWT token:

{"profile": {"id": 42, "role": "user"}}

Role-Based Access Control (RBAC)

RBAC middleware checks whether the authenticated user has the required role to access a resource. This enables fine-grained permission control.

function authorize(...allowedRoles) {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: "Authentication required" });
    }

    if (!allowedRoles.includes(req.user.role)) {
      return res.status(403).json({
        error: "Insufficient permissions",
        required: allowedRoles,
        yourRole: req.user.role
      });
    }

    next();
  };
}

app.get("/admin/dashboard", jwtAuth, authorize("admin"), (req, res) => {
  res.json({ dashboard: "Admin control panel" });
});

app.get("/moderator/content", jwtAuth, authorize("admin", "moderator"), (req, res) => {
  res.json({ content: "Moderated content" });
});

Expected output for a user with role "user" accessing /admin/dashboard:

{"error": "Insufficient permissions", "required": ["admin"], "yourRole": "user"}

Common Mistakes

  1. Storing tokens in local storage — Tokens in local storage are vulnerable to XSS Attacks. Use httpOnly cookies instead.

  2. Not checking token expiry — A token can be validly signed but expired. Always verify expiration before accepting a token.

  3. Exposing user data in error messages — Error messages should not reveal whether a user exists or why authentication failed.

  4. Hardcoding secrets — Never hardcode JWT secrets or API keys. Use environment variables or a secrets manager.

  5. Not handling token refresh — Short-lived tokens require a refresh mechanism. Implement a refresh token flow alongside access tokens.

Practice Questions

  1. What is the difference between authentication and authorization? Authentication verifies who you are. Authorization determines what you can do.

  2. Why should JWT tokens have an expiration time? Expiration limits the damage if a token is stolen. Short-lived tokens minimize the window of vulnerability.

  3. How do you handle token refresh in middleware? Create a separate refresh endpoint that issues new access tokens. The middleware only validates access tokens.

  4. Challenge: Implement middleware that supports both JWT and API key authentication.

function multiAuth(req, res, next) {
  if (req.headers.authorization) {
    return jwtAuth(req, res, next);
  }
  if (req.headers["x-api-key"]) {
    return apiKeyAuth(req, res, next);
  }
  res.status(401).json({ error: "Authentication required" });
}

FAQ

Should I validate JWTs in middleware or in the route handler?

Always validate in middleware. This keeps route handlers clean and ensures consistent authentication across all routes.

Can authentication middleware be bypassed?

If middleware is registered conditionally or in the wrong order, requests can bypass authentication. Always register auth middleware globally where needed.

How do I test authentication middleware?

Mock the req object with and without headers, and verify that protected routes return 401 without tokens and 200 with valid tokens.

What is the difference between Bearer tokens and Basic auth?

Bearer tokens use a token string in the Authorization header. Basic auth sends username:password encoded in base64. Bearer is preferred for APIs.

How do I logout a user with JWT?

JWT is stateless. Blacklist the token on the server side, or use short-lived tokens that expire naturally.

Mini Project

Build a complete auth middleware system with JWT validation, role-based access control, and a token refresh mechanism.

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

const SECRET = process.env.JWT_SECRET || "change-me";
const REFRESH_SECRET = process.env.REFRESH_SECRET || "change-me-too";

function auth(req, res, next) {
  const token = req.headers.authorization?.split(" ")[1];
  if (!token) return res.status(401).json({ error: "No token" });
  try {
    req.user = jwt.verify(token, SECRET);
    next();
  } catch {
    res.status(401).json({ error: "Invalid or expired token" });
  }
}

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

app.post("/login", (req, res) => {
  const accessToken = jwt.sign({ userId: 1, role: "user" }, SECRET, { expiresIn: "15m" });
  const refreshToken = jwt.sign({ userId: 1 }, REFRESH_SECRET, { expiresIn: "7d" });
  res.json({ accessToken, refreshToken });
});

app.get("/admin", auth, authorize("admin"), (req, res) => {
  res.json({ secret: "admin data" });
});

app.listen(3000);

What's Next

Now that you understand authentication middleware, explore building robust error-handling middleware for production. Then learn about validating request data in middleware.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro