2-how-to-build-a-secure-rest-api-with-expressjs-and-jwt-authentication.html

How to Build a Secure REST API with Express.js and JWT Authentication

In today's digital landscape, developing secure applications is paramount. One of the most common ways to build secure web applications is by creating a REST API (Representational State Transfer Application Programming Interface) with authentication mechanisms to protect user data. In this article, we will dive into building a secure REST API using Express.js, a popular Node.js framework, and JWT (JSON Web Token) for authentication. By the end, you’ll have a solid understanding of how to implement security features in your API.

What is a REST API?

A REST API is a set of rules that allows different software applications to communicate over the internet. It uses standard HTTP methods like GET, POST, PUT, and DELETE to perform operations on resources. REST APIs are stateless, meaning that each request from the client must contain all the information needed to understand and process the request.

Use Cases for REST APIs

  • Mobile Applications: REST APIs are commonly used to connect mobile apps to back-end services.
  • Web Applications: They facilitate data exchange between the front-end and back-end of web applications.
  • Microservices Architecture: REST APIs enable different microservices to communicate with each other.

Why Use JWT for Authentication?

JSON Web Tokens (JWT) provide a compact and self-contained way for securely transmitting information between parties. Here are a few reasons why JWT is a popular choice for authentication:

  • Statelessness: JWT does not require the server to store session information, making it scalable.
  • Cross-Domain Authentication: JWT can be used across different domains seamlessly.
  • Compactness: The token size is small, which improves performance.

Setting Up Your Development Environment

Before we start coding, make sure you have the following installed:

  • Node.js: Download and install from Node.js official website.
  • npm: Comes packaged with Node.js.
  • Postman: For testing your API endpoints.

Step 1: Create a New Express Application

To begin, create a new directory for your project and initialize a new Node.js application:

mkdir secure-api
cd secure-api
npm init -y

Next, install Express and other required packages:

npm install express jsonwebtoken bcryptjs body-parser dotenv

Step 2: Set Up Basic Express Server

Create a file named server.js and set up a basic Express server:

const express = require('express');
const bodyParser = require('body-parser');
const dotenv = require('dotenv');

dotenv.config();

const app = express();
const PORT = process.env.PORT || 5000;

app.use(bodyParser.json());

app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

Step 3: User Registration and Password Hashing

To handle user registration, create a new file named auth.js and set up routes for registering users:

const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

const router = express.Router();
const users = []; // This will temporarily hold user data

// User Registration
router.post('/register', async (req, res) => {
    const { username, password } = req.body;

    // Check if user already exists
    const existingUser = users.find(user => user.username === username);
    if (existingUser) {
        return res.status(400).send('User already exists');
    }

    // Hash the password
    const hashedPassword = await bcrypt.hash(password, 10);

    // Save user
    users.push({ username, password: hashedPassword });
    res.status(201).send('User registered successfully');
});

module.exports = router;

In your server.js, import this router:

const authRoutes = require('./auth');
app.use('/api/auth', authRoutes);

Step 4: Implementing JWT Authentication

Now, let’s add a login route that generates a JWT upon successful authentication:

// User Login
router.post('/login', async (req, res) => {
    const { username, password } = req.body;
    const user = users.find(user => user.username === username);

    if (!user || !(await bcrypt.compare(password, user.password))) {
        return res.status(401).send('Invalid username or password');
    }

    // Generate JWT
    const token = jwt.sign({ username: user.username }, process.env.JWT_SECRET, { expiresIn: '1h' });
    res.json({ token });
});

Step 5: Protecting Routes with Middleware

To secure your API, create middleware that verifies the JWT for protected routes:

const authenticateJWT = (req, res, next) => {
    const token = req.headers['authorization']?.split(' ')[1];
    if (!token) return res.sendStatus(403);

    jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
        if (err) return res.sendStatus(403);
        req.user = user;
        next();
    });
};

// Protected Route
router.get('/protected', authenticateJWT, (req, res) => {
    res.send('This is a protected route');
});

Step 6: Testing Your API

Using Postman, you can now test your API:

  1. Register a User: Send a POST request to http://localhost:5000/api/auth/register with a JSON body: json { "username": "testuser", "password": "password123" }

  2. Login to Get JWT: Send a POST request to http://localhost:5000/api/auth/login with the same JSON structure.

  3. Access Protected Route: Use the token received in the previous step to access http://localhost:5000/api/auth/protected by adding it in the Authorization header as Bearer <your_token>.

Conclusion

Building a secure REST API with Express.js and JWT authentication involves several steps: setting up your server, handling user registration and login, and protecting routes with middleware. By following the steps outlined in this article, you've created a basic but secure API that can be extended with more features and functionalities.

As you continue developing your API, consider implementing features like role-based access control, logging, and input validation for improved security and usability. Happy coding!

SR
Syed
Rizwan

About the Author

Syed Rizwan is a Machine Learning Engineer with 5 years of experience in AI, IoT, and Industrial Automation.