expressjs / session

Simple session middleware for Express
MIT License
6.25k stars 975 forks source link

TypeError: Cannot read properties of undefined (reading 'reload') #948

Closed TheDogHusky closed 8 months ago

TheDogHusky commented 1 year ago

I've been tryna make a website with sessions on express@latest but I encounter an unknown problem. Whenever I try to load a session, it brings me that error:

D:\Utilisateurs\Adam\Documents\dev\TypeScript\hestia\node_modules\express-session\index.js:395
      var _reload = sess.reload
                         ^

TypeError: Cannot read properties of undefined (reading 'reload')
    at wrapmethods (D:\Utilisateurs\Adam\Documents\dev\TypeScript\hestia\node_modules\express-session\index.js:395:26)
    at D:\Utilisateurs\Adam\Documents\dev\TypeScript\hestia\node_modules\express-session\index.js:386:11
    at D:\Utilisateurs\Adam\Documents\dev\TypeScript\hestia\node_modules\express-session\session\session.js:93:21
    at D:\Utilisateurs\Adam\Documents\dev\TypeScript\hestia\node_modules\connect-mongo\build\main\lib\MongoStore.js:224:17
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Node.js v18.16.1

I use a updateSession function to update and manage my sessions in each requests:

export function updateSession(req: Request, res: Response, isDash?: boolean, redirectTo?: boolean): Promise<void|FlashMessage[]> {
    return new Promise(async (resolve, reject) => {
        req.session.reload((err: Error) => {
            if(err) updateSession(req, res, isDash, redirectTo).then(resolve).catch(reject);
            else resolve();
        });

        if(req.session.token && !await isValidToken(req.session.token as string, req.session.token_expire as number)) {
            reject(new Error("ERR_TOKEN_INVALID : Token is invalid"));
            return;
        }

        const dbusr = await Users.findOne({ id: req.session.user?.id });
        if(!dbusr) {
            reject(new Error("ERR_USR_NOT_FOUND : User not found in database"));
            return;
        }
        req.session.dbusr = dbusr;

        const flashes = req.session.flashmsgs;
        if(isDash) {
            req.session.flashmsgs = [];
        }

        req.session.save((err: Error) => {
            if(err) reject(err);
            else resolve(flashes);
        });
    });
}

That is called (per example in my dashboard middleware):

this.router.use(async (req, res, next) => {
            try {
                const flashes = await updateSession(req, res, true).catch(next);
                req.flashes = flashes;
                const dbusr = await Users.findOne({ id: req.session.user?.id });
                if (!dbusr || dbursr.disabled) return res.redirect('/logout');
                if (dbusr.staffLevel <= 3) return res.render('account.ejs', {
                    user: req.session.user,
                    token: req.session.token,
                    avatar: req.session.avatar,
                    dbusr: dbusr,
                    session: req.session,
                    flashes
                });
                next();
            } catch(err: any) {
                await handleErr(err, req, res, next, true);
            }
        });

Any ideas how to fix that? I use :

joewagner commented 8 months ago

@TheDogHusky I can't say for sure what the problem is, but it looks like your updateSession function promise is calling session.reload, and session.save at the same time. If you want to reload the session here, you should probably wait to modify and save to the session until after the reload finishes.

something like:

export function updateSession(req: Request, res: Response, isDash?: boolean, redirectTo?: boolean): Promise<void|FlashMessage[]> {
    return new Promise(async (resolve, reject) => {
        await new Promise(function (resolve, reject) {
            req.session.reload((err: Error) => {
                if(err) {
                    console.log(err); // this might help debugging
                    updateSession(req, res, isDash, redirectTo).then(resolve).catch(reject);
                } else {
                    resolve();
                }
           });
        });

       // now your session is reloaded...
TheDogHusky commented 8 months ago

I’ll try your solution out, thank you very much.

As I don’t know express-session a lot, I just have some little questions about this code:

joewagner commented 8 months ago
  • Does the session.reload() function saves after its execution just like doing session.reload() and session.save() ? (If yes I could optimize and fix a lot of bugs)

reload loads the req.session object with whatever value is in your store. It does not save the current session object. I don't fully understand what your code is trying to accomplish, but this middleware automatically loads the session for you. So if you can try to change your code so you don't reload the session, that might be a general way to start eliminating bugs.

  • Is there any like deep documentation of express-session which explains in detail what each function does ? (So I could know what issues my code has, and if some function already saves by themselves)

The README for this repo is most detailed documentation I know of.
The loading and saving of the session should generally "just work". The only time I've found myself needing to intervene in the loading and saving is when I modify the session inside a route that results in a redirect, which does not appear to be the case for you.

TheDogHusky commented 8 months ago

All right, thank you very much for your answers, everything’s clear now. I’ll look into fixing my code and clearing things up. As for my code, I am trying to update the session each time someone makes a request to the website from a browser: check if the token is still valid (if not logout the user), if he is still in the database (if not, log him out to make him login back so it adds a valid entry).

The overall thing I’m trying to make with zero experience in sessions is a discord community website where there are accounts from Discord login, with a database to store account related data, and a lot of functionalities including an admin dashboard with statistics, ticket management, announcement creation, partnership management, account management (with different permissions and access following the user’s staffLevel variable). So then partnerships are shown on a special page, announcements too and in the main page, etc..

Mostly a paid project one of my friends wants me to do to train myself and make him a good website (so you don’t punch me in the face for being that incompetent in a paid job xD)

That is a big project and I’m paid to do it, but it’s my first time making things that complex with sessions.

Anyways, you helped me on the issue I had, I am not going to bother you with the overall thing as I already know it’s horribly unoptimized and ugly.

Thank you so much for your time and help, Adam.