Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent behavior when destroying a session #143

Open
TurtIeSocks opened this issue Jun 24, 2023 · 1 comment
Open

Inconsistent behavior when destroying a session #143

TurtIeSocks opened this issue Jun 24, 2023 · 1 comment

Comments

@TurtIeSocks
Copy link

This might not be an actual bug, or could be an issue with Passport, but I'm experiencing this inconsistent behavior after updating this lib, so I'm starting here first.

Problem:

When a user calls the /auth/logout route, the server errors. It was working previously on 2.1.6, but after recently updating and changing the implementation, the same code now crashes.

Version:

I recently upgraded from ^2.1.6 to ^3.0.0.

Related Libs:

  • Passport.js
  • Express.js

Error

Stack Trace

TypeError: Cannot read properties of undefined (reading 'regenerate')
    at /Users/user/Documents/GitHub/ReactMap/node_modules/passport/lib/sessionmanager.js:83:17
    at /Users/user/Documents/GitHub/ReactMap/node_modules/express-mysql-session/index.js:245:10
    at /Users/user/Documents/GitHub/ReactMap/node_modules/underscore/underscore-node-f.cjs:1213:19
    at Query.<anonymous> (/Users/user/Documents/GitHub/ReactMap/node_modules/@sentry-internal/tracing/cjs/node/integrations/mysql.js:59:13)
    at Query.<anonymous> (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/Connection.js:526:10)
    at Query._callback (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/Connection.js:488:16)
    at Sequence.end (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/protocol/sequences/Sequence.js:83:24)
    at Query._handleFinalResultPacket (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/protocol/sequences/Query.js:149:8)
    at Query.OkPacket (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/protocol/sequences/Query.js:74:10)
    at Protocol._parsePacket (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/protocol/Protocol.js:291:23)
    at Parser._parsePacket (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/protocol/Parser.js:433:10)
    at Parser.write (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/protocol/Parser.js:43:10)
    at Protocol.write (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/protocol/Protocol.js:38:16)
    at Socket.<anonymous> (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/Connection.js:88:28)
    at Socket.<anonymous> (/Users/user/Documents/GitHub/ReactMap/node_modules/mysql/lib/Connection.js:526:10)
    at Socket.emit (node:events:513:28)

The property that is undefined is the request.

Codeblock from Passport.js

SessionManager.prototype.logOut = function(req, options, cb) {
  if (typeof options == 'function') {
    cb = options;
    options = {};
  }
  options = options || {};
  
  if (!req.session) { return cb(new Error('Login sessions require session support. Did you forget to use `express-session` middleware?')); }
  
  var self = this;
  
  // clear the user from the session object and save.
  // this will ensure that re-using the old session id
  // does not have a logged in user
  if (req.session[this._key]) {
    delete req.session[this._key].user;
  }
  var prevSession = req.session;
  
  req.session.save(function(err) {
    if (err) {
      return cb(err)
    }
  
    // regenerate the session, which is good practice to help
    // guard against forms of session fixation
    req.session.regenerate(function(err) {
      if (err) {
        return cb(err);
      }
      if (options.keepSessionInfo) {
        merge(req.session, prevSession);
      }
      cb();
    });
  });
}

Code

Logout Route

router.get('/logout', (req, res) => {
  // Had to call both of these for some reason to get the session to actually be logged out correctly
  req.logout((err) => {
    if (err) log.error(HELPERS.auth, 'Unable to logout', err)
  })
  req.session.destroy() // removing this with 3.0.0 makes the error go away
  res.redirect('/')
})

Old Session Store

const sessionStore = dbSelection
  ? new MySQLStore(
      {
        clearExpired: true,
        checkExpirationInterval: sessionCheckIntervalMs,
        createDatabaseTable: true,
        schema: {
          tableName: sessionTableName,
        },
      },
      mysql2.createPool({
        host: dbSelection.host,
        port: dbSelection.port,
        password: dbSelection.password,
        user: dbSelection.username,
        database: dbSelection.database,
      }),
    )
  : null

New Session Store

const sessionStore = dbSelection
  ? new MySQLStore(
      {
        clearExpired: true,
        checkExpirationInterval: sessionCheckIntervalMs,
        createDatabaseTable: true,
        endConnectionOnClose: true,
        schema: {
          tableName: sessionTableName,
        },
        host: dbSelection.host,
        port: dbSelection.port,
        password: dbSelection.password,
        user: dbSelection.username,
        database: dbSelection.database,
      },
    )
  : null

Solutions

So far these have appeared to "solve" the problem, but given the inconsistency, maybe something in the lib needs to be adjusted?

  • Removing the request.destroy() call
  • Go back to creating my own pool and passing that into express-mysql-session instead of passing in my db details.

Thank you! I've been using your library in my OSS projects for several years and it's been great :)

@chill117
Copy link
Owner

chill117 commented Aug 31, 2023

I suspect the problem is actually in your code (the logout route):

router.get('/logout', (req, res) => {
  // Had to call both of these for some reason to get the session to actually be logged out correctly
  req.logout((err) => {
    if (err) log.error(HELPERS.auth, 'Unable to logout', err)
  })
  req.session.destroy() // removing this with 3.0.0 makes the error go away
  res.redirect('/')
})

The req.logout method provided by passport calls req.session.regenerate provided by express session which calls req.session.destroy. So there is a race condition here because you call req.logout then also req.session.destroy. Removing the second req.session.destroy is the correct solution.

Changing the above code block to:

router.get('/logout', (req, res) => {
  req.logout((err) => {
    if (err) {
        log.error(HELPERS.auth, 'Unable to logout', err)
    }
    // Logout failed or successful. In either case, redirect the user.
    res.redirect('/')
  })
})

Should resolve the passport related error and also the inconsistent logout behavior that you described.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants