1

Using node.js and pgAdmin. I have set up all the necessary code for a server, sessions, cookies, and all that. The error I'm getting is "TypeError: Cannot read property 'username' of undefined". The session will not persist after it redirects from one page to another. Specifically from the loginregister page to the orderform. How do I get the session to stay after being redirected? Any help would be greatly appreciated, thanks!

const LocalStrategy = require("passport-local").Strategy;
const { pool } = require('./dbConfig');
const bcrypt = require('bcryptjs');

function initialize (passport) { 
           
    const authenticateUser = (username, password, done) => {

        pool.query(

            `SELECT * FROM credentials WHERE username = $1`, 

            [username], (err, results) => {

                if(err) {
                    throw err;
                }

                if(results.rows.length === 1) {
                    const user = results.rows[0];

                    bcrypt.compare(password, user.password, (err, isMatch) => {
                        if(err){
                            throw err
                        }
                        if(isMatch){
                            
                            return done(null, user);
                        }
                        else {
                            return done(null, false, {message: "Incorrect password"});
                        }
                    })
                }
                else {
                    return done(null, false, {message: "Account does not exist"});
                }
            }
        );
    }

    passport.use('user-local', new LocalStrategy({
        usernameField: "username",
        passwordField: "password"
    }, authenticateUser));

    passport.serializeUser(function (user, done) 
    { 
        done(null, user.user_id);
    });

    passport.deserializeUser(function (id, done) { console.log("---------------------------------------------");
        pool.query(
            `SELECT * FROM credentials WHERE user_id = $1`,
            [id],
            (err, results) => {
                if(err) {
                    throw err;
                }
                return done(null, results.rows[0]);
            }
        )
    });

    const authenticateEmp = (username, password, done)=>{
        pool.query(
            `SELECT * FROM employee WHERE username = $1`, 
            [username], (err, results) => {
                if(err) {
                    throw err;
                }

                if(results.rows.length == 1) {
                    const user = results.rows[0];

                    bcrypt.compare(password, user.password, (err, isMatch) => {
                        if(err){
                            throw err
                        }
                        if(isMatch){
                            return done(null, user);
                        }
                        else {
                            return done(null, false, {message: "Incorrect password"});
                        }
                    });
                }
                else {
                    return done(null, false, {message: "Account does not exist"});
                }
            }
        );
    }

    passport.use('emp-local', new LocalStrategy({//Create admin controler 
        usernameField: "username",
        passwordField: "password"
    }, authenticateEmp));

    passport.serializeUser((user, done) => done(null, user.user_id));
    passport.deserializeUser((id, done) => {
        pool.query(
            `SELECT * FROM employee WHERE emp_id = $1`,
            [id],
            (err, results) => {
                if(err) {
                    throw err;
                }
                return done(null, results.rows[0]);
            }
        );
    });
}

module.exports = initialize;
userRouter.get("/loginregister", (req, res) => {
  res.render("../views/loginRegister");
});

userRouter.get("/orderform", (req, res) => {
  console.log(req.user);
  res.send(`Hello ${req.user.username}. Your session ID is ${req.sessionID} 
  and your session expires in ${req.session.cookie.maxAge} 
  milliseconds.<br><br>
  <a href="/logout">Log Out</a><br><br>
  <a href="/secret">Members Only</a>`);
    //res.render("../views/orderForm");
});
function loginController (req, res, next) {
    passport.authenticate('user-local', function(err, user, info) {
      if (err) { return next(err); }
      if (!user) { return res.redirect('/loginregister'); } // Can attach flash message here and personalize req.body.email 
      req.logIn(user, function(err) {
        if (err) { return next(err); }
        console.log(req.sessionID);
        return res.redirect('/user/orderform');
      });
    })(req, res, next);
};

const { name } = require("ejs");
const express = require("express");
const app = express();
const path = require('path');
const parseurl = require('parseurl')
const session = require("express-session");
const flash = require("express-flash");
const uid = require('uid-safe');
const assert = require('assert');
const pgStore = require('connect-pg-simple')(session);
const router = require('../views/routes');
const passport = require('passport');
const { pool } = require('./dbConfig');
const passportConfig = require("./passportConfig");
const PORT = process.env.PORT || 4000;

app.set('view engine', 'ejs');
app.use(express.urlencoded({ extended: false }));

uid(18, function (err, val) {
  if (err) return done(err)
  assert.strictEqual(Buffer.byteLength(val), 24);
})

app.use(session({
  genid: function(req) {
    return uid(18) // use UUIDs for session IDs
  },

  secret: process.env.SECRET_KEY,
  resave: 'false',
  saveUninitialized: 'false',
  cookie: { 
    secure: false,
    maxAge: 24 * 60 * 60 * 1000 // One day
  }, 

  store: new pgStore({
    pool : pool,                // Connection pool
    tableName : process.env.TABLE_NAME   
    // Insert connect-pg-simple options here
  })

}));

if (app.get("env") === "production") {
    // Serve secure cookies, requires HTTPS
    session.Cookie.secure = true;
  }

app.use(flash());

passportConfig(passport);
app.use(passport.initialize());
app.use(passport.session());

app.use(express.json());

app.use ('/', router);

app.use(express.static(path.resolve(__dirname, './css')));

app.listen(PORT, () => {

    console.log(`Server running on port ${PORT}`);
});
Link
  • 35
  • 7
  • 1
    The usual diagnosis for problems like this is to examine the cookies sent with each request to your server and send back from the server. If these requests are all coming from a browser, you can use the chrome debugger to see cookies sent with requests and received back with responses. What you want to do is track the session cookie that first comes back from the server and see if it the same exact cookie is sent back with the next request from the client. If not, then that explains why the session is getting lost and you have to figure out why the browser isn't sending the session cookie. – jfriend00 Jan 04 '22 at 21:45
  • 1
    If the browser is sending the desired session cookie back with the next request, then you have to dive into the server-side behavior to find out why its not accepting that cookie and add more diagnostics on the server-side to output exactly what cookies it is receiving on each request and what cookies it is sending on each response. Again, you want to see what session cookie was sent and which one it got back. – jfriend00 Jan 04 '22 at 21:46
  • @jfriend00 thank you so much! but in doing so I found I see no cookies being set in the browser? Any advice? Thank you again! – Link Jan 05 '22 at 01:42
  • 1
    Try an experiment where you dumb down your session middleware to just this: `app.use(session({ secret: 'keyboard cat', }));` so it's the simplest in-memory session with no restrictions on the cookie at all and no reliance on any other infrastructure. See if you get a cookie then. If you do, then one-by-one, add back other pieces of your session settings until you find what is causing you to lose the cookie. – jfriend00 Jan 05 '22 at 01:51
  • Also, are these requests coming from a browser URL bar or from Javascript code running in a web page? – jfriend00 Jan 05 '22 at 01:52
  • 1
    Also, your `uid()` function call attempts to call `done(err)` in some conditions, but there is no such function defined in the scope of that function. – jfriend00 Jan 05 '22 at 01:53
  • @jfriend00 thank you sm for the advice! gonna try that. And yes it is javascript for the requests, the user logs in which triggers the login controller to invoke passport code. – Link Jan 05 '22 at 02:51
  • 1
    Hmmm, if it's Javascript for the requests, then show us the client-side code that is making the requests. You may not be using the right settings to include cookies in that request. – jfriend00 Jan 05 '22 at 02:56
  • I didn't ask you to remove session middleware. I asked you to replace it with a simpler one. – jfriend00 Jan 05 '22 at 02:57
  • @jfriend00 gotcha! so I commented out the genID and it started to work! any suggestion though on another unique session ID besides the genID to use? – Link Jan 05 '22 at 03:13
  • 1
    So, express-session has it's own id generator. Why do you feel like you have to provide one? – jfriend00 Jan 05 '22 at 03:35
  • @jfriend00 ..... good question lmao, thought I had to provide one to make sure each user had a unique session and I saw online we would have to do it that way – Link Jan 05 '22 at 03:35
  • @jfriend00 update it ended up working after! thank you so much for your help! – Link Jan 05 '22 at 03:45

1 Answers1

1

To debug why you're not getting a session cookie, replace your existing session middleware with just this:

app.use(session({ 
    secret: 'keyboard cat', 
}));

Verify that your session cookie works with that. Then, if so, start adding your session options back one at a time until you find the one that causes things to stop working.

A few other notes.

Your uid implementation for express-session is not implemented properly.

To start with, express-session has its own uid generator built in. You don't need to provide one yourself unless you have some specific unique requirement that the built-in generator does not satisify.

According to the doc for uid-safe, a synchronous UID is generated with:

 uid.sync(18)

not, what you are using which is just uid(18). That just returns a promise object which will not work for a unique id.

So, this:

genid: function(req) {
  return uid(18);
},

should be this:

genid: function(req) {
  return uid.sync(18);
},

Or, just leave the uid generation for express-session to do it on its own.

Oh, here's a fun fact, the default implementation in express-session already uses uid-safe.

jfriend00
  • 683,504
  • 96
  • 985
  • 979