I have a Node.js server and I am using passport for session management. Planning on using it to implement remember me functionality for the server soon. But I have a problem where the normal sessions are basically acting as a remember me. Aka the session cookie connect.sid is not getting destroyed on browser close which I believe is not the correct behavior. I am using connect-bluesky as the session store (uses azure tables) and couchbase for storing default session information for future use.
Relevant code:
Server.js
app.configure(function()
{
app.use(express.logger('dev')); // log every request to the console
// set up our express application
app.use(express.cookieParser());
app.use(express.json())
.use(express.urlencoded());
app.use(express.static(__dirname));
var db = new couchbase.Connection({host: 'http://localhost:8091', bucket: 'default'}, function (err){
console.log("In cb connect");
console.log(err);
});
app.use(express.session({secret: 'SECRET',
store: new BlueskyStore({
account: 'ACCOUNT',
key: 'KEY',
table: 'sessionsTable',
cookie: { path: '/', httpOnly: true, maxAge: null }
})
}));
app.engine('html', engines.ejs);
//app.set('views', __dirname+'/views');
app.set('view engine', 'html');
var data = fs.readFileSync( __dirname+"/"+process.argv['2'],'utf8');
GLOBAL.infoJSON = JSON.parse(data);
require('./config/passport')(passport, infoJSON);
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
app.use(app.router);
});
Routes.js
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/');
}
app.post('/login2', passport.authenticate('local-login'),
function(req, res)
{
var input = "irrelevant input string";
var loginInfo = {
input : input,
userName : req.user.id,
repoId : req.user.repoId
}
edgeMod.loginWF(loginInfo, req, res, function (req, res, result)
{
res.write(result);
res.end();
});
});
app.get('/logout', function(req, res)
{
req.logout();
res.redirect('/');
});
passport.js
passport.serializeUser(function(user, done) {
db.set(user.id, user, function(err, result) {
done(null, JSON.stringify(user));
});
});
passport.deserializeUser(function(json, done)
{
var user = JSON.parse(json);
db.get(user.id, function(err, result)
{
if (result)
{
done(null, result.value);
}
else
{
done(new Error("Bad JSON string in session"), null);
}
});
});
passport.use('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done)
{
---login checks---
return done(null, serializeJson);
});
}));
How do I ensure sessions get destroyed on browser close? Any tips for doing this as its my 1st time doing something like this?
EDIT: It seems to be clearing the session on browser close in Firefox but not in chrome. Could this have something to do with chrome remembering pages on close?
Having "Continue where you left off" ticked in chrome options seems to be the problem. Doesn't clear the session on browser close with this ticked.
Related
I'm building a simple site with expressjs and passportjs and I'm running into the problem that I can't access session variables in my routes.
There are a lot of threads with this topic but the solutions don't work for me. It looks like my error is somewhere else.
My app is configured like this:
app.configure(function() {
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({
path: '/',
secret: 'very secret'
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
Once passport verified the twitter account it's redirected to this site:
app.get('/auth/twitter/callback',
passport.authenticate('twitter', {
failureRedirect: '/login'
}),
function(req, res) {
res.redirect('/');
console.log("populate session")
populateSession(req, res);
});
This works, as I'm seeing the "populate session" output in the console if I'm logged in.
The populateSession() looks like this:
function populateSession(req, res) {
User.findOne({
twitterID: req.user.id
}, function(err, result) {
if (result) {
// Store data to session
req.session.twitterAccessToken = result.twitterAccessToken;
req.session.twitterAccessTokenSecret = result.twitterAccessTokenSecret;
req.session.lastfmAccountName = result.lastfmAccountName;
console.log("in session: " + req.session.lastfmAccountName)
}
})
}
"in session" is printed the right way. So the session itself works. Now my problem is that I want to have access to my session variables in routes because I want to pass them to my view templates like this:
app.get('/account', ensureAuthenticated, function(req, res) {
console.log(req.session)
console.log("lastfm nick " + req.session.lastfmAccountName)
res.render('account', {
user: req.user,
lastfmnick: req.session.lastfmAccountName
});
That's where I'm running into the problems. req.session contains all the twitter fields passport is populating it with but req.session.lastfmAccountName is undefined.
Any idea what's wrong there or is there a better way to pass variables to the view? I feel like it's not a good idea to have DB queries for the fields in all my routes if it could just be stored in the session.
Thanks!
The session will automatically be saved when the response ends, in this case by res.redirect(), which is done before the modifications are made to the session.
function(req, res) {
res.redirect('/'); // ends the response, saving the session
populateSession(req, res); // modifies the session without saving
});
Since .findOne() is asynchronous, if you revise populateSession() to take and call a callback when the find completes, you can control the order so the session is modified first:
function populateSession(req, res, next) {
User.findOne({
twitterID: req.user.id
}, function(err, result) {
if (result) {
// ...
}
if (next) next(err);
})
}
app.get('/auth/twitter/callback',
/* ... */,
function(req, res) {
populateSession(req, res, function () {
res.redirect('/');
});
});
It also allows you to use populateSession as middleware:
app.get('/auth/twitter/callback',
/* ... */,
populateSession,
function(req, res) {
res.redirect('/');
});
app.use(lib.express.cookieParser(lib.config.cookieSecret));
app.use(lib.express.session({
secret: 'very secret'
})
}));
This two line should be consecutive and in that order.
I have some login page, as soon as the authenticated get success, in the next page, I should display Login Details.. ( ie., username and password )
I'm using Express NodeJs, Passport, connect-flash, mongoose
My prob is: I'm unable to understand how to retrieve username, password in the next rendered page..
Please someone suggest me how to achieve it. I'm not asking you code, BUT show me a way to get my output.
EDIT:
app.js
var http = require('http');
var express = require('express'),
passport = require('passport')
, LocalStrategy = require('passport-local').Strategy,
flash = require('connect-flash'),
User = require('./routes/userdao.js');
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ uname: username ,pwd:password}, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
return done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
var app = express();
app.configure(function(){
app.use(express.logger('dev')); /* 'default', 'short', 'tiny', 'dev' */
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'mysecret' }));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
app.engine('.html', require('ejs').__express);
app.set('view engine', 'html');
app.set('views', __dirname + '/views');
app.use(app.router);
// app.use(express.errorHandler());
}).listen(3000);
console.log('Listening on port 3000...');
app.get('/',function(req,res){
/* req.flash('info', 'Flash is back!')
res.redirect('/success');*/
res.render('home.ejs');
});
app.get('/login',function(req,res){
res.render('login.ejs');
});
app.get('/success',function(req,res){
res.render('success.ejs',{ uname : req.user.username });
});
app.post('/login',
passport.authenticate('local', { successRedirect: '/success', failureRedirect: '/login',
failureFlash: true }));
success.ejs
<%= uname %>
I'm trying to display username which I entered in the login page.. I'm getting output: undefined
please tell me what went wrong and what I need to correct?
In the most general sense, if authentication succeeds the user object you return from authenticate ends up at req.user in your downstream functions.
http://passportjs.org/guide/authenticate/
And then, assuming you are using sessions, any subsequent requests will also expose req.user based on your serializeUser/deserializeUser functions.
I have a server in node.js using express and passport with the passport-local strategy.
I have the users in the database and through passport I'm able to authenticate them, unfortunately when a second request comes from the same client the req.isAuthenticated() method returns false.
There is also no user in the request (req.user = undefined).
I've also checked and when doing the authentication although I get back a user from passport.authenticate('local'... I do not get req.user populated then. If I try to set it up manually it just doesn't propagate for following requests.
I don't understand what I'm doing wrong, here is my code.
server.js
var express = require('express'),
compass = require('node-compass'),
routes = require('./server/routes')
http = require('http'),
path = require('path'),
passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
Database = require('./server/repositories/database'),
Configuration = require('./server/config').Config,
crypto = require('crypto');
var app = express();
app.enable("jsonp callback");
passport.use(new LocalStrategy(
function(email, password, done) {
process.nextTick(function () {
var userService = new UserService();
userService.login(email, crypto.createHash('md5').update(password).digest("hex"), function(error, user) {
if (error) done(error, user);
else if (!user) return done(null, false, { message: 'wrong credentials'});
return done(null, user);
});
});
}
));
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
var userService = new UserService();
userService.findById(id, function(err, user) {
done(err, user);
});
});
app.configure(function(){
app.set('port', Configuration.Port);
app.set('views', __dirname + '/app/views');
app.set('view engine', 'ejs');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(compass({
project: path.join(__dirname, 'app'),
sass: 'styles'
}));
app.use(express.session({ secret: 'keyboard cat' }));
app.use(function(err, req, res, next){
console.error(err.stack);
res.send(500, 'Something broke!');
});
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'app')));
});
routes.configure(app);
Database.open(function() {
app.listen(Configuration.Port, function() {
console.log("Express server listening on port " + Configuration.Port);
});
});
routes.js
var Configuration = require('./config').Config;
var ApiResult = require('../model/apiResult').ApiResult;
var ApiErrorResult = require('../model/apiErrorResult').ApiErrorResult;
var ApiReturnCodes = require('../model/apiReturnCodes').ApiReturnCodes;
var passport = require('passport');
var usersController = require('./controllers/usersController');
exports.configure = function(app) {
function ensureAuthenticated(req, res, next) {
console.log(req.isAuthenticated());
if (req.isAuthenticated()) { return next(); }
else {res.send(new ApiErrorResult(ApiReturnCodes.NOT_LOGGED_IN, null));}
}
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err || !user) { console.log(info); res.send(new ApiErrorResult(ApiReturnCodes.ENTITY_NOT_FOUND, null)); }
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user
else res.send(new ApiResult(user));
})(req,res,next);
});
app.get('/anotherLink', ensureAuthenticated, function(req, res, next) {
res.json({Code:0});
});
}
When I hit the link /anotherLink after being authenticated I get res.isAuthenticated() as false.
Also when I see the req.session after the ensureAuthenticated is called I get:
{ cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
passport: {} }
What am I missing for it to save the information that that user is authenticated?
On the client side I'm using Angular only doing a simple get with the url without parameters.
If I forgot to put something here just tell me, I'll update it.
Any help will be greatly appreciated. Thanks
So I found out what was wrong with my code.
My passport.deserializeUser method used the method userService.findById
And that called the repository... like this:
userRepository.findUnique({"_id": id}, callback);
because the id was generated by MongoDB the correct call needs to be:
userRepository.findUnique({"_id": new ObjectID(id)}, callback);
I hope this saves some time to the next person with the same problem.
With this detail, this code should work nicely for everyone wanting to use the LocalStrategy on the Passport framework.
I'm using express and passport module for authentication. I tried in different ways to do redirection after session cookie expires without results.
Currently I'm trying with a schedule function after login action. I have the following code:
var express = require('express')
, passport = require('passport')
, util = require('util')
, LocalStrategy = require('passport-local').Strategy
, schedule = require('node-schedule');
var users = [
{id: 1, username: 'user', password: 'passwd'}
];
function findById(id, fn) {
var idx = id - 1;
if (users[idx]) {
fn(null, users[idx]);
} else {
fn(new Error('User ' + id + ' does not exist'));
}
}
function findByUsername(username, fn) {
for (var i = 0, len = users.length; i < len; i++) {
var user = users[i];
if (user.username === username) {
return fn(null, user);
}
}
return fn(null, null);
}
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
findById(id, function (err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy(
function(username, password, done) {
process.nextTick(function () {
findByUsername(username, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
return done(null, user);
})
});
}
));
var app = express.createServer();
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({ secret: 'keyboard cat', cookie: {maxAge: 10000}}));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/../../public'));
});
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
app.get('/login', function(req, res){
res.render('login', { user: req.user, message: 'error' });
});
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', dfailureFlash: true }),
function(req, res) {
res.redirect('/');
dateExpiration = new Date(Date.now() + 10000);
var j = schedule.scheduleJob(dateExpiration, function(){
console.log('Cookie expiration event');
// How can I redirect to login page after cookie session expires?
//redirect('/login')
});
});
app.get('/logout', function(req, res){
console.log('Logout');
req.logout();
res.redirect('/');
});
app.listen(3000);
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
Is there another way to detect session cookie expiration event in node.js so that I can redirect to another page?.
Thank you.
So, if I understand what you're asking properly, you can't really "detect" a cookie expiration per se.
If a cookie is expired, the browser will not send it to the server, which is to say, cookie expiration actually happens on the browser side, not the server side.
However, if you wanted to "fake" it, you might try something like the following psuedocode:
on "login":
set a cookie that expires in 100x \
the amount of time it takes your session to expire
on "ensureAuthenticated":
check for req.user
if i don't have req.user but have my other cookie
Fire my expiration event, which unsets the other cookie
if i don't have req.user or my other cookie
Send the user to the login page
if i've got req.user
Re-set my other cookie so that i update the expiration
Might not be 100% of what you're looking for, but should be close.
I was playing around with node.js as a beginner and trying to get the login/atuthentication running but got stuck there. I then downloaded connect-flash middleware using npm and this is how my server.js looks like:
var express = require('express'),
passport = require('passport'),
util = require('util'),
LocalStrategy = require('passport-local').Strategy,
wines = require('./routes/wines');
var users = [
{ id: 1, username: 'bob', password: 'secret', email: 'bob#example.com' }
, { id: 2, username: 'joe', password: 'birthday', email: 'joe#example.com' }
];
function findById(id, fn) {
var idx = id - 1;
if (users[idx]) {
fn(null, users[idx]);
} else {
fn(new Error('User ' + id + ' does not exist'));
}
}
function findByUsername(username, fn) {
for (var i = 0, len = users.length; i < len; i++) {
var user = users[i];
if (user.username === username) {
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) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
findById(id, function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object. In the real world, this would query a database;
// however, in this example we are using a baked-in set of users.
passport.use(new LocalStrategy(
function(username, password, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// Find the user by username. If there is no user with the given
// username, or the password is not correct, set the user to `false` to
// indicate failure and set a flash message. Otherwise, return the
// authenticated `user`.
findByUsername(username, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
return done(null, user);
})
});
}
));
var flash = require('connect-flash');
var express = require("express");
var app = express();
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
app.get('/flash', function(req, res){
req.flash('info', 'Flash is back!')
res.redirect('/');
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', { user: req.user });
});
app.get('/login', function(req, res){
res.render('login', { user: req.user, message: "Hello!"});
});
// configure Express
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({ secret: 'keyboard cat' }));
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.cookieParser('keyboard cat'));
app.use(express.session({ cookie: { maxAge: 60000 }}));
app.use(flash());
});
// POST /login
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
//
// curl -v -d "username=bob&password=secret" http://127.0.0.1:3000/login
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.listen(3000);
// Simple route middleware to ensure user is authenticated.
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
app.get('/wines', wines.findAll);
app.get('/wines/:id', wines.findById);
console.log('Listening on port 3000...');
Why do I still get that error? How to fix it?
Move all your app[METHOD] middleware to after all the app.use middleware