How to Implement a Login System with JWT in Node.js
In today's digital world, securing user data is paramount. Implementing a login system is one of the first steps in protecting sensitive information. JSON Web Tokens (JWT) have emerged as a popular method for handling authentication in web applications. In this article, we’ll walk through the process of creating a login system using JWT in Node.js, covering everything from setup to deployment.
What is JWT?
JSON Web Tokens (JWT) are an open standard (RFC 7519) that defines a compact and 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 are commonly used for authentication and information exchange.
Key Features of JWT
- Compact: JWTs can be sent via URL, POST parameters, or inside an HTTP header.
- Self-contained: They contain all the necessary information about the user, eliminating the need for database lookups.
- Secure: Can be signed using a secret (HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Use Cases for JWT
- Authentication: Instead of sending username and password with each request, send a JWT token.
- Information Exchange: Securely transmit information between parties.
- Single Sign-On (SSO): Allow users to authenticate once and gain access to multiple applications.
Setting Up Your Node.js Project
To start, ensure you have Node.js and npm installed. Create a new directory for your project and initialize it.
mkdir jwt-login-system
cd jwt-login-system
npm init -y
Next, install the necessary packages:
npm install express jsonwebtoken bcryptjs dotenv
- Express: A web framework for Node.js.
- jsonwebtoken: A library to work with JWT.
- bcryptjs: For hashing passwords.
- dotenv: To manage environment variables.
Creating the Basic Server
Create a new file named server.js
and set up a basic Express server.
// server.js
const express = require('express');
const dotenv = require('dotenv');
dotenv.config();
const app = express();
const PORT = process.env.PORT || 5000;
app.use(express.json());
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
User Registration
Next, let's implement user registration, where we will hash the password before storing it.
User Model
For simplicity, we'll use an in-memory array to store users instead of a database.
// Mock database
let users = [];
Registration Endpoint
Add a registration endpoint to server.js
:
app.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);
// Store the user
users.push({ username, password: hashedPassword });
res.status(201).send('User registered successfully');
});
User Login
Now let's create a login endpoint that generates a JWT token upon successful authentication.
Login Endpoint
Add the following code to handle user login:
app.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 });
});
Securing Endpoints with JWT
Now we need to create middleware to protect certain routes by verifying the JWT.
Middleware for Authentication
Add the following middleware function:
function authenticateToken(req, res, next) {
const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
Protected Route
Create a protected route that requires a valid JWT:
app.get('/protected', authenticateToken, (req, res) => {
res.send(`Hello ${req.user.username}, you have access to this protected route`);
});
Testing the API
You can use tools like Postman or Insomnia to test your API:
- Register a new user by sending a POST request to
/register
. - Login to receive a JWT by sending a POST request to
/login
. - Access the protected route by including the JWT in the
Authorization
header.
Conclusion
Implementing a JWT-based login system in Node.js is a powerful way to secure your web applications. With the steps outlined in this guide, you should have a reliable authentication system that can easily be extended and integrated into your projects.
JWTs not only enhance security but also improve user experience by allowing session persistence without the need for constant database checks. As you build more complex applications, consider incorporating additional features such as token expiration, refresh tokens, and more robust user management.
Happy coding!