🔐 Mastering Authentication in Node.js with JWT and bcrypt
A Beginner-Friendly Guide to Password Hashing, JWT, and Securing Routes in Node.js

I am an Enthusiastic and self-motivated web-Developer . Currently i am learning to build end-to-end web-apps.
1. What is Authentication?
Definition
Authentication is the process of verifying the identity of a user, system, or device before granting access to resources.
Why is Authentication Needed?
Security – Prevents unauthorized access to sensitive data.
User Identification – Ensures that only the right person can access personal information.
Access Control – Restricts certain operations based on user roles.
Data Integrity – Ensures data is accessed and modified only by authenticated users.
2. Types of Authentication
1. Password-Based Authentication
- The most common method where users enter a username & password to log in.
2. Token-Based Authentication (JWT)
Instead of storing user sessions on the server, a token is generated and sent to the client.
The client includes this token in every request for authentication.
3. Multi-Factor Authentication (MFA)
- Requires additional verification steps (e.g., OTP via email/SMS).
3. Password Hashing
Why is Password Hashing Important?
Prevents Plaintext Password Storage – Storing passwords as they are (plaintext) is dangerous because any security breach will expose all user credentials.
Enhances Security – Even if the database is compromised, the attacker cannot retrieve original passwords easily.
Protects Against Brute Force Attacks – Hashing makes it computationally expensive to guess passwords.
Ensures Compliance – Security standards (such as OWASP) recommend always hashing passwords. To securely store user passwords, we will use bcrypt, a widely used password-hashing library.
How bcrypt Works?
Salting – Adds a random value (salt) to the password before hashing. This prevents attackers from using precomputed hash tables (rainbow tables) to crack passwords.
Salt Rounds – The number of rounds used to generate the salt. A higher number increases security but also the processing time. Usually,
10rounds are considered a good balance between security and performance.Hashing – Converts the salted password into an irreversible cryptographic string.
Comparison – Allows securely checking passwords during login by comparing the stored hash with the provided password.
Steps to Implement bcrypt in Node.js
1. Install bcrypt
npm install bcrypt
2. Update the Signup Function with Password Hashing
const bcrypt = require('bcrypt');
app.post('/signup', async (req, res) => {
const { username, email, password } = req.body;
// Generate a salt and hash the password
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
const newUser = new User({ username, email, password: hashedPassword });
await newUser.save();
res.status(201).json({ message: 'User registered successfully' });
});
4. What is JWT (JSON Web Token)?
JWT (JSON Web Token) is a self-contained, secure token used for authentication and data exchange between a client and a server.
Why Use JWT?
Stateless Authentication – No need to store session data on the server.
Compact – Small JSON-based token sent via HTTP headers.
Secure – Digitally signed to prevent tampering.
Cross-Domain Usage – Can be used for Single Sign-On (SSO).
5. How JWT Works?
Step-by-Step JWT Authentication Flow
1️⃣ User Logs In – Provides email and password.
2️⃣ Server Verifies Credentials – If valid, the server generates a JWT.
3️⃣ Token Sent to Client – Client stores the token (in localStorage or sessionStorage).
4️⃣ Client Sends Token with Requests – Token is sent in the Authorization header as Bearer Token.
5️⃣ Server Verifies Token – If valid, access is granted; otherwise, access is denied.
6. Structure of a JWT
A JWT consists of three parts, separated by dots (.):
Header.Payload.Signature
1. Header (Metadata about Token)
The header specifies the type of token (JWT) and the algorithm used for signing (e.g., HS256).
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload (User Data & Claims)
The payload contains user details and other claims.
{
"userId": "123456",
"role": "admin",
"iat": 1631234560,
"exp": 1631238160
}
3. Signature (Verification Mechanism)
The signature ensures that the token has not been modified.
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secretKey
)
7. What are HTTP Headers? Why Are They Important?
Definition:
HTTP headers are key-value pairs sent in HTTP requests and responses.
Why Are Headers Used?
Authentication – Includes JWT in the Authorization header.
Security – Ensures secure communication.
Content-Type – Defines the format of the request (e.g.,
application/json).
Authorization Header with Bearer Token
When making a request, the JWT is included in the Authorization header like this:
Authorization: Bearer <JWT_TOKEN>
8. Implementation of Hashing Password & Generating JWT
const express = require("express");
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.json());
const JWT_SECRET = "masai-secret"; // move to .env in real projects
// MongoDB connection
mongoose.connect("mongodb://127.0.0.1:27017/auth_demo");
// User Schema
const userSchema = new mongoose.Schema({
name: String,
email: { type: String, unique: true },
password: String,
});
const User = mongoose.model("User", userSchema);
// Signup Route
app.post("/signup", async (req, res) => {
const { name, email, password } = req.body;
try {
const hashedPassword = await bcrypt.hash(password, 8);
const user = new User({ name, email, password: hashedPassword });
await user.save();
res.status(201).json({ message: "User registered successfully!" });
} catch (err) {
res.status(400).json({ error: "Signup failed", details: err.message });
}
});
// Login Route
app.post("/login", async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email });
if (!user) return res.status(404).json({ error: "User not found" });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(401).json({ error: "Invalid credentials" });
const token = jwt.sign({ userId: user._id }, JWT_SECRET, { expiresIn: "7d" });
res.json({ message: "Login successful", token });
} catch (err) {
res.status(500).json({ error: "Login failed", details: err.message });
}
});
app.listen(8080, () => {
console.log("Server running on port 8080");
});
9. Protecting Routes Using JWT Middleware
Middleware to Validate JWT
const authMiddleware = (req, res, next) => {
const authHeader = req.header('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(403).json({ message: 'Access denied' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, SECRET_KEY);
req.user = decoded; // Attach user details to the request
next();
} catch (err) {
res.status(401).json({ message: 'Invalid token' });
}
};
// Protected Route Example
app.get('/dashboard', authMiddleware, (req, res) => {
res.json({ message: 'Welcome to the dashboard!', user: req.user });
});
10. Conclusion
Authentication ensures that only authorized users can access resources.
JWT is a compact and stateless way of handling authentication.
The Bearer Token method is used to send JWTs in the Authorization header.
Signup, Login, and JWT middleware were implemented using Node.js and Express.
The authentication flow follows secure best practices to ensure data integrity.



