3-creating-a-secure-jwt-authentication-system-in-a-nodejs-express-app.html

Creating a Secure JWT Authentication System in a Node.js Express App

In today’s digital landscape, building secure web applications is paramount. One of the essential components of this security is authentication. JSON Web Tokens (JWT) provide a robust method for handling authentication in a stateless manner, making it particularly suitable for modern applications. This article will guide you through creating a secure JWT authentication system in a Node.js Express application, covering definitions, use cases, and practical coding examples.

What is JWT?

JWT, or JSON Web Token, is an open standard (RFC 7519) that defines a compact, self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with HMAC algorithm) or a public/private key pair using RSA or ECDSA.

Use Cases of JWT

  1. Single Sign-On (SSO): JWTs can help in managing user sessions across multiple applications.
  2. API Authentication: They are widely used to authenticate users in RESTful APIs.
  3. Mobile Applications: JWTs are convenient for mobile applications to authenticate users without requiring them to store session data.

Setting Up Your Node.js Environment

Before diving into the code, ensure you have Node.js and npm installed on your machine. If you haven’t set up a basic Node.js Express app, follow these steps:

  1. Create a new directory for your project: bash mkdir jwt-auth-example cd jwt-auth-example

  2. Initialize a new Node.js project: bash npm init -y

  3. Install required packages: bash npm install express jsonwebtoken bcryptjs body-parser dotenv

Building the Authentication System

Step 1: Set Up Basic Express Server

Create a new file called server.js and set up your Express server:

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

dotenv.config();

const app = express();
app.use(bodyParser.json());

const PORT = process.env.PORT || 3000;

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

Step 2: Create User Registration

To authenticate users, we first need to register them. We'll create a simple in-memory array to hold user data.

const users = []; // This will hold our users for demonstration purposes

app.post('/register', (req, res) => {
    const { username, password } = req.body;

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

    // Hash the password before storing
    const hashedPassword = bcrypt.hashSync(password, 8);

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

Step 3: Implement User Login

Now, let’s create a login endpoint that generates and returns a JWT:

app.post('/login', (req, res) => {
    const { username, password } = req.body;

    // Find user
    const user = users.find(u => u.username === username);
    if (!user) {
        return res.status(404).send('User not found');
    }

    // Validate password
    const passwordIsValid = bcrypt.compareSync(password, user.password);
    if (!passwordIsValid) {
        return res.status(401).send({ auth: false, token: null });
    }

    // Generate JWT
    const token = jwt.sign({ id: user.username }, process.env.JWT_SECRET, {
        expiresIn: 86400 // expires in 24 hours
    });

    res.status(200).send({ auth: true, token });
});

Step 4: Protecting Routes with JWT

To ensure that only authenticated users can access certain routes, we need to create a middleware function that verifies the JWT:

const verifyToken = (req, res, next) => {
    const token = req.headers['x-access-token'];
    if (!token) return res.status(403).send('No token provided.');

    jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
        if (err) return res.status(500).send('Failed to authenticate token.');

        // Save user ID for use in other routes
        req.userId = decoded.id;
        next();
    });
};

// Example protected route
app.get('/me', verifyToken, (req, res) => {
    const user = users.find(u => u.username === req.userId);
    res.status(200).send(user);
});

Step 5: Testing Your API

Now that your JWT authentication system is in place, you can test it using Postman or any API testing tool:

  1. Register a new user by sending a POST request to http://localhost:3000/register with a JSON body containing username and password.
  2. Login with the same credentials at http://localhost:3000/login to receive a JWT.
  3. Access the protected route by sending a GET request to http://localhost:3000/me with the token in the headers.

Conclusion

Creating a secure JWT authentication system in a Node.js Express application is straightforward and enhances your app's security significantly. By following the steps outlined above, you can implement user registration, login, and protect your routes effectively. Remember to keep your JWT secret secure and consider additional security measures, such as token expiration and refresh tokens, for a more robust system.

With this foundational knowledge, you can further enhance your application by adding features like user roles, permissions, and even integrating with external identity providers. 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.