Post thumbnail
WEB DEVELOPMENT

Building Secure Authentication by Setting Up JWT in a Node.js App

By Poonam Chauhan

Building a secure and scalable authentication system doesn’t have to be complicated. What if you could eliminate the need for managing server-side sessions while keeping user sessions lightweight and secure?

Enter JSON Web Tokens (JWT) — a fast, stateless, and reliable solution for user authentication. JWTs are ideal for modern web applications, from single-page apps to complex microservices architectures.

In this guide, you’ll learn how to set up JWT in a Node.js application from scratch & create a secure login system that avoids server-side session storage.

Table of contents


  1. Step 1: Project Setup – Let’s Build the Foundation
    • 1: Initialize a Node.js Project
    • 2: Install the Necessary Packages
  2. Step 2: Building the Authentication Route
    • 1: Set Up the Express Server
    • 2: Defining the Login Route
    • What’s a JWT?
  3. Step 3: Testing the Login Route
  4. Step 4: Using the JWT for Protected Routes
    • 1: Create a Middleware to Verify the JWT
    • 2: Use the Middleware in the /protected Route
  5. Benefits of This Approach
    • Next Steps
  6. Wrapping Up
  7. Frequently Asked Questions
    • What is the role of JWT in a Node.js app?
    • How can I handle JWT expiration in my app?
    • Is JWT the best choice for every authentication scenario?

Step 1: Project Setup – Let’s Build the Foundation

Before we jump into the coding magic, let’s set up our project.

1.1: Initialize a Node.js Project

First, create a new project directory and initialize a Node.js project using npm. Don’t worry; it’s a one-liner to get started!

mkdir jwt-auth-app
cd jwt-auth-app
npm init –y

This creates a package.json file that will keep track of all your dependencies.

1.2: Install the Necessary Packages

Now, let’s install the key packages we’ll need:

  • express: To build our server and handle routing.
  • JSON web token: To create and verify JWTs.

Run this command to install both:

npm install express jsonwebtoken

With the dependencies set up, we’re ready to write some code!

Step 2: Building the Authentication Route

2.1: Set Up the Express Server

Create a new file called index.js. This is where the magic begins. We’ll start by setting up a basic Express server that listens on a port and can handle incoming requests.

Here’s the code for that:

const express = require('express');
const app = express();


// Middleware to parse JSON bodies
app.use(express.json());


const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

With this simple server in place, we’re ready to add our authentication route.

2.2: Defining the Login Route

Now, let’s add the /login route, which will authenticate the user based on their credentials and generate a JWT. For simplicity, we’ll use hardcoded values for username and password. But in a real-world application, you would check against a database.

Here’s the code for the login route:

const jwt = require('jsonwebtoken');
const SECRET_KEY = 'your_secret_key';  // Keep this secret in production!


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


  // Hardcoded credentials (replace this with actual database checks in production)
  if (username === 'user' && password === 'password') {
    // Create a JWT with user info and an expiration time of 1 hour
    const token = jwt.sign({ username }, SECRET_KEY, { expiresIn: '1h' });


    // Send the token back to the user
    res.json({ token });
  } else {
    res.status(401).send('Unauthorized');
  }
});

Here’s what happens when a user hits this /login route:

  • We check if the provided username and password match our hardcoded credentials (‘user’ and ‘password’).
  • If they match, we generate a JWT using the jwt.sign() method. The token will contain the user’s username and an expiration time of 1 hour.
  • This token is returned to the user as a JSON response.

What’s a JWT?

Think of the JWT as a secure passport. It’s a compact string that contains all the necessary information to verify a user’s identity. Once generated, the user can use this token to access protected routes within the app without needing to log in again — as long as the token hasn’t expired!

Step 3: Testing the Login Route

Now that we have our login route set up, it’s time to test it. You can use Postman (or any other API testing tool) to send a POST request to /login with a username and password in the body.

Here’s what you do:

  1. Open Postman.
  2. Set the request method to POST.
  3. Enter http://localhost:3000/login as the request URL.
  4. In the body, add the following JSON data:
{
"username": "user",
"password": "password"
}

Click Send, and if your credentials match the hardcoded ones, you’ll receive a response like this:

{
"token": "your.jwt.token.here"
}

This token is your ticket to access protected resources in your app!

MDN

Step 4: Using the JWT for Protected Routes

Once we have our JWT, we can use it to authenticate the user for future requests. For example, let’s create a protected route that can only be accessed if the user provides a valid JWT.

4.1: Create a Middleware to Verify the JWT

Instead of verifying the JWT in each route, we can create a middleware that will handle the token verification. This way, we can reuse the middleware for any route that requires authentication.

Here’s how you can structure the middleware:

// Middleware to verify JWT
const jwt = require('jsonwebtoken');
const SECRET_KEY = 'your_secret_key'; // Make sure to use an environment variable in production!


function verifyToken(req, res, next) {
  const token = req.header('Authorization')?.replace('Bearer ', ''); // Get token from Authorization header
  
  if (!token) {
    return res.status(401).send('Access Denied: No token provided');
  }


  try {
    // Verify the token using jwt.verify and decode it
    const decoded = jwt.verify(token, SECRET_KEY);
    
    // Attach the decoded user info to the request object for use in subsequent route handlers
    req.user = decoded;
    
    // Proceed to the next middleware or route handler
    next();
  } catch (error) {
    res.status(400).send('Invalid Token');
  }
}


module.exports = verifyToken; // Export the middleware

4.2: Use the Middleware in the /protected Route

Now that you have the verifyToken middleware, use it in the /protected route. This will ensure that only authenticated users can access the protected resource.

Here’s how to modify your code:

const verifyToken = require('./verifyToken'); // Import the middleware


// Protected route using the verifyToken middleware
app.get('/protected', verifyToken, (req, res) => {
  // If we reached this point, it means the token is valid
  res.send(`Welcome to the protected route, ${req.user.username}!`);
});


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

Step 5: Testing the Protected Route

To test the /protected route, follow these steps:

Login: Send a POST request to /login with the credentials in the request body:

{
  "username": "user",
  "password": "password"
}

If the credentials are valid, you will receive a JWT token in the response:

{
  "token": "your.jwt.token.here"
}

Access Protected Route: To access the /protected route, send a GET request to /protected with the token in the Authorization header as follows:

Authorization: Bearer your.jwt.token.here

If the token is valid, the response will be:

Welcome to the protected route, user!

Also Explore: Exploring the New Array and Object Methods in JavaScript

Benefits of This Approach

  • Code Reusability: The verifyToken middleware can be reused across all routes that require authentication, making your code DRY (Don’t Repeat Yourself).
  • Security: The JWT verification is centralized in one place. If you need to change the verification logic, you can do it in the middleware rather than in each route.
  • Clarity: By using middleware, you can easily separate concerns — authentication handling is in the middleware, while route logic remains focused on the actual functionality.

Next Steps

  • Store Secret Keys Securely: Always use environment variables for sensitive information like the SECRET_KEY. You can use packages like dotenv to load them from .env files in development.
  • Refresh Tokens: If you’d like users to remain logged in for a longer period, you can implement refresh tokens, which are securely stored and used to get a new access token when the old one expires.

JWTs are a crucial part of modern web security, and by implementing them in your application, you’re taking a significant step toward building a robust and scalable system.

Unlock your potential as a Java Full-Stack Developer with our comprehensive Java Full-Stack development course! Dive deep into the world of Java, mastering front-end and back-end development to build powerful, dynamic web applications. Gain hands-on experience with essential tools and frameworks like Spring Boot, Hibernate, Angular, and React, all while learning best practices for performance optimization and scalable coding. Start your journey today and become the all-in-one developer every company is searching for!

Wrapping Up

By implementing JWT in your Node.js application, you’ve built a secure, scalable authentication system that’s lightweight and efficient. With reusable middleware and protected routes, your app is now equipped to handle user authentication with modern best practices.

Take it further by securing sensitive keys, adding refresh tokens, and exploring advanced authentication features to enhance your system’s robustness. You’re now one step closer to building secure and scalable web applications!

Frequently Asked Questions

1. What is the role of JWT in a Node.js app?

JWTs play a crucial role in enabling secure and stateless authentication in Node.js apps. Instead of maintaining server-side sessions, JWTs store user information in a compact signed token that the client sends with each request. This reduces server overhead and allows scalable authentication systems.

2. How can I handle JWT expiration in my app?

When a JWT expires, you can prompt the user to log in again or use refresh tokens to generate a new JWT. Refresh tokens are long-lived and allow for seamless re-authentication without asking users to log in repeatedly.

MDN

3. Is JWT the best choice for every authentication scenario?

JWT is excellent for stateless authentication in APIs and microservices. However, for applications requiring high security or stateful interactions, traditional session-based authentication may be a better fit. Choose based on your app’s requirements.

Career transition

Did you enjoy this article?

Schedule 1:1 free counselling

Similar Articles

Loading...
Share logo Copy link
Power Packed Webinars
Free Webinar Icon
Power Packed Webinars
Subscribe now for FREE! 🔔
close
Webinar ad
Table of contents Table of contents
Table of contents Articles
Close button

  1. Step 1: Project Setup – Let’s Build the Foundation
    • 1: Initialize a Node.js Project
    • 2: Install the Necessary Packages
  2. Step 2: Building the Authentication Route
    • 1: Set Up the Express Server
    • 2: Defining the Login Route
    • What’s a JWT?
  3. Step 3: Testing the Login Route
  4. Step 4: Using the JWT for Protected Routes
    • 1: Create a Middleware to Verify the JWT
    • 2: Use the Middleware in the /protected Route
  5. Benefits of This Approach
    • Next Steps
  6. Wrapping Up
  7. Frequently Asked Questions
    • What is the role of JWT in a Node.js app?
    • How can I handle JWT expiration in my app?
    • Is JWT the best choice for every authentication scenario?