In any website or web application, ensuring that users are who they claim to be and protecting sensitive data from unauthorized access is crucial . One popular method for implementing authentication in Node.js is using JSON Web Tokens (JWT) and custom middleware. In this article, lets explore how to create a middleware that authenticates users using JWT.
What is JWT?
JWT (JSON Web Token) is an open standard for securely transmitting information between parties as a JSON object. This information is digitally signed, ensuring its integrity and authenticity. JWT is commonly used for authentication and authorization purposes.
A JWT is composed of three parts:
- Header: Contains metadata about the token, such as the type of token (JWT) and the signing algorithm used (e.g., HMAC SHA256).
- Payload: Contains the claims, which are statements about an entity (typically, the user) and additional data. There are three types of claims: registered, public, and private.
- Registered claims: Predefined claims like
iss
(issuer),exp
(expiration time),sub
(subject), andaud
(audience). - Public claims: Custom claims defined, such as user roles.
- Private claims: Claims agreed upon between parties using the JWT.
- Registered claims: Predefined claims like
- Signature: Used to verify the token’s integrity and authenticity. It is created by encoding the header and payload using Base64Url, concatenating them with a dot, and signing the resulting string with a secret key using the specified algorithm.
A JWT typically looks like:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Setting Up the Project
Before we dive into the middleware, let’s set up a basic Node.js project with Express.
- Initialize a new Node.js project:
mkdir auth-middleware cd auth-middleware npm init -y
- Install necessary packages:
npm install express jsonwebtoken body-parser
- Create a basic Express server:
// server.js const express = require('express'); const bodyParser = require('body-parser'); const jwt = require('jsonwebtoken'); const app = express(); app.use(bodyParser.json()); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });
Creating the Middleware
Next lets create the middleware that will intercept and handle authentication in our application
- Create the middleware:
// middleware/auth.js const jwt = require('jsonwebtoken'); const authenticateToken = (req, res, next) => { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (token == null) return res.sendStatus(401); // If no token, unauthorized jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => { if (err) return res.sendStatus(403); // If invalid token, forbidden req.user = user; next(); // Proceed to the next middleware or route handler }); }; module.exports = authenticateToken;
- Protecting routes using the middleware:
// server.js const express = require('express'); const bodyParser = require('body-parser'); const jwt = require('jsonwebtoken'); const authenticateToken = require('./middleware/auth'); const app = express(); app.use(bodyParser.json()); const PORT = process.env.PORT || 3000; // Dummy user for demonstration const user = { id: 1, username: 'testuser' }; // Route to login and generate a token app.post('/login', (req, res) => { const username = req.body.username; if (username !== user.username) { return res.status(403).send('User not found'); } const accessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET); res.json({ accessToken }); }); // A protected route app.get('/protected', authenticateToken, (req, res) => { res.send('This is a protected route'); }); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });
Generating and Verifying JWTs
When a user logs in, for us to verify the user, we need to generate the JWT and verify that user is having a valid token
- Generate a JWT:
In the /login
route, we create a token using ‘jwt.sign()
‘
const accessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET);
- Verify a JWT:
In the middleware, we use ‘jwt.verify()
‘ to check the token’s validity
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => { if (err) return res.sendStatus(403); req.user = user; next(); });
Environment Variables
In order to keep our security keys for access token generation hidden from public , lets store our key in a .env file in the server and load that in our application when needed
Create a ‘.env
‘ file in the project root:
ACCESS_TOKEN_SECRET=secret_key
Load these variables in the application using ‘dotenv
‘:
npm install dotenv
// server.js require('dotenv').config();
Conclusion
By implementing custom middleware with JWT, we can secure our Node.js applications with a robust authentication mechanism. This approach ensures that only authenticated users can access protected routes, enhancing the overall security of our application.