I have a simple Node+Express application with Passport Local authentication, it's working nearly perfectly (the right pages are served up, login/logout works, etc).
One issue though is that if a second user logs in, that session "takes over" the first user's session and suddenly the first user is now logged in as the second user!
This is almost certainly an issue with the order of the middleware, but I haven't been able to find it - I'm fairly new to Passport and Express. The code is taken liberally from docs and examples, but I might have messed the order up.
I've followed the order in this answer but it's still not working. Can anyone suggest where I might be mixed up?
The main server:
var express = require('express');
var passport = require('passport');
var Strategy = require('passport-local').Strategy;
var url = require('url');
var db = require('./db');
// Configure the local strategy for use by Passport.
passport.use(new Strategy(
function(username, password, cb) {
db.users.findByUsername(username, function(err, user) {
if (err) { return cb(err); }
if (!user) { return cb(null, false); }
if (user.password != password) { return cb(null, false); }
return cb(null, user);
});
}));
// Configure Passport authenticated session persistence.
passport.serializeUser(function(user, cb) {
console.log('DEBUG: serializeUser called ' + user.id);
cb(null, user.id);
});
passport.deserializeUser(function(id, cb) {
console.log('DEBUG: deserializeUser called ' + id);
db.users.findById(id, function (err, user) {
if (err) { return cb(err); }
cb(null, user);
});
});
// Create a new Express application.
// Use application-level middleware for common functionality (Order is important!)
// Initialize Passport and restore authentication state, if any, from the session.
var app = express();
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'qwerty', resave: false, saveUninitialized: false })); // Step 2.
app.use(passport.initialize());
app.use(passport.session());
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login.html' }),
function(req, res) {
console.log('Logged In: ' + req.user.username);
res.redirect('/');
});
app.get('/logout', function(req, res){
if (req.user != null) {
console.log('Logged Out: ' + req.user.username);
req.logout();
} else {
console.log('Warning: null user logging out');
}
res.redirect('/login.html');
});
app.get('/user', function(req, res){
if (req.user != null) {
res.send(req.user.displayName);
} else {
res.send('ERROR: No current user?');
}
});
function isAuthenticated(req, res, next) {
if (req.user) { // User is logged in
return next();
}
if (req.url.startsWith('/login')) { // Allow login through to avoid infinite loop
return next();
}
res.redirect('/login.html');
}
app.use('/', isAuthenticated, express.static('/public/'));
The db/users.js file:
var records = [
{ id: 1, username: 'jill', password: 'birthday', displayName: 'Jill'}
{ id: 2, username: 'jack', password: 'hello', displayName: 'Jack'}
];
exports.findById = function(id, cb) {
process.nextTick(function() {
var idx = id - 1;
if (records[idx]) {
cb(null, records[idx]);
} else {
cb(new Error('User ' + id + ' does not exist'));
}
});
}
exports.findByUsername = function(username, cb) {
process.nextTick(function() {
for (var i = 0, len = records.length; i < len; i++) {
var record = records[i];
if (record.username === username) {
return cb(null, record);
}
}
return cb(null, null);
});
}
Tumbleweeds!
So it turns out that it was working fine - the problem was just that Chrome reuses its session across tabs. I found a helpful comment buried in this question's answer. One login from Chrome and another from IE works fine, no conflict.
Node + Passport - multiple users
Related
I am writing an Express app using Passport and PostgreSQL for account creation and login.
All of the database parts are working fine but when it gets to passport.authenticate nothing seems to happen.
I've tried modifying my code to better match examples I found online but haven't been able to get anything back from any of the print statements.
Here's some of the code from my index.js file:
const express = require("express");
const bodyParser = require("body-parser");
const session = require("express-session");
const { response } = require("express");
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcrypt");
const db = require("./dbManager.js")
const app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(express.static(__dirname + "/Public"));
app.use(session({
secret: "example",
saveUninitialized: true,
resave: true
}));
app.use(passport.initialize());
app.use(passport.session());
//Uses pasport to authenticate user
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
}, function (email, password, done) {
console.log("Logging in ", email);
//This sends the query to the db
db.findUser(email).then(function (row) {
if (!row) {
return done(null, false, "Sorry that user doesn't exist");
} else {
//This compairs the passwords
bcrypt.compare(password, row.password, function (ex, result) {
//Checks if they match
if (result == true) {
//Sets up user
done(null, row.id);
} else {
return done(ex, false, "Incorrect password");
}
})
}
}).catch(function (ex) {
console.error(ex);
return done(ex, false);
});
}));
passport.serializeUser(function (user, done) {
console.log(user, done);
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
db.deserializeUser(id).then(function (row) {
console.log("User found id is:", row.id);
done(null, row.id)
}).catch(function (ex) {
console.error(ex);
})
});
app.listen(3000, function () {
console.log('Server running on port 3000');
});
app.get("/createAccount", function (req, res) {
res.sendFile(__dirname + "/Public/Create Account/createAccount.html");
});
app.post("/createAccount", function (req, res) {
const email = req.body.email;
const password = req.body.password;
bcrypt.hash(password, 10, function (ex, hashedPassword) {
if (ex != null) {
console.log(ex);
}
db.addUser(email, hashedPassword).then(function () {
console.log("New user added");
res.status(204).send("user added");
}).catch(function (ex) {
console.error(ex);
res.status(403).send(ex);
});
});
});
app.get("/logIn", function (req, res) {
res.sendFile(__dirname + "/public/Log In/login.html");
});
app.post("/logIn", function (req, res) {
console.log("Logging user in");
passport.authenticate("local", function (ex, user, info) {
console.log("Authentication");
if (ex) {
console.error(ex);
res.status(403).send(ex);
} else if (!user) {
console.log(info);
res.status(400).send(info);
} else {
req.logIn(user, function (ex) {
if (ex) {
console.error(ex);
res.status(400).send(ex);
} else {
console.log("User now authenticated");
res.status(200).send("Success");
}
})
}
});
});
Here is code from my createAccount.js script:
//This will run when the form is submitted
$("#submitBtn").click(function() {
//prepares data to be sent
body= {
email:$("#email")[0].value,
password:$("#password")[0].value
};
//This sends the post request to the server
$.post("/createAccount",body).done(accountCreated(body)).fail(accountNotCreated);
});
function accountCreated(body) {
console.log("User Added");
console.log(body.email);
//Sends post request to log user in
$.post("/logIn",body).done(function() {
console.log("You are now logged in");
window.location.replace("/");
}).fail(function(ex) {
console.error(ex);
});
}
function accountNotCreated(ex) {
console.error(ex);
}
The log output for this is as follows:
Logging user in
New user added
I'm not sure why this is in the wrong order either.
The problem here is that you are running the accountCreated function before you make the post to /createAccount. This line is the culprit
$.post("/createAccount",body).done(accountCreated(body)).fail(accountNotCreated);
This line of code is supposed to be setting up the accountCreated function to run after the call to $.post finishes successfully. However, because you're including the brackets after the function it's executing the function immediately and using whatever is returned as the handler for when they post completes.
You can fix this by wrapping the call to accountCreated in an anonymous function.
$.post("/createAccount",body).done(function() {
accountCreated(body)
}).fail(accountNotCreated);
I hope that helps.
I downloaded one sample project from internet. Below there are some fragments of the code:
On the routes file I have the following (just a fragment):
var authController = require('./controllers/authController'),
var passport = require('passport');
var authLoginFacebook =
passport.authenticate(
'facebook',
{
session: false,
scope: ['public_profile', 'email']
}
);
var checkJwt = function(req, res, next) {
passport.authenticate(
'jwt',
{session: false },
function (err, user, info) {
next();
}
)(req, res, next);
}
module.exports = function(app) {
// ...
app.get(
'/api/auth/login/facebook/callback',
checkJwt,
authLoginFacebook,
authController.login
);
// ...
}
On the passport file I have the following (just a fragment):
var User = require('../models/user');
var credentials = require('./credentials');
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var LocalStrategy = require('passport-local').Strategy;
var FacebookStrategy = require('passport-facebook').Strategy;
module.exports = function(passport) {
passport.use(
new JwtStrategy({
secretOrKey: credentials.secret,
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('JWT'),
},
function(payload, done) {
User.findById(
payload._id,
function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
}
);
}
)
);
var fbStrategy = credentials.facebook;
fbStrategy.passReqToCallback = true;
passport.use(new FacebookStrategy(fbStrategy,
function(req, token, refreshToken, profile, done) {
// asynchronous
process.nextTick(function() {
// check if the user is already logged in
if (!req.user) {
User.findOne({
'facebook.id': profile.id
}, function(err, user) {
if (err)
return done(err);
if (user) {
// if there is a user id already but no token (user was linked at one point and then removed)
if (!user.facebook.token) {
user.facebook.token = token;
user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
user.facebook.email = (profile.emails[0].value || '').toLowerCase();
user.save(function(err) {
if (err)
return done(err);
return done(null, user);
});
}
return done(null, user); // user found, return that user
} else {
// if there is no user, create them
var newUser = new User();
newUser.facebook.id = profile.id;
newUser.facebook.token = token;
newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
newUser.facebook.email = (profile.emails[0].value || '').toLowerCase();
newUser.save(function(err) {
if (err)
return done(err);
return done(null, newUser);
});
}
});
} else {
// user already exists and is logged in, we have to link accounts
var user = req.user; // pull the user out of the session
user.facebook.id = profile.id;
user.facebook.token = token;
user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
user.facebook.email = (profile.emails[0].value || '').toLowerCase();
user.save(function(err) {
if (err)
return done(err);
return done(null, user);
});
}
});
})
);
// ...
};
I have few questions here:
why on: passport.authenticate('jwt', ... are passed these arguments: (req, res, next) and on passport.authenticate('facebook', ... don't while they are used in the same line one next to other?
app.get(
'/api/auth/login/facebook/callback',
checkJwt,
authLoginFacebook,
authController.login
);
If I remove those arguments, then the web page keeps loading indefinitely.
why inside: passport.use(new FacebookStrategy is defined: req.user? where was declared the field: user for the object req?
Thanks!
*Edit: this is a function invoking another function...which is required because of the callback using next(). The facebook function doesn't have that.
In other words, when you invoke passport.authenticate, the return value is actually going to be a function expecting the parameters req, res, next. Normally you don't need to wrap it, because it just works. However, in this case there is a callback function being passed in as an argument, and that callback function needs access to the next parameter. So you have to wrap the whole thing to gain access to that next parameter.
*Note: the wrapped function isn't actually returning anything to/through the wrapping function. passport.authenticate() returns a function, and then this return function is self-invoked with the parameter group that follows. But this second self-invoked function result isn't captured as a variable or returned or anything.
The reason is that the important thing is either sending a response using the res parameter or allowing express to continue to the next layer of middleware/etc by calling the next() callback parameter. It's all happening asynchronously, and flow is directed using the callbacks.
var checkJwt = function(req, res, next) {
passport.authenticate(
'jwt',
{session: false },
function (err, user, info) {
next();
}
)(req, res, next);
}
the req.user would be a user from a previous login, which i believe passport.js normally stores using the express-session package.
I'm new to saml and using Nodejs + Express + passport-saml + okta identity provider. I know this is a duplicate question but somehow I am not able to solve this by looking lot of threads on the internet.
I used yeoman express generator for project. Here are my settings:
Server is behind ngnix using https. So, if I hit https://mywebsite.com, it redirects internally to localhost:3000 on that server.
express.js
var samlUtil = require('./saml-util.js');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
limit: '2mb',
extended: true
}));
app.use(compress());
app.use(cookieParser(config.SERVER_KEYS.SERVER_SECRET));
app.use(express.static(config.root + '/public'));
app.use(methodOverride());
app.use(session({
secret: config.SERVER_KEYS.SERVER_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
expires: false,
secure: true
}
}));
app.use(samlUtil.initialize());
app.use(samlUtil.session());
app.get('/saml/response', samlUtil.protected, function(req, res) {
res.end("Hello " + req.session.passport.user);
});
app.get('/saml/invalid', function(req, res) {
res.end("Authentication failed");
});
app.post('/saml/callback', samlUtil.authenticate('saml', {
failureRedirect: '/saml/response/',
failureFlash: true
}), function(req, res) {
req.session.save(function() {
res.redirect('/saml/response/');
})
});
app.get('/saml/login', samlUtil.authenticate('saml', {
failureRedirect: '/saml/response/',
failureFlash: true
}), function(req, res) {
res.redirect('/saml/response/');
});
saml-util.js
var path = require('path');
var passport = require('passport');
var root = path.normalize(__dirname + '/../..');
var constant = require(root + '/app/util/constants.js');
var config = require(constant.APP_CONFIG_FILE);
var SamlStrategy = require('passport-saml').Strategy;
var users = [];
function findByEmail(email, fn) {
for (var i = 0, len = users.length; i < len; i++) {
var user = users[i];
if (user.email === email) {
return fn(null, user);
}
}
return fn(null, null);
}
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
passport.serializeUser(function(user, done) {
console.log('serializing');
done(null, user.email);
});
passport.deserializeUser(function(id, done) {
console.log('de-serializing');
findByEmail(id, function(err, user) {
done(err, user);
});
});
passport.use(new SamlStrategy({
issuer: config.SAML.ISSUER_URL,
path: config.SAML.PATH,
entryPoint: config.SAML.ENTRY_POINT,
cert: config.SAML.CERTIFICATE,
}, function(profile, done) {
console.log('got profile');
console.log(profile);
if (!profile.email) {
return done(new Error("No email found"), null);
}
process.nextTick(function() {
console.log('Finding by email');
findByEmail(profile.email, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
console.log('new user');
users.push(profile);
return done(null, profile);
}
console.log('existing user');
return done(null, user);
})
});
}));
passport.protected = function protected(req, res, next) {
console.log('is isAuthenticated =' + req.isAuthenticated());
if (req.isAuthenticated()) {
return next();
}
res.redirect('/saml/invalid');
};
exports = module.exports = passport;
What is happening:
I can hit the URL: /saml/login
Gets redirected to the okta login page (where I have identity settings)
I login successfully
I'm redirected to the URL: /saml/callback with response:
{issuer:
{ _: 'http://www.okta.com/exkctyzcknbMikNjl0h7',
'$':
{ Format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity',
'xmlns:saml2': 'urn:oasis:names:tc:SAML:2.0:assertion' } },
sessionIndex: '_3acb290873febaf825cd',
nameID: 'ashutosh#myemail.com',
nameIDFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
nameQualifier: undefined,
spNameQualifier: undefined,
firstName: 'Ashutosh',
lastName: 'Pandey',
email: 'ashutosh#myemail.com',
getAssertionXml: [Function] }
In the /saml/callback URL, I can see value returned in req.user but
req.isAuthenticated() in saml-util is always returning false.
I need some help with setting up Passport Consumer Strategy and integrating it into "locals" (Right now, we have the local strategy working just fine). We have tried several approaches but with no luck on it working 100%. The below code is not the complete code, we have taken out some of it so this post doesn't get too long. Any help with this would greatly be appreciate. There could be compensation as well if someone can get us over this hurdle.
So one question is, if the user is authenticated by the consumer key and secret, how does Passport store the session variables so they are used throughout the site?
Second question, how do we handle the user after it passes the authentication process?
Both local and consumer need to be working.
Consumer key and secret using a POST by a Provider <- I can show some of the post if needed.
This needs to be OAuth1 Only, as of right now, OAuth2 isn't an option.
This is for a single sign-on authentication.
I can supply a consumer session output if needed.
Ultimately, we would like the local strategy and the consumer strategy working with the same "locals" global variables. As far as I can tell, we can authenticate the consumer, retrieve the user from our DB, create a session and can tell if user is "ensureAuthenticated".
Here is what we have working right now.
Local strategy is authenticating correctly.
We render the pages with these local variables:
"Omitted most of the code to save time."
//=================================================================
// The Authentication Module will bind to POST /login route
//=================================================================
authentication.initLocalStrategyRoutes(app);
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
...
function renderPage(req, res, pageName, pageTitle){
res.render(pageName, {
pageName: pageTitle,
username: req.user ? req.user.username : '',
...
Consumer strategy is authenticating by a POST request from a "Provider"
We have tried adding the Consumer strategy to the authentication.
server.js
//=================================================================
// The Authentication Module will bind to POST /login route
//=================================================================
authentication.initLocalStrategyRoutes(app);
ADDED -> authentication.initConsumerStrategyRoutes(app);
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
ADDED -> passport.authenticate('consumer', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
authentication.js (omitted code)
module.exports = function(siteConfig, defaultRedirectPage, server, sessionStore, log) {
var passport = require('passport')
...
, ConsumerStrategy = require('passport-http-oauth').ConsumerStrategy
, TokenStrategy = require('passport-http-oauth').TokenStrategy
, LocalStrategy = require('passport-local').Strategy;
var auth = {};
var authenticationRedirects = { successRedirect: '/', failureRedirect: '/login' };
passport.serializeUser(function(user, done) {done(null, user);});
passport.deserializeUser(function(obj, done) {done(null, obj);});
auth.authenticate = function(email, password, callback) {
email = email.toLowerCase();
userController.findUserByUsernameWithPermissions(email,
function(err, user) {
if (err) return callback(err);
if (!user) return callback(null, null, 'Incorrect username.');
bcrypt.compare(password, user.password_hash, function(err, res) {
if(err){return callback(err);
} else if (!res) {return callback(null, null, 'Incorrect password.');
} else {if (user.account_state>0) {callback(null, user);} else {return callback(null, null, '/reset?rand='+user._id);}}
});
}
);
}
auth.initLocalStrategyRoutes = function(app){
passport.use(new LocalStrategy(auth.authenticate));
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) return next(err);
if (!user) return res.send({success: false, message: info});
req.logIn(user, function(err) {
if (err) { return next(err); }
res.send(req.user);
});
}) (req, res, next);
});
}
auth.initConsumerStrategyRoutes = function(app){
// passport.use(new LocalStrategy(auth.authenticate));
console.log('app: ', app)
passport.use('consumer', new ConsumerStrategy(
function(key, done) { console.log('starting ConsumerStrategy');
dbConsumerKey.findByConsumerKey({consumerKey: key}, function(err, consumerKey) {
if (err) { return done(err); }
if (!consumerKey) {
var errCode = dbError.find({name:'no_resource_link_id'}, function(err, errorCodes) {
console.log('statusText: ', errorCodes[0]["statusText"]);
return errorCodes[0]["statusText"];
});
return done(null, errCode);
} else {
if (!consumerKey[0]["consumerKey"]) { return done(err); }
if (!consumerKey[0]["consumerSecret"]) { return done(err); }
// return done(null, consumerKey[0]["consumerKey"], consumerKey[0]["consumerSecret"]);
return done(null, consumerKey[0], consumerKey[0]["consumerSecret"]);
}
});
},
function(requestToken, done) {
dbRequestTokens.find(requestToken, function(err, token) {
console.log('inside requestToken');
if (err) { return done(err); }
var info = { verifier: token.verifier,
clientID: token.clientID,
userID: token.userID,
approved: token.approved
}
done(null, token.secret, info);
});
},
function(timestamp, nonce, done) {
done(null, true)
}
));
};
auth.initTokenStrategyRoutes = function(app){}
auth.addUser = function(username, email, password, callback){auth.authenticate(username, "pass", callback);}
return auth;
};
The authentication.js strategy does validate the consumer key and secret. but it doesn't create the session variable we are wanting. We would like the consumer strategy code to be in the authentication.js file.
Now here is another approach, we created a separate files called consumerkey.js
This direction works to a point. We can output the passport session either on the screen or on the command line.
var passport = require('passport')
exports.launchLti = [
passport.authenticate('consumer', { session: false/true [tried both] }),
function(req, res) {
db.findByStudentUserId({lis_person_contact_email_primary:
req.body.lis_person_contact_email_primary}, function(err, user) {
req.logIn(user, function(err) {
req.user.username = user[0].lis_person_contact_email_primary;
...
// req.session.save(function(){
// res.redirect('/classes');
res.redirect(200,'/');
// });
});
})
// res.render('launch', {launch: launch});
}
}]
I solved this issue by changing some of my code structure.
app.pst('/launch/lti/:id', function(req, res, next) {
passport.authenticate('consumer', {failureRedirect: '/login'}),
dbConsumerKey.findByStudentUserId({},
function(err, user) {
if (err) console.log(err, user);
req.logIn(user, function(err) {
if (err) return err;
ADDED -> req.session.valid = true;
ADDED -> res.redirect('/');
});
}
});
});
and modifying the render page function to adapt to the incoming information.
function renderPage(req, res, pageName, pageTitle){
...
this is where the locals are created
...
this allowed me to use my current local strategy as is and adding a totally different strategy route but making the session correctly.
I need a "Remember Me" checkbox when logging in like this .
And I add a middleware before using passport
app.use(function(req, res, next) {
if (req.method == 'POST' && req.url == '/login') {
if (req.body.rememberme) {
req.session.cookie.maxAge = 1000 * 60 * 3;
} else {
req.session.cookie.expires = false;
}
}
next();
});
app.use(passport.initialize());
app.use(passport.session());
I can not login when req.body.rememberme is true and the user is remembered when req.body.rememberme is false.
I also tried connect-ensure-login and it still wrong.
and another question: when should I delete the cookies in my database and how?
:)
Other code is exactly the same as the passport guide
route:
app.get('/', passport.authenticate('local', {
failureRedirect: '/login'
}), function(req, res) {
res.redirect('/user/home');
});
app.post('/login', passport.authenticate('local', {
failureRedirect: '/login'
}), function(req, res) {
res.redirect('/user/home');
});
sessions:
passport.serializeUser(function(user, done) {
var CreateAccessToken = function() {
var token = user.GenerateSalt();
User.findOne({
accessToken: token
}, function(err, existingUser) {
if (err)
return done(err);
if (existingUser) {
CreateAccessToken();
} else {
user.set('accessToken', token);
user.save(function(err) {
if (err)
return done(err);
return done(null, user.get('accessToken'));
})
}
});
};
if (user._id)
CreateAccessToken();
});
passport.deserializeUser(function(token, done) {
User.findOne({
accessToken: token
}, function(err, user) {
if (err)
return done(err);
return done(err, user);
});
});
and the strategie:
passport.use(new LocalStrategy(function(userId, password, done) {
User.findOne().or([{
username: userId
}, {
email: userId
}]).exec(function(err, user) {
if (err)
return done(err);
if (!user) {
return done(null, false, {
message: 'Invalid password or username'
});
}
if (user.Authenticate(password)) {
return done(null, user);
} else {
return done(null, false, {
message: 'Invalid password or username'
});
}
});
}));
I noticed that Express will update the cookie only when hash value changed. so I have modified the code in the middleware
app.use(function(req, res, next) {
if (req.method == 'POST' && req.url == '/login') {
if (req.body.rememberme) {
req.session.cookie.maxAge = 1000 * 60 * 3;
req.session._garbage = Date();
req.session.touch();
} else {
req.session.cookie.expires = false;
}
}
next();
});
now I can login with "Remember Me", but it only works on chromium and firefox on Ubuntu. I still can not login with the "Remember Me" checkbox on chrome and firefox on Win7 and Android.
I checked response header when POST to "/login" on chrome on win7 and it had the same "Set-Cookie" field as it on Ubuntu, why it can not work?
Time is out of sync...so I post a extra time field.
$('#login').ajaxForm({
beforeSubmit: function(arr, $form, option) {
arr.push({
name: '__time',
value: (new Date()).toGMTString()
});
}
});
and the "RememberMe" middleware:
app.use(function(req, res, next) {
if (req.method == 'POST' && req.url == '/login') {
if (req.body.rememberme) {
req.session.cookie.maxAge = moment(req.body.__time).add('m', 3) - moment();
req.session._garbage = Date();
req.session.touch();
} else {
req.session.cookie.expires = false;
}
}
next();
});
I had exactly the same problem as you. The following code works for me:
app.post("/login", passport.authenticate('local',
{ failureRedirect: '/login',
failureFlash: true }), function(req, res) {
if (req.body.remember) {
req.session.cookie.maxAge = 30 * 24 * 60 * 60 * 1000; // Cookie expires after 30 days
} else {
req.session.cookie.expires = false; // Cookie expires at end of session
}
res.redirect('/');
});
There is now a Passport strategy for adding Remember Me functionality writen by Jared Hanson.
https://github.com/jaredhanson/passport-remember-me
This works by issuing a unique remember-me token during every session that is consumed at the start of the next (invalidating it for future use.) As a result, this is likely to be a more secure solution.
Ensure that app.use(express.bodyParser()) is placed above your middleware, as you are relying on req.body.rememberme