API security ensures data protection by restricting sensitive operations to admins and validating user sessions. Middleware like allowAdminsOnly enforces admin-only access, while authorise verifies tokens and trusted devices for robust security across web and mobile clients.

API security is a critical aspect of modern web applications. Restricting sensitive operations to authorized users, such as administrators, ensures data protection and system integrity. This blog will demonstrate how to secure your API by implementing middleware to allow only admins and an authorization mechanism to validate user sessions and trusted devices.
The allowAdminsOnly middleware ensures that only users with an admin role can access specific routes. Here’s the implementation:
export const allowAdminsOnly = (req: Request, res: Response, next: NextFunction) => {
try {
const user = (req as ExtendedRequest).user;
if (!user) {
return res.status(401).json({ error: "Unauthorized. Please log in." });
}
if (user.user_role === userTypes.ADMIN) {
return next();
}
return res.status(403).json({ error: "You are not an admin" });
} catch (error) {
console.error("Error in checking admin role:", error);
res.status(500).json({ error: "Internal server error" });
}
};ADMIN role.The authorise middleware validates user sessions and ensures that requests are made from trusted devices. Here’s the implementation:
export const authorise = (req: Request, res: Response, next: NextFunction) => {
try {
let accessToken;
let deviceId;
const isMobileApp = req.headers["client-type"] === "mobile_app";
if (isMobileApp) {
accessToken = req.headers.authorization?.split(" ")[1];
deviceId = req.headers.deviceId;
} else {
accessToken = req.cookies?.accessToken;
deviceId = req.cookies?.deviceId;
}
if (!accessToken) {
return res.status(401).json({ error: "User not logged in" });
}
jwt.verify(accessToken, process.env.SECRET_KEY || "", async (err: any, decoded: any) => {
if (err) {
return res.status(403).json({ error: "Invalid access token" });
}
const device = await TrustedDeviceModel.findOne({
user_id: decoded.userId,
device_id: deviceId,
isActive: true,
});
if (!device) {
return res.status(401).json({ error: "Invalid or inactive device. Please Login Again" });
}
(req as ExtendedRequest).user = decoded;
next();
});
} catch (error) {
console.error("Error in authorise middleware:", error);
res.status(500).json({ error: "Internal server error" });
}
};To implement these middlewares, we’ll use a streamlined user model. Here’s a cleaned-up version of the user schema:
const userSchema = new Schema({
username: {
type: String,
unique: true,
required: false,
index: true,
},
full_name: {
type: String,
required: true,
},
email_address: {
type: String,
unique: true,
required: true,
index: true,
},
user_role: {
type: String,
required: true,
enum: ["admin", "user"],
default: "user",
index: true,
},
});
const UserModel = model<UserDocument, UserModel>("User", userSchema);username, full_name, and email_address: Basic user details.user_role: Differentiates between admin and regular users.To protect your routes, simply apply the middleware:
const app = express();
// Route accessible only by admins
app.get("/admin", authorise, allowAdminsOnly, (req, res) => {
res.send("Welcome, Admin!");
});
// Public route
app.get("/public", (req, res) => {
res.send("Welcome, Guest!");
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});By implementing the allowAdminsOnly and authorise middlewares, you can effectively secure your API. The admin-only restriction ensures sensitive operations are safeguarded, while the authorization process validates users and devices, adding another layer of security.
Integrate these techniques into your Node.js application to protect your resources and maintain robust API security.
That's it for today. See ya 👋
New tutorials, book updates, and behind-the-scenes notes from the studio. No schedule, no spam.