In this tutorial, we'll walk through creating a JWT authentication system using Express.js. JWT (JSON Web Tokens) is a powerful tool for securely transmitting information between parties, and it's widely used in modern web applications for authentication.
What is JWT Authentication?
JWT (JSON Web Token) is an open standard for securely transmitting information between parties as a JSON object. It is commonly used in web applications to authenticate users and allow them to access protected resources.
In simple terms:
- JWT provides a way to authenticate users by generating a token after they log in.
- This token can then be used to access protected resources.
- The token is signed with a secret key, which ensures that the token has not been tampered with.
In this guide, we'll build a simple Express.js application that:
- Allows users to log in and receive a JWT.
- Protects certain routes using that JWT.
Let's dive in!
What Will We Build?
We'll create a simple Express.js server with the following features:
- Login route: A POST route where users submit their username and password to receive a JWT.
- Protected route: A GET route that requires a valid JWT to access.
Prerequisites
Before we begin, ensure you have the following:
- Node.js installed on your computer (download it here).
- A basic understanding of JavaScript and Express.js.
Step 1: Setting Up the Project
Let’s start by setting up a new Node.js project.
-
Create a new directory for your project:
Open your terminal/command prompt and create a new folder:
mkdir jwt-auth-example cd jwt-auth-example
-
Initialize the Node.js project:
Run the following command to create a
package.json
file:npm init -y
-
Install the necessary dependencies:
We need three libraries:
express
: The web framework for building our server.jsonwebtoken
: A library to create and verify JWTs.dotenv
: A library to manage environment variables securely.
Install them using npm:
npm install express jsonwebtoken dotenv
Step 2: Writing the Server Code
Now, let’s write the code for the Express server.
- Create a file named
index.js
in the root of your project. - Add the following code to handle JWT authentication:
// Import necessary packages
const express = require('express');
const jwt = require('jsonwebtoken');
const dotenv = require('dotenv');
// Load environment variables from .env file
dotenv.config();
const app = express();
const port = 3000;
// Middleware to parse JSON request bodies
app.use(express.json());
// Secret key for signing JWTs (should be in .env file for security)
const SECRET_KEY = process.env.JWT_SECRET || 'your_jwt_secret_key';
// Dummy user data for demonstration (In a real app, you would query a database)
const users = [
{ id: 1, username: 'john_doe', password: 'password123' },
{ id: 2, username: 'jane_doe', password: 'securepassword' }
];
// Route for user login
app.post('/login', (req, res) => {
const { username, password } = req.body;
// Check if the user exists and if the password is correct
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ message: 'Invalid credentials' });
}
// Create a JWT token
const token = jwt.sign(
{ id: user.id, username: user.username },
SECRET_KEY,
{ expiresIn: '1h' } // Token will expire in 1 hour
);
res.json({ message: 'Login successful', token });
});
// Middleware to verify JWT token
const verifyToken = (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(403).json({ message: 'Access denied. No token provided.' });
}
jwt.verify(token, SECRET_KEY, (err, decoded) => {
if (err) {
return res.status(403).json({ message: 'Invalid token' });
}
req.user = decoded; // Attach user info to the request
next(); // Proceed to the next middleware or route handler
});
};
// Protected route that requires a valid JWT
app.get('/protected', verifyToken, (req, res) => {
res.json({ message: `Welcome ${req.user.username}, you have access to this protected route!` });
});
// Start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
Step 3: Set Up Environment Variables
For security purposes, we don't want to expose our secret key directly in the code. Instead, we’ll store it in an environment variable using the dotenv
package.
- Create a
.env
file in the root of your project. - Add the following line to the
.env
file to store your secret key:
JWT_SECRET=your_jwt_secret_key
Replace
your_jwt_secret_key
with a strong, secret key. This key will be used to sign and verify JWT tokens.
Step 4: Testing the API
1. Login Endpoint (/login
)
First, let’s test the login functionality. To log in, you need to send a POST request to the /login
route with the following body:
{
"username": "john_doe",
"password": "password123"
}
If the credentials are correct, the response will contain a JWT token:
Response:
{
"message": "Login successful",
"token": "your_jwt_token_here"
}
2. Accessing the Protected Route (/protected
)
Now that we have a token, we can access the protected route /protected
. To do this, include the token in the Authorization
header as a Bearer token.
Request:
GET http://localhost:3000/protected
Authorization: Bearer your_jwt_token_here
If the token is valid, the response will be:
{
"message": "Welcome john_doe, you have access to this protected route!"
}
If the token is invalid or missing, you’ll get an error message like:
{
"message": "Invalid token"
}
Step 5: Running the Server
To run the server, use the following command in your terminal:
node index.js
Your server will start running on http://localhost:3000