While working on my side project that includes building API callbacks, I’ve used some session-based actions, and I want to document here a case that may be useful to refer to in the future. Namely, in short, my idea was this: the /delete-account
route is supposed to delete the currently logged-in user. So, the delete request doesn’t use any body or id, but it deletes the user that has been logged in in the previous /login request. This is basically simulating in the API how the account deletion process looks like on the front-end app. Namely, there, the user has to log in, and after that, they end up on the dashboard page where they get the option to delete their account.
The main technology used here is Express.js
Here is the folder structure of the project for better understanding:
Project folder
-src
-api.js (API callback functions)
-index.js (server)
-mongo.js (MongoDB setup)
Login API callback:
router.post("/login", async (req, res) => {
try {
const { name, password } = req.body;
console.log("Login Request - Name:", name); // Log the name from the request
console.log("Login Request - Password:", password); // Log the password from the request (for typos)
let user;
user = await UserCollection.findOne({ $or: [{ username: name }, { email: name }] });
if (!user) {
console.log("User not found."); // Log if the user isn't found
return res.status(401).json({ message: "Invalid username or email" });
}
console.log("User from DB - Username:", user.username);
console.log("User from DB - Email:", user.email);
console.log("User from DB - Hashed Password:", user.password); // VERY IMPORTANT
const isMatch = await bcrypt.compare(password, user.password);
console.log("bcrypt.compare Result:", isMatch); // Log the result of the comparison
if (!isMatch) {
console.log("Password mismatch."); // Log if the passwords don't match
return res.status(401).json({ message: "Invalid password" });
}
req.session.user = { _id: user._id, username: user.username, email: user.email };
res.json({ message: "Login successful", user: { username: user.username, email: user.email } });
} catch (error) {
console.error("Login Error Details:", error); // Log the FULL error object
res.status(500).json({ message: "Error during login" });
}
});
To ensure /delete-account
correctly identifies the logged-in user:
- 1. Use Session Authentication in Middleware
Modify your middleware in the api.js file to check req.session.user
first:
router.use(async (req, res, next) => {
if (req.session && req.session.user) {
req.user = await UserCollection.findById(req.session.user._id);
} else {
const authHeader = req.header('X-Test-Auth');
if (authHeader) {
const [username, password] = authHeader.split(':');
const user = await TestUserCollection.findOne({ username });
if (user && user.password === password) {
req.user = user;
}
}
}
next();
});
- This ensures that if the user is logged in via
/login
, their session is used. - The
X-Test-Auth
header is still supported for Postman tests but doesn’t override a session login.
- 2. Ensure
/delete-account
Usesreq.user
Since the middleware now ensures req.user
exists for logged-in users, /delete-account
should work correctly:
router.post('/delete-account', async (req, res) => {
if (!req.user) {
return res.status(401).json({ message: 'Unauthorized' });
}
try {
const result = await UserCollection.deleteOne({ _id: req.user._id });
if (result.deletedCount === 0) {
return res.status(404).json({ message: "User not found" });
}
req.session.destroy((err) => {
if (err) {
console.error("Error destroying session:", err);
return res.status(500).json({ message: 'Error during logout after deletion' });
}
res.json({ message: "Account deleted successfully" });
});
} catch (error) {
console.error("Error deleting account:", error);
res.status(500).json({ message: "Error deleting account" });
}
});
- 3. Ensure Express Session is Configured
Make sure express-session
is properly set up in your main file (index.js):
// Set up express-session middleware
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
store: MongoStore.create({
mongoUrl: process.env.DATABASE_URL,
collectionName: "sessions",
}),
})
);
Edge cases
Requests by order:
/login (this one won't be deleted)
/login (this one won't be deleted)
/login (this one will get deleted)
/delete
When we send multiple /login requests, the /delete request will delete the user related to the last successful login request submitted.