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
Folder structure:
web-app/
├─ views/
│ ├─ login.ejs
│ ├─ register.ejs
│ └─ dashboard.ejs
├─ public/
│ └─ style.css
├─ index.js
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}`));
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>");
});
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");
});
Logout part:
// Logout
app.get("/logout", (req, res) => {
res.clearCookie(auth.jwt.cookieName || "authToken");
res.redirect("/login");
});
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}`);
});
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>
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>
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>
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;
}
And we'll run our app:
node index.js
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}`));

Top comments (0)