Trying to complete the passport section of Advanced Node and Express - req.session is being dropped after redirect

req.session.passport being dropped during redirect
Hi all,

I’m trying to set up the local authentication strategy in the Advanced Node and Express course. I’m passing all of the tests, but I realized that the authentication isn’t actually working. When a user logs in by POSTing to ‘/login’, I call passport.authenticate, then on success, I redirect to ‘/profile’. The problem is that when I get the GET request to ‘/profile’ and call ensureAuthenticated, user.session no longer has a passport property. So it would appear that this property is being dropped during the redirect. My code is below, and I’ve included the console log after each console.log call - there are three of them - one logging that a user has attempted to log in (in the database call where the local strategy is defined), one logging req.session once a user has successfully logged in (in the POST request to /login), and one logging req.session when I am verifying that a user has been authenticated (in the ensureAuthenticated function). I’ve been stuck on this for hours! Any help would be greatly appreciated!!!

'use strict';
require('dotenv').config();
const express = require('express');
const myDB = require('./connection');
const fccTesting = require('./freeCodeCamp/fcctesting.js');
const session = require('express-session');
const passport = require('passport');
const { ObjectID } = require('mongodb');
const LocalStrategy = require('passport-local');

const app = express();
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: true,
  saveUninitialized: true,
  cookie: {secure: false}
}))

app.set('view engine', 'pug');
app.set('views','./views/pug');
fccTesting(app); //For FCC testing purposes
app.use('/public', express.static(process.cwd() + '/public'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(passport.initialize());
app.use(passport.session());

myDB(async client => {
  const myDataBase = await client.db('database').collection('users');

  app.route('/').get((req,res) => {
    res.render('index', {
      title: 'Connected to Database',
      message: 'Please login',
      showLogin: true,
      showRegistration: true,
    })
  })

  passport.use(new LocalStrategy((username,password,done) => {
    myDataBase.findOne({username: username})
      .then(user => {
        console.log(`User ${username} attempted to log in`);
        if (!user) return done(null,false);
        if (password != user.password) return done(null,false);
        return done(null,user);
      })
      .catch(err => {
        return done(err);
      })
  }))

  app.route('/login').post(passport.authenticate('local', {failureRedirect: '/'}),(req,res) => {
    console.log(req.session)
/*
Session {
  cookie: {
    path: '/',
    _expires: null,
    originalMaxAge: null,
    httpOnly: true,
    secure: false
  },
  passport: { user: new ObjectId("665d27a2cee92617fe9d409b") }
}
*/
    res.redirect('/profile');
  })

  app.route('/profile').get(ensureAuthenticated,(req,res,next) => {
    res.render('profile',{
      username: req.user.username,
    });
  })

  app.route('/logout').get((req,res) => {
    req.logout();
    res.redirect('/');
  })

  app.route('/register').post((req,res,next) => {
    // First middleware checks if user exists
    myDataBase.findOne({username: req.body.username})
      .catch(err => next(err))
      .then(user => {
        if (user) return res.redirect('/');
        myDataBase.insertOne({
          username: req.body.username,
          password: req.body.password
        })
          .then(doc => next(null,doc.ops[0]))
          .catch(err => res.redirect('/'))
      });
    },
    passport.authenticate('local', {failureRedirect: '/'}),
    (req,res,next) => {
      res.redirect('/profile');
    }
  )

  app.use((req,res,next) => {
    res.status(404)
      .type('text')
      .send('Not found');
  })

  passport.serializeUser((user,done) => {
    done(null, user._id);
  });
  passport.deserializeUser((id,done) => {
    myDataBase.findOne({_id: new ObjectID(id)}, (err,doc) => {
      done(null,doc);
    })
  });

  function ensureAuthenticated(req,res,next) {
    console.log(req.session);
/*
Session {
  cookie: {
    path: '/',
    _expires: null,
    originalMaxAge: null,
    httpOnly: true,
    secure: false
  }
}
*/
    if (req.isAuthenticated()) {
      return next();
    }
    res.redirect('/');
  }

}).catch(e => {
  app.route('/').get((req,res) => {
    res.render('index', {
      title: e, message: 'Unable to connect to database'
    })
  })
})

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log('Listening on port ' + PORT);
});

Challenge: Set up a Template Engine

Link to the challenge:

When I copied your code and ran it, both console outputs had the session.passport property:

Session {
  cookie: {
    path: '/',
    _expires: null,
    originalMaxAge: null,
    httpOnly: true,
    secure: false
  },
  passport: { user: 665f58630b4969100637705d }
}
Session {
  cookie: {
    path: '/',
    _expires: null,
    originalMaxAge: null,
    httpOnly: true,
    secure: false
  },
  passport: { user: '665f58630b4969100637705d' }
}

That said I think isAuthenticated checks both req.session.passport.user as well as req.user, which it populates during the authenticate() call. From the docs:

“In this route, passport.authenticate() is middleware which will authenticate the request. By default, when authentication succeeds, the req.user property is set to the authenticated user, a login session is established”

and here’s isAuthenticated:

req.isAuthenticated = function() {
  var property = 'user';
  if (this._passport && this._passport.instance) {
    property = this._passport.instance._userProperty || 'user';
  }
  
  return (this[property]) ? true : false;
};