How to Implement JWT Authentication from Scratch

How to Implement JWT Authentication from Scratch
JSON Web Tokens (JWT) is a popular method for securely transmitting information between parties as a JSON object. The information can be verified and trusted because it is digitally signed. In this guide, we will implement JWT authentication from scratch using Node.js and Express.
Problem
Traditional session-based authentication stores session data on the server, which can scale poorly with large numbers of users. Stateless authentication with JWTs provides a scalable solution by storing user session state on the client-side. The server only needs to verify the JWT's signature and expiration.
Solution
Key Concepts
- JWT Structure: JWTs consist of three parts: Header, Payload, and Signature. The Header specifies the algorithm used. The Payload contains claims. The Signature is used to verify the token's authenticity.
- Secret Key: Used to sign the token and verify its signature. Keep it secure.
- Expiration: Tokens should have an expiration time to reduce risk if compromised.
Implementation
First, ensure you have Node.js and npm installed. Create a new Node.js project and install the required packages:
npm init -y
npm install express jsonwebtoken dotenv
Create a .env file for environment variables:
SECRET_KEY=your_secret_key
Code
Create a file server.js and set up Express:
require('dotenv').config();
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
const users = [{ id: 1, username: 'user1', password: 'password' }];
// Authenticate user and generate JWT
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username && u.password === password);
if (!user) return res.status(401).send('Invalid credentials');
const token = jwt.sign({ id: user.id }, process.env.SECRET_KEY, { expiresIn: '1h' });
res.json({ token });
});
// Middleware to verify token
function authenticateToken(req, res, next) {
const token = req.header('Authorization')?.split(' ')[1];
if (!token) return res.sendStatus(403);
jwt.verify(token, process.env.SECRET_KEY, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
// Protected route
app.get('/protected', authenticateToken, (req, res) => {
res.send('This is a protected route');
});
app.listen(3000, () => console.log('Server running on port 3000'));
Explanation
- /login Endpoint: Authenticates user credentials and generates a JWT.
- authenticateToken Middleware: Verifies the JWT from the request headers.
- Protected Route: Only accessible with a valid JWT.
With this setup, you have a basic JWT authentication mechanism. Remember to secure your SECRET_KEY and consider additional security measures like HTTPS in production.