Passport's 'req.user' becomes undefined sometimes - node.js

I am using passport for authorization and I am saving the session in mongodb using 'connect-mongo-session' module. I noticed that after sometime when I try to login the req.user object becomes undefined, although the session object is present in the database and passport and session details are present in the req object. Here is my session setup:
let store;
if(env === "development")
store = new MongoDBStore({ //Allows session to be stored and retreived even when server restarts.
uri: config.db,
collection: 'mySessions'
})
else {
store = new MongoDBStore({ //Allows session to be stored and retreived even when server restarts.
uri: config.db,
collection: 'Cosmos'
})
}
app.use(session({
store: store,
saveUninitialized: true,
resave: true,
secret: config.app.sessionSecret
}));
app.use(passport.initialize());
app.use(passport.session());
and here is the code responsible for serializing and :
passport.serializeUser((user, done) => {
done(null, user.id);
});
// Use Passport's 'deserializeUser' method to load the user document
passport.deserializeUser((id, done) => {
User.findOne({
_id: id
}, '-password -salt', (err, user) => {
done(err, user);
});
});

I was able to solve the problem by changing saveUninitialized to false and resave to false, so it looks like this now:
app.use(session({
store: store,
saveUninitialized: false,
maxAge: 1000 * 60 * 60 * 84, // I added this just in case
resave: false,
secret: config.app.sessionSecret
}));

Related

Persist expired session data into new session in express js

const adminSession = session({
secret: process.env.ADMIN_SECRET,
resave: false,
saveUninitialized: false,
store: sessionStore,
name: "adminSession",
cookie: {
maxAge: 600000,
secure: false,
},
});
app.use(adminSession());
app.get("/sessionViews", function (req, res, next) {
if (req.session.views) {
req.session.views++;
res.send(`Number of view: ${req.session.vies}`);
} else {
req.session.views = 1;
res.send(" No views");
}
});
Here after the session is expired, req.session.views value is also gone. And new session will be generated with req.session.views=0.
That's how we create the number of views in the certain page, isn't it?
How to keep value persistent with another session?

Logout issue with PassportJs / Oauth / GoogleStrategy

I am writing an application using Nodejs and passportjs with Google Strategy. After logging out, the user is still able to login again without entering credentials. How can the user be made to enter credentials again? This can be a big issue on shared computers.
The front end is built using create-react-app that runs on port 3000. For requests related to authentication, a proxy running on port 5000(nodejs server) is used. The code given below is hosted on port 5000
app.use(
session({
cookie: {maxAge: 30 * 24 * 60 * 60 * 1000},
secret: [keys.cookieKey],
resave: false,
saveUninitialized: false
})
);
app.use(cookieParser());
app.use(passport.initialize());
app.use(passport.session());
passport.use(
new GoogleStrategy(
{
clientID: googleClientID,
clientSecret: googleClientSecret,
callbackURL: '/auth/google/callback',
proxy: true
},
async (accessToken, refreshToken, profile, done) => {
const existingUser = await User.findOne({googleId: profile.id});
if(existingUser){
return done(null, existingUser);
}
const user = await new User({ googleId : profile.id}).save();
done(null, user);
}
)
);
app.get('/api/logout', (req, res) => {
console.log(req.user.accessToken);
if(req.session.passport){ delete req.session.passport; }
req.session.destroy(function (err) {
req.logout();
req.logOut();
req.user = null;
res.clearCookie('connect.sid');
res.redirect('/');
});
});
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
["/api", "/auth/google"],
createProxyMiddleware({
target: "http://localhost:5000",
})
);
};
For advanced configurations like logout, passport,a proper session configuration is the key:
Try with this:
app.use(session({
secret: "changeme",
resave: true,
saveUninitialized: true,
rolling: true,
cookie: {
secure: false,
maxAge: (5 * 1000)
}
}));
This was used in
https://github.com/utec/geofrontend-server
A mini server implementation for client side javascript applications, like your react app. I think it will useful for you. Also you can customize the security!

NodeJS express session expire after page refresh

The session of my nodejs app is expiring every time I refresh the page, after login. It does work fine if I visit different pages but as soon as I refresh the page, the session ends. I've tried a couple of things but none of it seems to work. How can I keep it from expiring even after the page refresh? If I can store session in the database or someplace else to keep it from expiring.
Here are the files
Passport-init.js
var mongoose = require('mongoose');
var User = mongoose.model('user');
var localStrategy = require('passport-local').Strategy;
var bcrypt = require('bcrypt-nodejs');
module.exports = function(passport) {
passport.serializeUser(function(user, done) {
console.log('serializing user:',user.username);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
if(err) {
done(500,err);
}
console.log('deserializing user:',user.username);
done(err, user);
});
});
passport.use('login', new localStrategy({
passReqToCallback : true
},
function(req, username, password, done) {
User.findOne({'username': username},
function(err, user) {
if(err) {
return done(err);
}
if(!user) {
console.log("UserName or Password Incorrect");
return done(null, false);
}
if(!isValidPassword(user, password)) {
console.log("UserName or Password is Incorrect");
return done(null, false);
}
return done(null, user);
});
}));
passport.use('signup', new localStrategy({
passReqToCallback : true
}, function(req, username, password, done) {
User.findOne({'username': username},
function(err, user) {
if(err) {
console.log("Error in signup");
return done(err);
}
if(user) {
console.log("Username already exist" + username);
return(null, false);
}
else {
var newUser = new User();
newUser.username = username;
newUser.password = createHash(password);
newUser.save(function(err) {
if(err) {
console.log("Error in saving user");
throw err;
}
console.log(newUser.username + ' Registration succesful');
return done(null, newUser);
});
}
});
}));
var isValidPassword = function(user, password) {
return bcrypt.compareSync(password, user.password);
}
var createHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(10), null);
}
};
Auth.js
var express = require('express');
var router = express.Router();
module.exports = function(passport) {
router.get('/success', function(req, res) {
res.send({state: 'success', user: req.user ? req.user : null});
});
router.get('/failure', function(req, res) {
res.send({state: 'failure', user: null, message: 'Invalid Username or Password'});
});
router.post('/login', passport.authenticate('login', {
successRedirect: '/auth/success',
failureRedirect: '/auth/failure'
}));
router.post('/signup', passport.authenticate('signup', {
successRedirect: '/auth/success',
failureRedirect: '/auth/failure'
}));
router.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
return router;
};
Server.js
var express = require('express');
var path = require('path');
var app = express();
var server = require('http').Server(app);
var logger = require('morgan');
var passport = require('passport');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var mongoose = require('mongoose');
var MongoStore = require('connect-mongo')(session);
mongoose.connect("mongodb://localhost:27017/scriptknackData");
require('./models/model');
var api = require('./routes/api');
var auth = require('./routes/auth')(passport);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(passport.session());
app.use(session({
secret: 'super secret key',
resave: true,
cookie: { maxAge: 60000 },
saveUninitialized: true,
store: new MongoStore({ mongooseConnection: mongoose.connection })
}));
var initpassport = require('./passport-init');
initpassport(passport);
app.use('/api', api);
app.use('/auth', auth);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
var port = process.env.PORT || 3000;
server.listen(port, function() {
console.log("connected");
});
As per express-session documentation
cookie.maxAge
Specifies the number (in milliseconds) to use when calculating the Expires Set-Cookie attribute. This is done by taking the current server time and adding maxAge milliseconds to the value to calculate an Expires datetime. By default, no maximum age is set.
And use express.session() before passport.session() to ensure login session is stored in correct order. passport docs
In your case you have specified maxAge as 60000ms (60sec) only. Try this:
...
app.use(session({
secret: 'super secret key',
resave: true,
cookie: { maxAge: 8*60*60*1000 }, // 8 hours
saveUninitialized: true,
store: new MongoStore({ mongooseConnection: mongoose.connection })
}));
app.use(passport.initialize());
app.use(passport.session());
...
Increase your cookie maxAge value according to your need, it will solve your issue.
I was facing the same issue as you and I got the problem fixed by doing this:
If anyone is having issues, this could probably help to solve it.
app.use(session({
secret: "our-passport-local-strategy-app",
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 24 * 60 * 60 * 1000
},
store: new MongoStore({
mongooseConnection: mongoose.connection,
ttl: 24 * 60 * 60 // Keeps session open for 1 day
})
}));
I had this problem and I found out how to fix it. In my case, this problem was just during using localhost during running react app on own port. I use the build production version, there was no problem. But it is not good to run build every time you need to see changes.
First I run Nodejs on 5000 port at localhost.
In React's package.json, I added "proxy": "http://localhost:5000/". After that, I ran react app on port 3000. Now when I use fetch, the URL to my API is not http://localhost:5000/api/login but just /api/login.
You can read more about that here:
https://create-react-app.dev/docs/proxying-api-requests-in-development/
Do not forget to remove the proxy from package.json when you will deploy to the server. This is good only for the development version.
As per the fine manual (emphasis mine):
Note that enabling session support is entirely optional, though it is recommended for most applications. If enabled, be sure to use express.session() before passport.session() to ensure that the login session is restored in the correct order.
In your case, the order is not correct. Try this:
...
app.use(session({
secret: 'super secret key',
resave: true,
cookie: { maxAge: 60000 },
saveUninitialized: true,
store: new MongoStore({ mongooseConnection: mongoose.connection })
}));
app.use(passport.initialize());
app.use(passport.session());
...

Invalidate all user sessions using express-sessions and knex

I have inherited a Express site which needs some updating. There is a route for resetting password, but I need to invalidate all the users sessions when this happen and allow for auto login on the current browser at the same time.
I'm kinda new to Express, so can anybody point me in the direction of a guide?
Setting up of sessionStore:
const sessionStore = knexSession(session)
const store = new sessionStore({
knex: db
})
app.use(session({
secret: sessionKey,
resave: false,
saveUninitialized: false,
store,
cookie: {
maxAge: null,
httpOnly: true,
secure: app.get('env') !== 'development' || app.get('port') === 443,
}
}))
Resetting password:
let data = {
password,
token: null,
expires: null
}
return models.user.update(user.id, user.id, data)
.then(_ => {
//reset session user sessions
//only invalidates the current session
req.logout()
req.session.destroy(function (err) {
res.clearCookie('connect.sid')
res.redirect('/')
});
})

New sessions being created on route with Express 4 + Passport JS (LocalStrategy) + MongoDB (Mongoose) + Sessions

Could someone explain why a new session is being created each time I make a call to my express server.
The problem is that, once a new session is created it doesn't contain the user id against the passport property.
The architecture in the frontend is 2 html pages, which I'm serving via get routes.
(1) html page is the sign in form.
(2) html page is a single page ember app which the user can only access once authenticated (working)
I get and post data via jQuery $.ajax - it's almost as if each time I make a request, I get a new session. Very odd.
partial app.js
server.use(cookieParser());
server.use(bodyParser.json());
server.use(bodyParser.urlencoded());
server.use(express.static(__dirname + '/public'));
server.use(session({
name: settings.sessionName,
secret: settings.secret,
cookie: { maxAge: 2 * 60 * 60 * 1000 },
store: new MongoStore({
db: settings.database.name
})
}));
server.use(passport.initialize());
server.use(passport.session());
require("./server/config/passport.js")(server, passport);
require("./server/routes.js")(server, passport);
passport.js
module.exports = function (server, passport) {
var LocalStrategy = require('passport-local').Strategy;
var User = require('../schemas/user');
passport.serializeUser(function (user, done) {
done(null, user._id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (error, user) {
if (!error) done(null, user);
else done(error, null);
});
});
passport.use("local", new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function (req, email, password, done) {
User.findOne({ 'emailAddress': email }, function (error, user) {
if (error) {
return done(error);
}
if (!user) {
return done(null, false);
}
if (!user.validPassword(password)) {
return done(null, false);
}
return done(null, user);
});
})
);
};
Anything will help at this point. Tearing my hair out!
:)
Okay! I found the solution.
I was not passing the session secret into the cookie parser. Problem Solved!
Here's the fix:
partial app.js
server.use(cookieParser(settings.secret));
server.use(bodyParser.json());
server.use(bodyParser.urlencoded());
server.use(express.static(__dirname + '/public'));
server.use(session({
name: settings.sessionName,
secret: settings.secret,
cookie: { maxAge: 2 * 60 * 60 * 1000 },
store: new MongoStore({
db: settings.database.name
}),
}));
The cookie parser needs to have the same secret as the express session.

Resources