In a secure web application, it's crucial to ensure that only authenticated users can access certain routes. Additionally, specific actions, such as modifying or deleting data, should only be allowed for users with proper permissions. This is where authentication and authorization come into play.
In this article, we’ll break down a simple implementation of authentication and authorization middleware in a Node.js application using Express.
1. Authentication Middleware
Authentication is the process of verifying that a user is who they claim to be. In this example, we use a JWT (JSON Web Token) to validate the user's identity before they can access protected routes.
const authentication = async (req, res, next) => {
try {
let { authorization } = req.headers;
if (!authorization) throw { name: "InvalidToken" }; // No token provided
const [bearer, token] = authorization.split(" ");
if (bearer !== "Bearer") throw { name: "InvalidToken" }; // Invalid token format
const payload = verifyToken(token); // Verify the token and get user data
const member = await Member.findByPk(payload.id); // Find the user in the database
if (!member) throw { name: "InvalidToken" }; // No matching user found
req.member = { id: member.id }; // Attach user data to the request
next(); // Pass to the next middleware or route handler
} catch (error) {
next(error); // Handle errors
}
};
The token is expected to be in the Authorization header (formatted as Bearer token).
The middleware verifies if the token is present and properly formatted.
Using verifyToken(), it checks if the token is valid and decodes the user's data.
The user's ID from the token payload is used to find them in the database.
If successful, the user's ID is attached to the request (req.member), allowing other routes to know who the user is.
If the token is missing, invalid, or the user doesn’t exist, an error is thrown.
2. Authorization Middleware
Authorization controls what actions an authenticated user is allowed to perform. In this case, the authorization middleware ensures that a user can only interact with tickets they own.
const authorization = async (req, res, next) => {
try {
let recipientId = req.member.id; // The authenticated user's ID
let ticketId = req.params.id; // The ticket ID from the request
let ticket = await EventTicket.findByPk(ticketId); // Find the ticket in the database
if (!ticket) throw { name: "Custom", status: 404, message: "Ticket not found" }; // If ticket doesn't exist
if (recipientId !== ticket.recipientId) throw { name: "Unauthorized" }; // If user doesn't own the ticket
next(); // If authorized, proceed to the next middleware or route handler
} catch (error) {
next(error); // Handle errors
}
};
The middleware retrieves the authenticated user’s ID (req.member.id) and the ticket ID from the request parameters (req.params.id).
It checks if the ticket exists in the database.
It ensures that the logged-in user (the recipient) is the owner of the ticket. If the user doesn’t own the ticket, an "Unauthorized" error is thrown.
If the user is authorized, the middleware passes control to the next handler.
3. Error Handling Middleware
The error-handling middleware ensures that errors caught during the request lifecycle are properly handled and returned as responses.
app.use((error, req, res, next) => {
let status = error.status || 500; // Default to 500 if status isn't provided
let message = error.message || "Internal server error"; // Default message
switch (error.name) {
case "SequelizeValidationError":
case "SequelizeUniqueConstraintError":
status = 400;
message = error.errors[0].message; // Handle Sequelize validation errors
break;
case "InvalidToken":
case "JsonWebTokenError":
status = 401;
message = "Invalid token"; // Handle token errors
break;
case "Unauthorized":
status = 403;
message = "You are not authorized"; // Handle unauthorized access
break;
}
res.status(status).json({ message }); // Send error response
});
How It Works:
This middleware catches any errors passed down by the next(error) function.
Depending on the error type, it sets the appropriate status code (e.g., 401 for invalid tokens, 403 for unauthorized actions, etc.).
A relevant message is returned to the client, helping users understand what went wrong.
Top comments (0)