Authentication is a critical component of web application security, ensuring that users are who they say they are. In Express.js, middleware provides a powerful way to handle authentication logic before it reaches to the request handlers. This blog post will guide you through implementing authentication middleware in an Express.js application.
What are middlewares?
In Express.js, middleware functions are functions that have access to the request, response, and the next middleware in the application’s request-response cycle. They can perform various tasks such as logging, authentication, and error handling or any other task which we want perform before the final response of a request.
Prerequisites:
Nodejs setup: Install nodejs before creating an angular project
Step 1: Initialize the express application
Create a new directory and initialize a new express application by running the below commands:
mkdir my-express-app
cd my-express-app
npm init -y
Step 2: Install the required dependencies
npm install express jsonwebtoken dotenv
express - to create server using expressjs
jsonwebtoken - to create tokens for a token based authentication
dotenv - to secure and configure env variables
Step 3: Create a basic server
// Create a file app.js and paste the below code to setup a basic server
const express = require('express');
const app = express();
const dotenv = require('dotenv');
dotenv.config();
app.use(express.json()); // middleware provided by expressjs to handle json data
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Step 4. Create a middleware for authentication
// Create a folder middlewares and create a file auth.js and paste the below code
const jwt = require('jsonwebtoken');
const authenticateToken = (req, res, next) => {
const token = req.headers.authorization // Get the token from the authorization header
// If no is found in the headers
if (!token) {
return res.status(401).send({ message: 'No token provided' });
}
// verfify the token if found
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
// If token provided in invalid or expired
if (err) {
return res.status(403).send({ message: 'Invalid access token' });
}
// If token is decoded successfully attach the decoded the to the request object
req.user = decoded;
// Move the request to the next middleware or route handler
next();
});
};
module.exports = authenticateToken;
With above code, we have created a simple auth middleware
Step 5. Create routes to test and integrate the middleware
Create a folder routes and create a file auth.js and paste the below code to create functions for registration and login to the get the token.
const express = require('express');
const jwt = require('jsonwebtoken');
const router = express.Router();
const authenticateToken = require('../middlewares/auth');
let users = []; // This will act as a temporary in-memory database for this example
// Registration route to register as a user
router.post('/register', (req, res) => {
const { username, password } = req.body;
// Check if user already exists
const userExists = users.find(user => user.username === username);
if (userExists) {
return res.status(400).send({ message: 'User already exists' });
}
// Save new user temporarily
users.push({ username, password });
res.status(201).send({ message: 'User registered successfully' });
});
// Login route to login user and get the token for the testing
router.post('/login', (req, res) => {
const { username, password } = req.body;
// Validate user
const user = users.find(user => user.username === username && user.password === password);
if (!user) {
return res.status(401).send({ message: 'Invalid credentials' });
}
// Generate a token
const token = jwt.sign({ username }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.send({ token });
});
module.exports = router;
Step 6: Integrate Routes into the app.js file
Now, we need to integrate the routes we just created into our main application. Update app.js to include the new routes.
const express = require('express');
const dotenv = require('dotenv');
const app = express();
dotenv.config();
const authRoutes = require('./routes/auth'); // Import the auth routes
app.use(express.json()); // middleware provided by expressjs to handle json data
// Use authentication routes to register and login
app.use('/auth', authRoutes); // Prefix all routes with /auth
// A public route which do not requires authentication
app.get('/', (req, res) => {
res.send('Hello from the Public route')
})
// Protected route to test the middleware for authentication
app.get('/protected', (req, res) => {
res.send('Hello, This is a protected route.')
})
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Testing the api and authentication middlewares:
Now, our express app is complete and ready to test install a tool like postman to test the api and its working.
1. Register a New User
Send a POST request to http://localhost:3000/auth/register with the following JSON body:
{ "username": "testuser", "password": "123456" }
2. Log In to Get a Token
Next, send a POST request to http://localhost:3000/auth/login with the same credentials.
{ "username": "testuser", "password": "123456" }
You should receive a response with a token of the below format after successfully logging in:
{"token": "your_token" }
3. Access a Protected Route
Now, use the token above to access a protected route.
Send a GET request to http://localhost:3000/protected with the token in the Authorization header.
Set up the token in authorization header as shown below:
Authorization: your_token
Make the api call. If the token is valid, you should see a response like this:
Hello, This is a protected route.
Thus, our express app is completed and tested for authentication.
Summary
In this blog post, we learned how to implement authentication middleware in an Express.js application. By using middleware, you can enforce authentication logic and protect your routes effectively. This basic setup can be extended further by implementing features such as password hashing, user management, and role based authorisation by creating authorisation middlewares in a similar fashion as shown.