DEV Community

Cover image for How to protect Express routes in Node.js
Jahongir Sobirov
Jahongir Sobirov

Posted on

How to protect Express routes in Node.js

Securing routes is critical in modern web applications. In this tutorial, we’ll learn how to use auth-verify, a powerful Node.js authentication library, to protect Express routes using JWT middleware.
Firstly we need to install all essential packages:

npm install express ejs body-parser auth-verify path
Enter fullscreen mode Exit fullscreen mode

Folder structure:

web-app/
├─ views/
│  ├─ login.ejs
│  ├─ register.ejs
│  └─ dashboard.ejs
├─ public/
│  └─ style.css
├─ index.js
Enter fullscreen mode Exit fullscreen mode

So let's make our index.js:

const express = require("express");
const bodyParser = require("body-parser");
const AuthVerify = require("auth-verify");
const path = require("path");

const app = express();
const PORT = 3000;

// Serve CSS
app.use(express.static(path.join(__dirname, "public")));

// Middlewares
app.use(bodyParser.urlencoded({ extended: true }));
app.set("view engine", "ejs");

// Initialize AuthVerify
const auth = new AuthVerify({ jwtSecret: "supersecret", storeTokens: "memory" });

// In-memory "database"
const users = []; // { username, password, role }

// Start server
app.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));
Enter fullscreen mode Exit fullscreen mode

Making routes

We'll make pages and routes for registration, login, logoutm, profile and admin-profile
And so let's make register page:

// Routes

// Register page
app.get("/register", (req, res) => res.render("register"));

// Handle registration
app.post("/register", async (req, res) => {
  const { username, password, role } = req.body;
  if (users.find(u => u.username === username)) {
    return res.send("User already exists. <a href='/register'>Try again</a>");
  }

  users.push({ username, password, role });
  res.send("Registered! <a href='/login'>Login</a>");
});
Enter fullscreen mode Exit fullscreen mode

Let's make login route:

// Login page
app.get("/login", (req, res) => res.render("login"));

// Handle login
app.post("/login", async (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username && u.password === password);
  if (!user) return res.send("Invalid credentials. <a href='/login'>Try again</a>");

  // Sign JWT and set cookie automatically (auth-verify handles cookies)
  await auth.jwt.sign({ username: user.username, role: user.role }, "1h", { res });
  res.redirect("/dashboard");
});
Enter fullscreen mode Exit fullscreen mode

Logout part:

// Logout
app.get("/logout", (req, res) => {
  res.clearCookie(auth.jwt.cookieName || "authToken");
  res.redirect("/login");
});
Enter fullscreen mode Exit fullscreen mode

And now our main task is protect profile and admin-only route with middleware:

// Protected dashboard
app.get("/dashboard", auth.jwt.protect(), (req, res) => {
  res.render("dashboard", { user: req.user });
});

// Admin-only page
app.get("/admin", auth.jwt.protect({ requiredRole: "admin" }), (req, res) => {
  res.send(`Hello Admin ${req.user.username}`);
});
Enter fullscreen mode Exit fullscreen mode

EJS templates

We'll make registration page:
views/register.ejs

<!DOCTYPE html>
<html>
<head>
    <title>Register</title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
    <div class="container">
        <h1>Register</h1>
        <form action="/register" method="post">
            <input type="text" name="username" placeholder="Username" required/>
            <input type="password" name="password" placeholder="Password" required/>
            <select name="role">
                <option value="user">User</option>
                <option value="admin">Admin</option>
            </select>
            <button type="submit">Register</button>
        </form>
        <a href="/login">Already have an account? Login</a>
    </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Login page also:
views/login.ejs:

<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
    <div class="container">
        <h1>Login</h1>
        <form action="/login" method="post">
            <input type="text" name="username" placeholder="Username" required/>
            <input type="password" name="password" placeholder="Password" required/>
            <button type="submit">Login</button>
        </form>
        <a href="/register">Don't have an account? Register</a>
    </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Profile page:
views/dashboard.ejs:

<!DOCTYPE html>
<html>
<head>
    <title>Dashboard</title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
    <div class="container">
        <h1>Dashboard</h1>
        <h2>Welcome <%= user.username %>!</h2>
        <p>Role: <%= user.role %></p>
        <a href="/logout">Logout</a>
    </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

CSS styling

public/style.css

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background: linear-gradient(120deg, #84fab0, #8fd3f4);
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}

.container {
    background: white;
    padding: 40px 50px;
    border-radius: 12px;
    box-shadow: 0 10px 25px rgba(0,0,0,0.2);
    width: 350px;
    text-align: center;
}

h1 {
    margin-bottom: 20px;
    color: #333;
}

input, select {
    width: 100%;
    padding: 12px 10px;
    margin: 10px 0;
    border: 1px solid #ccc;
    border-radius: 8px;
}

button {
    width: 100%;
    padding: 12px;
    background: #4facfe;
    background: linear-gradient(to right, #00f2fe, #4facfe);
    border: none;
    color: white;
    font-weight: bold;
    border-radius: 8px;
    cursor: pointer;
    margin-top: 10px;
    transition: 0.3s ease;
}

button:hover {
    opacity: 0.9;
}

a {
    display: inline-block;
    margin-top: 15px;
    color: #4facfe;
    text-decoration: none;
}
Enter fullscreen mode Exit fullscreen mode

And we'll run our app:

node index.js
Enter fullscreen mode Exit fullscreen mode

And the result:

And full index.js:

const express = require("express");
const bodyParser = require("body-parser");
const AuthVerify = require("auth-verify");
const path = require("path");

const app = express();
const PORT = 3000;

// Serve CSS
app.use(express.static(path.join(__dirname, "public")));

// Middlewares
app.use(bodyParser.urlencoded({ extended: true }));
app.set("view engine", "ejs");

// Initialize AuthVerify
const auth = new AuthVerify({ jwtSecret: "supersecret", storeTokens: "memory" });

// In-memory "database"
const users = []; // { username, password, role }

// Routes

// Register page
app.get("/register", (req, res) => res.render("register"));

// Handle registration
app.post("/register", async (req, res) => {
  const { username, password, role } = req.body;
  if (users.find(u => u.username === username)) {
    return res.send("User already exists. <a href='/register'>Try again</a>");
  }

  users.push({ username, password, role });
  res.send("Registered! <a href='/login'>Login</a>");
});

// Login page
app.get("/login", (req, res) => res.render("login"));

// Handle login
app.post("/login", async (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username && u.password === password);
  if (!user) return res.send("Invalid credentials. <a href='/login'>Try again</a>");

  // Sign JWT and set cookie automatically (auth-verify handles cookies)
  await auth.jwt.sign({ username: user.username, role: user.role }, "1h", { res });
  res.redirect("/dashboard");
});

// Logout
app.get("/logout", (req, res) => {
  res.clearCookie(auth.jwt.cookieName || "authToken");
  res.redirect("/login");
});

// Protected dashboard
app.get("/dashboard", auth.jwt.protect(), (req, res) => {
  res.render("dashboard", { user: req.user });
});

// Admin-only page
app.get("/admin", auth.jwt.protect({ requiredRole: "admin" }), (req, res) => {
  res.send(`Hello Admin ${req.user.username}`);
});

// Home page
app.get("/", (req, res) => res.redirect("/login"));

// Start server
app.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));
Enter fullscreen mode Exit fullscreen mode

Top comments (0)