Session-Based Account Deletion in API Callbacks

Session-Based Account Deletion in API Callbacks

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 Uses req.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.

Scroll to Top