Amber Wallen
Amber Wallen

Reputation: 57

Passport + Sequelize not saving req.user

I'm having an issue with saving the session. It appears that logging in and logging out works fine. However, if making code changes or if the nodemon server refreshes, it renders null on the current_user route.

And then this error when making post requests.

Cannot read property 'id' of undefined

router.get("/current_user", (req, res) => {
  if(req.user){
    res.status(200).send({ user: req.user});
  } else {
    res.json({ user:null})
  }

});

routes

const jwt = require('jsonwebtoken');
const passport = require('passport');

router.post('/loginUser',  passport.authenticate('login', {session: true}), (req, res, next) => {
  passport.authenticate('login', (err, user, info) => {
    if (err) {
      console.log(err);
    }

    if (info != undefined) {
      console.log(info.message);
      res.status(401).send(info.message);
    } else {
      req.logIn(user, err => {
       models.User.findOne({
          where: {
            username: req.body.username,
          },
        }).then(user => {
          const token = jwt.sign({ id: user.id  }, process.env.JWT_SECRET);
          // res.cookie("jwt", token, { expires: new Date(Date.now() + 10*1000*60*60*24)});
          jwt.verify(token, process.env.JWT_SECRET, function(err, data){
            console.log(err, data);
          })

          res.status(200).send({

            auth: true,
            token: token,
            message: 'user found & logged in',
          });
          // console.log(req.user)

        });
      });
    }
  })(req, res, next);
});

passport.js

const bcrypt = require('bcrypt'),
      BCRYPT_SALT_ROUNDS = 12,
      JWTstrategy = require('passport-jwt').Strategy,
      ExtractJWT = require('passport-jwt').ExtractJwt,
      Sequelize = require('sequelize'),
      Op = Sequelize.Op,
      models = require( '../models/'),
      localStrategy = require('passport-local').Strategy;
      // passport = require("passport");

// serialize session, only store user id in the session information


module.exports = async (passport) => {

  passport.use(
    'register',
    new localStrategy(
      {
        usernameField: 'username',
        passwordField: 'password',
        passReqToCallback: true,
        session: false,
      },
      (req, username, password, done) => {
        try {
           models.User.findOne({
            where: {
              [Op.or]: [
                {
                  username: username,
                },
                { email: req.body.email },
              ],
            },
          }).then(user => {
            if (user != null) {
              console.log('username or email already taken');
              return done(null, false, {
                message: 'username or email already taken',
              });
            } else {
              bcrypt.hash(password, BCRYPT_SALT_ROUNDS).then(hashedPassword => {
                models.User.create({
                  username: req.body.username,
                  password: hashedPassword,
                  email: req.body.email
                }).then(user => {
                  console.log('user created');
                  return done(null, user);
                });
              });
            }
          });
        } catch (err) {
          done(err);
        }
      },
    ),
  );



passport.use(
  'login',
  new localStrategy(
    {
      usernameField: 'username',
      passwordField: 'password',
      session: false,
    },
    (username, password, done, req) => {
      try {
        models.User.findOne({
          where: {
            [Op.or]: [
              {
                username: username,
              }
            ],
          },
        }).then(user => {
          if (user === null) {
            return done(null, false, { message: 'Username doesn\'t exist' });

          } else {
            bcrypt.compare(password, user.password).then(response => {
              if (response !== true) {
                console.log('passwords do not match');
                return done(null, false, { message: 'passwords do not match' });
              }

              console.log('user found & authenticated');
              // note the return needed with passport local - remove this return for passport JWT
              return done(null, user);
            });


          }
        });
      } catch (err) {
        done(err);
      }
    },
  ),
);

const opts = {
  jwtFromRequest: ExtractJWT.fromAuthHeaderWithScheme('JWT'),
  secretOrKey: process.env.JWT_SECRET,
};




passport.use(
  'jwt',
  new JWTstrategy(opts, (jwt_payload, done) => {
    try {
       models.User.findOne({
        where: {
          username: jwt_payload._id,
        },
      }).then(user => {
        if (user) {
          console.log('user found in db in passport');
          // note the return removed with passport JWT - add this return for passport local
          done(null, user);
          // console.log(user);
        } else {
          console.log('user not found in db');
          done(null, false);
        }
      });
    } catch (err) {
      done(err);
    }
  }),
);

passport.serializeUser(function(user, done) {
   done(null, user.id);
   console.log(user.id); // gets user id
});

// from the user id, figure out who the user is...
passport.deserializeUser(function(id, done){
  models.User.findOne({
    where: {
      id,
    },
  }).then(user => done(null, user))
  .catch(done);
});

}

app.js

var express = require('express');
var app = express();
var userRoute = require('./routes/users');
var postRoute  = require('./routes/posts');
var bodyParser = require('body-parser');
var logger = require('morgan');
var session = require('express-session');
var cookieParser = require('cookie-parser') ;
var dotenv = require('dotenv');
var env = dotenv.config();
var cors = require('cors');
var models = require('./models/');
const host = '0.0.0.0';
const PORT = process.env.PORT || 8000;
const passport = require('passport');
const path = require('path');
// const allowOrigin = process.env.ALLOW_ORIGIN || '*'
// CORS Middleware
if (!process.env.PORT) {
  require('dotenv').config()
}

// console.log(process.env.DATABASE_URL);
if (!process.env.PORT) {
  console.log('[api][port] 8000 set as default')
  console.log('[api][header] Access-Control-Allow-Origin: * set as default')
} else {
  console.log('[api][node] Loaded ENV vars from .env file')
  console.log(`[api][port] ${process.env.PORT}`)
  console.log(`[api][header] Access-Control-Allow-Origin: ${process.env.ALLOW_ORIGIN}`)
}

app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'build')));
app.use(cookieParser());
app.use(session({
  secret : process.env.JWT_SECRET,
}));

require('./config/passport.js')(passport); // PASSPORT Init
app.use(passport.initialize());
app.use(passport.session());


app.use(bodyParser.urlencoded({ extended:false})); 
app.use(bodyParser.json());
// this code may be useless or useful, still trying to understand cors. 
app.use(function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Credentials',  true);
  res.header("preflightContinue", false)
  res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  next();
});


app.use(cors({
  origin: process.env.ALLOW_ORIGIN,
  credentials: true,
  allowedHeaders: 'X-Requested-With, Content-Type, Authorization',
  methods: 'GET, POST, PATCH, PUT, POST, DELETE, OPTIONS'
}))

app.use('/api/users', userRoute );
app.use('/api/posts', postRoute );

// In order to use REACT + EXPRESS we need the following code, alone with a build
// in the client folder we run a npm run build in the client folder then it is referred
// in the following code. 

app.use(express.static(path.join(__dirname, 'client/build')));

if(process.env.NODE_ENV === 'production') {
  app.use(express.static(path.join(__dirname, 'client/build')));
  //
  app.get('*', (req, res) => {
    res.sendfile(path.join(__dirname = 'client/build/index.html'));
  })
}
//build mode
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname+'/client/public/index.html'));
})



app.use(function(req, res, next) {
  res.locals.user = req.user; // This is the important line
  // req.session.user = user
  console.log(res.locals.user);
  next();
});



models.sequelize.sync().then(function() {

  app.listen(PORT, host, () => {
    console.log('[api][listen] http://localhost:' + PORT)
  })

})

Upvotes: 1

Views: 494

Answers (1)

randal
randal

Reputation: 1362

The question is not so much about saving the req.user, this is an issue with not having a store set in place.

app.use(session({
  store: '', // enter a store
  secret : process.env.JWT_SECRET,
}));

You're using Sequelize, so i would recommend you to look into this

https://github.com/mweibel/connect-session-sequelize

If all runs well, you will not have to worry about req.id becoming undefined everytime you make a code change. Hope this helps.

You can do something like

const SequelizeStore = require('connect-session-sequelize')(session.Store);


const sequelize = new Sequelize(
  process.env.POSTGRES_DB, 
  process.env.POSTGRES_USER, 
  process.env.POSTGRES_PASSWORD,{
    "dialect": "sqlite",
    "storage": "./session.sqlite"
});

myStore = new SequelizeStore({
  db:sequelize,
})

app.use(session({
  store: myStore, 
  secret : process.env.JWT_SECRET,
}));

Upvotes: 2

Related Questions