Adding Authentication to Node.js API with JWT

Adding Authentication to Node.js API with JWT

Created by eneaslari 21/9/2023

nodejs

Previous articles:

How to Make API using NodeJS and Express

Enhancing Our Node.js API: Hot Reloading with Nodemon, Bundling with Webpack & Using ES6

1. JWT Overview

JSON Web Tokens (JWT) offer a compact and self-contained way to securely transmit information between parties as a JSON object. In authentication, when the user logs in using their credentials, a server-side application provides a signed token to the client. The client then uses this token to access protected routes or resources.

2. Setting up JWT Authentication

Step 1: Install the necessary packages:

npm install jsonwebtoken bcryptjs
  • jsonwebtoken - For generating and verifying JWTs.
  • bcryptjs - For hashing and checking user passwords.

Step 2: Set up a User Model with Password Hashing (models/user.js):

If not already set, add a password field to the user schema. Then, implement password hashing using bcrypt:

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
    // ... other fields ...
    password: {
        type: String,
        required: true,
    },
});

// Hash password before saving
userSchema.pre('save', async function(next) {
    if (this.isModified('password')) {
        this.password = await bcrypt.hash(this.password, 10);
    }
    next();
});

module.exports = mongoose.model('User', userSchema);

Step 3: Implement Signup and Login Routes:

  • Signup:

    const bcrypt = require('bcryptjs');
    const User = require('../models/user');
    
    exports.signup = async (req, res) => {
        const user = new User(req.body);
        await user.save();
        res.status(201).json({ message: "User registered successfully!" });
    };
    
  • Login:

    const jwt = require('jsonwebtoken');
    
    exports.login = async (req, res) => {
        const { email, password } = req.body;
        const user = await User.findOne({ email });
    
        if (!user || !await bcrypt.compare(password, user.password)) {
            return res.status(401).json({ error: "Invalid email or password" });
        }
    
        const token = jwt.sign({ userId: user.id }, 'YOUR_SECRET_KEY', { expiresIn: '1h' });
    
        res.json({ token });
    };
    

Remember to replace 'YOUR_SECRET_KEY' with your actual secret key.

3. Protecting Routes

To protect routes, we create a middleware that verifies the JWT:

Middleware (middlewares/auth.js):

const jwt = require('jsonwebtoken');

exports.verifyToken = (req, res, next) => {
    const token = req.headers['authorization'];

    if (!token) {
        return res.status(403).json({ error: 'No token provided' });
    }

    jwt.verify(token, 'YOUR_SECRET_KEY', (err, decoded) => {
        if (err) {
            return res.status(401).json({ error: 'Unauthorized' });
        }
        req.userId = decoded.userId;
        next();
    });
};

Now, apply this middleware to any route you want to protect. For instance, to protect a route that fetches user data:

const express = require('express');
const { verifyToken } = require('../middlewares/auth');

const router = express.Router();

router.get('/protectedRoute', verifyToken, (req, res) => {
    // Your protected route logic here
});

module.exports = router;

With the setup above, only authenticated users with valid JWTs can access the protected routes. Remember to handle security considerations, such as storing JWTs securely in frontend applications, using HTTPS, and frequently rotating your secret keys.

With this, your Node.js and Express API now has a robust authentication mechanism using JWTs and can protect routes from unauthorized access.

More to read


Designing Fun and Engaging Levels for My Space-Themed Match-3 Game
8/2/2025

Designing levels for my space-themed match-3 game has been a journey of balancing **fun, challenge, and variety**. In this article, I share my experience with **creating engaging puzzles, managing difficulty, and keeping gameplay fresh**. From playtesting to strategic layouts, these insights shape my approach to making levels that players will love. 🚀

From Burnout to Balance: Rediscovering Energy in Game Dev, Fitness, and Life
7/2/2025

A reflection on how burnout, illness, and lost momentum forced me to rethink my approach to productivity, motivation, and balance. Now, I’m refactoring my life—one habit at a time—to rebuild my energy without falling into the same cycle again.

New Year, New Code: My Developer Resolutions for the Year
29/12/2024

In this blog post, I share my New Year's resolutions as a developer: posting more about my work on social media, writing more articles, and finally finishing and publishing a game. Plus, I offer practical tips on setting realistic and achievable goals to help fellow developers kick off the year with purpose!

Bad Practices in JavaScript: Common Pitfalls and How to Avoid Them 🚫
28/12/2024

JavaScript is like the Swiss Army knife of web development—it’s everywhere, from tiny website features to massive, complex web apps. But with great power comes... well, the chance to make a mess! Its flexibility and loose rules make it super easy to use, but also super easy to misuse.