Express Session Does Not Save After Redirect - node.js

In my production app, saving data to a session then redirecting is completely unreliable. A console.log after saving the session shows the data has been attached. Then on redirect, another console.log shows that the session has been reset. Every 3-5 tries, the session will persist across the redirect, but it is mostly unreliable. In my development app this code works flawlessly...
• I've tried changing the version of express-session
• I've tried moving the static folder above the session middleware in server.js
• I've tried using req.session.save()
UPDATE ******
This is a known issue with the session middleware: https://github.com/expressjs/session/pull/69
Here is my server.js
// Module Dependencies
var express = require('express');
var app = express();
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var session = require('express-session');
var favicon = require('serve-favicon');
var methodOverride = require('method-override');
// Set Environment from ENV variable or default to development
var env = process.env.NODE_ENV = process.env.NODE_ENV || 'development';
var config = require('./config/config');
// Set Port
var port = process.env.PORT || config.app.port;
// Connect to our MongoDB Database
// mongoose.connect(config.db);
// set the static files location /public/img will be /img for users
app.use(express.static(__dirname + '/public'));
// Express Session
app.use(session({
secret: 'asfasfa3asfa',
resave: true,
saveUninitialized: true,
cookie: {
secure: false,
maxAge: 2160000000
}
}));
// Favicon
app.use(favicon(__dirname + '/public/img/favicon.ico'));
// Set Jade as the template engine
app.set('views', './app/views');
app.set('view engine', 'jade');
// Get req.body as JSON when receiving POST requests
app.use(bodyParser.json()); // parse application/json
app.use(bodyParser.json({
type: 'application/vnd.api+json'
})); // parse application/vnd.api+json as json
app.use(bodyParser.urlencoded({
extended: true
})); // parse application/x-www-form-urlencoded
// override with the X-HTTP-Method-Override header in the request. simulate DELETE/PUT
app.use(methodOverride('X-HTTP-Method-Override'));
// routes ==================================================
require('./app/routes')(app); // pass our application into our routes
// start app ===============================================
app.listen(port);
console.log('****** App is now running on port ' + port + ' ******'); // shoutout to the user
exports = module.exports = app; // expose app
Here is the controller where the session is being saved:
// Module dependencies.
var config = require('../../config/config');
// Render Either Home Page or Dashboard Page If User is Logged In
var index = function(req, res) {
console.log("Session At Home Page: ", req.session)
if (req.session.user) {
res.render('dashboard');
} else {
res.render('home');
}
};
// Handle Authentication Callback
var callback = function(req, res) {
// Get Access Token via Service-SDK
Service.getAccessToken(req, function(error, tokens) {
if (error) {
console.log(error);
return res.redirect('/');
}
// Otherwise, Save User Data & API Tokens To Session
req.session.regenerate(function(err) {
req.session.user = tokens.user_id;
req.session.access_token = tokens.access_token;
req.session.client_token = tokens.client_token;
req.session.save(function(err) {
console.log("Session Before Redirect: ", req.session);
res.redirect('/');
})
});
});
};
module.exports = {
index: index,
callback: callback
};
My Routes
app.get('/auth/service/callback', application.callback)
app.get('/logout', application.logout);
app.get('/', application.index);

There is a conflict between the livereload and the express-session in the Express 4. You can read more about it here https://github.com/expressjs/cookie-session/issues/14
But in short, the livereload must be called AFTER the session. As this:
var express = require("express");
var http = require("http");
var session = require("express-session");
var livereload = require("connect-livereload");
var app = express();
app.set("port", 9090);
/**
* First you must config the session
*/
app.use(session({
secret: "keyboardcat",
name: "mycookie",
resave: true,
saveUninitialized: true,
cookie: {
secure: false,
maxAge: 6000000
}
}));
/**
* Then you can config the livereload
*/
app.use(livereload())
/**
* This simple url should create the cookie.
* If you call the livereload first, the cookie is not created.
*/
app.get("/",function(req,res){
req.session.name = "blaine";
res.end("ok");
});
/**
* If you call the livereload first, the session will always return nothing
* because there is no cookie in the client
*/
app.get("/me",function(req,res){
var name = req.session.name;
var message = "Hello [" + name + "]!";
res.end(message);
});
http.createServer( app ).listen(
app.get("port"),
function(){
console.log("server is running in port ", app.get("port"));
}
)
If you call the livereload BEFORE the session config, all will seems to work but the cookie will not persist. This was a tricky bug and I lost a full day into it. I hope this help somebody.

Related

ReferenceError: done is not defined

const express = require('express');
const cookieParser = require('cookie-parser');
const port = 8000;
const app = express();
const expressLayout = require('express-ejs-layouts');
const db = require('./config/mongoose')
// used for session cookie
const session = require('express-session')
const passport = require('passport')
const passportLocal = require('./config/passport-local-strategy')
app.use(express.urlencoded())
app.use(cookieParser());
// where to look static files like css,js
app.use(express.static('./assets'))
// this line must be above the routes line (line no. 11 in this case) because in the routes all the views are going to be render and before that we have to tell to the browser the layout
app.use(expressLayout)
// extract style and scripts from sub pages into the layout
app.set('layout extractStyles', true);
app.set('layout extractScripts', true);
// set up the view engine
app.set('view engine', 'ejs');
app.set('views', './views');
app.use(session({
name: 'iFacebook',
// TODO change the secret before deployment in production mode
secret: 'Coder',
saveUninitialized: false,
resave: false,
cookie: {
maxAge : (1000*60*100)
}
}))
app.use(passport.initialize());
app.use(passport.session())
// use express router
// require('./routes/index) is similar to require('./routes) in this case, it by default fetch routes
app.use('/', require('./routes/index'))
app.listen(port, (err) => {
if (err) {
console.log(`Error in running the server : ${err}`);
}
console.log(`Server is listening at ${port}`);
})
I am using passport and passport-local strategy and this error comes and even i did not know from which file this error comes. I am sharing the index.js file code which is the server file. This is the first time i am using this even on the documentation i did not found anything

Cannot GET when using forever with node and express

We are using NodeJS and Express in combination with forever. That worked fine, but now we had to update our NodeJS version and it all stops working.
We use angular with ui-routing for frontend routing so we have an static folder.
I can go to our homepage (/) and from there I can navigate to the whole site. But when I refresh an page or I go directly to an page (eg. /products) I get an
Cannot GET /products
error.
Node gives an 404 error.
When I run the script directly without forever everything works fine. As you can see we have 2 static folders configured in express. Before the update everything works fine.
We also use Apache to redirect custom domainnames to specific pages without changing the address in the browser, but that works fine (only shows the GET error instead of the page).
Anybody any idea how to solve this?
our app.js
var path = require('path');
var fs = require('fs');
// Return values from different config files will be added to this object so you can call config.[category].[property].
config = {};
// Save the app root directory to a global variable so it can be used in config files and other parts of the app. global.root is reserved, but global.path.root can be used without problems.
global.path = {root: path.resolve(__dirname)};
// Set environment and initialize environment-specific config variables
config.env = require(path.join(__dirname, 'config', 'env.config'));
// Set up database connection to use throughout the application
config.db = require(path.join(__dirname, 'config', 'db.config'));
// HTTP for development environment, HTTPS for production environment
var http = require('http');
var https = require('https');
// Set up debugging/logging for the development environment
var debug = require('debug')('http');
// Start the app using the Express settings/routines in express.config.
var app = require(path.join(__dirname, 'config', 'express.config'));
// Start GraphQL process
// require(path.join(__dirname, 'config', 'graphql.config'))(app);
var router = require(path.join(__dirname, 'config', 'routes.config'));
router(app);
// Running in production mode: HTTPS only
if(config.env.name === 'production') {
var credentials = {
privateKey: fs.readFileSync('privkey'),
certificate: fs.readFileSync('fullchain')
};
var server = https.createServer(credentials, app);
server.listen(4443);
server.on('error', onError);
server.on('listening', onListen);
var server2 = http.createServer(app);
server2.listen(8080);
// Running in development mode: HTTP only
} else {
var server = http.createServer(app);
server.listen(config.env.port);
server.on('error', onError);
server.on('listening', onListen);
}
//some listeners here
Our express.config.js
var path = require('path');
console.log('Initializing API...');
var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var compression = require('compression');
var morgan = require('morgan');
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
var config = require(path.join(__dirname, 'db.config'));
// The GraphQL server implementation for Express by the Apollo team.
var graphqlExpress = require('graphql-server-express').graphqlExpress;
var graphiqlExpress = require('graphql-server-express').graphiqlExpress;
var OpticsAgent = require("optics-agent");
var passport = require('passport');
var app = express();
// Handle application/json requests
app.use(bodyParser.json({ limit: '50mb' }));
// Handle application/x-www-form-urlencoded requests (usually POST, PUT, etc.)
app.use(bodyParser.urlencoded({ extended: false, limit: '50mb' }));
app.use(cookieParser());
app.use(session({
store: new MongoStore({
url: 'mongodb://' + config.url + ':' + config.port + '/' + config.name
}),
secret: 'secret',
key: 'skey.sid',
resave: false,
saveUninitialized: false,
cookie : {
maxAge: 604800000 // 7 days in miliseconds
}
}));
app.use(passport.initialize());
app.use(passport.session());
require(path.join(__dirname, 'auth.config'))(passport); //Load passport config
app.use(function(req, res, next) {
req.resources = req.resources || {};
// res.locals.app = config.app;
res.locals.currentUser = req.user;
res.locals._t = function (value) { return value; };
res.locals._s = function (obj) { return JSON.stringify(obj); };
next();
})
// Use gzip compression (following the Express.js best practices for performance)
app.use(compression());
// Serve frontend and static files like stylesheets and images from the Express server
app.use(express.static(path.join(__dirname, '..', '..', 'app')));
app.use(express.static(path.join(__dirname, '..', '..', 'public')));
// Morgan logger (previously named express-logger)
app.use(morgan("dev"));
// Generate the GraphQL schema
var schema = require(path.join(__dirname, 'graphql.config'))().then(function(schema) {
/* Use Apollo Optics middleware for query optimization/tracing. */
OpticsAgent.instrumentSchema(schema);
app.use('/apiv2', OpticsAgent.middleware());
console.log('GraphQL schema generated.');
/* Return params object for Apollo GraphQL Server using a request handler function. */
app.use('/apiv2', graphqlExpress(function(req) {
return {
schema: schema,
debug: true,
context: {
opticsContext: OpticsAgent.context(req)
}
};
}));
app.use('/graphiql', graphiqlExpress({endpointURL: '/apiv2'}));
console.log('GraphQL started.');
/* Handle all other HTTP requests AFTER graphql server API endpoint and other routes are defined. */
app.use('*', express.static('app'));
});
// Always keep the errorHandler at the bottom of the middleware function stack!
// Returns a user-friendly error message when the server fails to fulfill the request
app.use(function(err, req, res, next) {
var status = 500, response = {message: 'An internal server error has occurred while trying to load this page. '};
console.error(err.stack);
res.status(status).json(response);
next(err);
});
module.exports = app;
Not really an solution to this problem, but we changed forever to PM2 (https://github.com/Unitech/pm2) and that program is doing his job fine!

CANNOT GET / in Node app

I've checked the other related questions and can't pinpoint what's causing the issue (don't have a lot of experience here). I'm trying to launch this app locally basically with its default settings (other than some Twilio keys), have the dependencies installed, mongo is running, but localhost:5000 returns CANNOT GET /.
> node server.js
listening on port 5000
server.js:
// modules =================================================
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var http = require('http').Server(app);
var io = require('socket.io')(http); //real-time chat
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var cookieParser = require('cookie-parser');
// configuration ===========================================
// public folder for images, css,...
app.use(express.static(__dirname + '/public'))
// config files
// database
var db = require('./config/db');
// models
var User = require('./app/models/users');
var Message = require('./app/models/messages');
//parsing
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); //for parsing url encoded
//AUTH========================================================
app.use(cookieParser());
app.use(require('express-session')({
secret: 'white rabbit',
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
// passport config
var Member = require('./app/models/members');
passport.use(new LocalStrategy(Member.authenticate()));
passport.serializeUser(Member.serializeUser());
passport.deserializeUser(Member.deserializeUser());
// view engine ejs
app.set('view engine', 'ejs');
//chat namespace
var chatSocket = io.of('/chat')
//routes
require('./app/routes/routes')(app, chatSocket);
//Heroku port
app.set('port', (process.env.PORT || 5000));
//ADMIN====================================================
//create an admin account if none exists
var admin = Member.find({admin: true}, function(err, admins) {
if (err) throw err
else if(admins.length == 0){
//no admin. create default account
Member.register(new Member({username: "admin", admin: true}), "mypassword", function(err, admin){
if(err) throw err;
console.log('Defaut account created successfully!');
})
}
else{
//at least one admin exists
console.log('Admin account already exists : ');
console.log(admins)
}
});
//START ===================================================
http.listen(app.get('port'), function(){
console.log('listening on port ' + app.get('port'));
});
//SOCKET ==================================================
require('./app/controllers/socket')(chatSocket, User, Message);
You didn't set any route that points to /
Point to that route by using app.get
app.get("/", function(req, res) {
res.send("home");
});
There are available routes in /app/routes/*.js files of the repository you cloned.

I cannot send chat messages with socket.io

I could send messages before, but I shared passport's sessions with express and socket.io using redis by following this tutorial http://www.scotthasbrouck.com/blog/2016/3/18/passportjs-express-session-with-sockeio and now I can no longer send any chat messages.
Also, in my routes req.isAuthenticated() always returns false but I didn't have this problem before.
This is my server.js.
// server.js
// set up ======================================================================
// get all the tools we need
var express = require('express');
var app = express();
var server = require('http').Server(app);
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var configDB = require('./config/database.js');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var passportSocketIo = require('passport.socketio');
var session = require('express-session');
var chatdb = require('./app/models/chat.js');
var user = require('./app/models/user.js');
var redis = require('redis');
var client = redis.createClient();
var RedisStore = require('connect-redis')(session);
// configuration ===============================================================
mongoose.connect(configDB.url); // connect to our database
require('./config/passport')(passport); // pass passport for configuration
// set up our express application
app.use(morgan('dev')); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser()); // get information from html forms
app.set('view engine', 'ejs'); // set up ejs for templating
var sessionStore = new RedisStore({ host: 'localhost', port: 6379, client: client, ttl: 260 });
// required for passport
app.use(session({
secret: 'secret',
store: sessionStore,
resave: true,
saveUninitialized: true,
cookie: {
secure: process.env.ENVIRONMENT !== 'development' && process.env.ENVIRONMENT !== 'test',
maxAge: 2419200000
},
}));
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(express.static(__dirname + '/views'));
app.use(express.static(__dirname + '/public'));
io.use(passportSocketIo.authorize({
key: 'connect.sid',
secret: 'secret',
store: sessionStore,
passport: passport,
cookieParser: cookieParser,
}));
var eventSocket = io.of('/chat');
// socket.io
io.on('connection', function (socket) {
console.log('a user connected');
socket.on('chat message', function(msg){
var name = "efe";
chatdb.saveMsg({name: name, msg: msg}, function(err){
if(err) throw err;
io.emit('chat message', msg);
});
if (socket.request.user && socket.request.user.logged_in) {
console.log(socket.request.user);
}
});
});
// routes ======================================================================
require('./app/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport
// launch ======================================================================
http.listen(port);
console.log('The magic happens on port ' + port);
I had the same problem. I tried to implement passport.socketio like you but I solved my problem without it.
Here is my middleware :
// Express session middleware
var sessionMiddleware = session({ secret: 'secret-key', resave: true, saveUninitialized: true });
// Send Express sessionMiddleware through socket.io
io.use(function(socket, next){
// Params: request object, response object and callback.
sessionMiddleware(socket.request, {}, next);
});
The second parameter (response object) of sessionMiddleware function is empty. This will renders the session read-only.
And Here is how I use passport js session variable to get user informations :
io.on("connection", function(socket){
socket.on('chat message', function(msg){
var passport = socket.request.session.passport; // Get the passport variable
// Check if passport session exists
if (passport && typeof passport !== 'undefined'){
var userSession = passport.user; // Get the user session
// Build JSON with user session data
var message = {
message : msg,
name : userSession.name,
avatar : userSession.avatar
};
io.emit('chat message', message); // Emit my socket
}
});
});
Hope it help.
This was how i was able to solve it.
var express = require('express');
var app = express();
var server = require('http').Server(app);
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
// Add connect-mongo to project - npm install connect-mongo
var MongoStore = require('connect-mongo')(session);
var configDB = require('./config/database.js');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var chatdb = require('./app/models/chat.js');
// configuration ===============================================================
// NOTE: This might need to be put into a callback/promise inside an initialize function
var db = mongoose.connect(configDB.url); // connect to our database
require('./config/passport')(passport); // pass passport for configuration
// set up our express application
app.use(morgan('dev')); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser()); // get information from html forms
app.set('view engine', 'ejs'); // set up ejs for templating
var mongoStore = new MongoStore({
mongooseConnection: db.connection,
});
app.use(session({
secret: 'secret',
clear_interval: 900,
cookie: { maxAge: 2 * 60 * 60 * 1000 },
store: mongoStore,
}));
// required for passport
app.use(session({ secret: 'secret' })); // session secret
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(express.static(__dirname + '/views'));
app.use(express.static(__dirname + '/public'));
// Intercept Socket.io's handshake request
io.use(function(socket, next) {
// Use the 'cookie-parser' module to parse the request cookies
cookieParser('secret')(socket.request, {}, function(err) {
// Get the session id from the request cookies
var sessionId = socket.request.signedCookies ? socket.request.signedCookies['connect.sid'] : undefined;
if (!sessionId) return next(new Error('sessionId was not found in socket.request'), false);
// Use the mongoStorage instance to get the Express session information
mongoStore.get(sessionId, function(err, session) {
if (err) return next(err, false);
if (!session) return next(new Error('session was not found for ' + sessionId), false);
// Set the Socket.io session information
socket.request.session = session;
// Use Passport to populate the user details
passport.initialize()(socket.request, {}, function() {
passport.session()(socket.request, {}, function() {
// This will prohibit non-authenticated users from connecting to your
// SocketIO server.
if (socket.request.user) {
next(null, true);
} else {
next(new Error('User is not authenticated'), false);
}
});
});
});
});
});
// socket.io
io.on('connection', function(socket) {
socket.on('chat message', function(msg) {
console.log(socket.request.user.facebook.name || socket.request.user.local.email);
var name = socket.request.user.facebook.name || socket.request.user.local.email;
chatdb.saveMsg({
name: name,
msg: msg
}, function(err) {
if (err) throw err;
io.emit('chat message', msg);
});
});
socket.on('disconnect', function() {
console.log('user disconnected');
});
});
// routes ======================================================================
require('./app/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport
// launch ======================================================================
http.listen(port);
console.log('The magic happens on port ' + port);

Supertest returns no result and node instances don't exit

I'm just learning testing and I've been at it for a few hours no with no success. Every time I run Mocha, my test shows up in the terminal and returns no result, like this:
Express test app started on port: 3001
addition
It stays blank afterwards. Further, the node processes don't close after testing and when they stack up they crash my computer.
Here is my sample test:
var supertest = require('supertest');
var should = require('should');
process.env.NODE_ENV = 'test';
var app = require('../server');
describe('addition', function() {
//... previous test
it('should return 2 given the url /add/1/1', function(done) {
request(app)
.get('/add/1/1')
.expect(200)
.end(function(err, res) {
should.not.exist(err);
parseFloat(res.text).should.equal(2);
done();
});
});
});
Here is my server.js
// Module Depndencies
var express = require('express'),
fs = require('fs'),
mongoose = require('mongoose'),
passport = require('passport'),
MongoStore = require('connect-mongo')(express),
flash = require('connect-flash'),
env = process.env.NODE_ENV = process.env.NODE_ENV || 'development',
ErrorHandler = require('./app/controllers/api/v1/error_handler_v1'),
config = require('./config/config'),
auth = require('./config/middlewares/authorization');
// Connect The Database
mongoose.connect(config.db);
// Catch Database Connection Error
mongoose.connection.on("error", function(err) {
return console.log("****** Could not connect to mongo server!");
});
// Express Settings
app = express();
app.set('showStackError', true);
//Should be placed before express.static
app.use(express.compress({
filter: function(req, res) {
return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
},
level: 9
}));
//Setting the fav icon and static folder - tejkh
app.use(express.favicon());
app.use(express.static(config.root + '/public'));
//Don't use logger for test env
if (process.env.NODE_ENV !== 'test') app.use(express.logger('dev'));
//Set views path, template engine and default layout
app.set('views', config.root + '/app/views');
app.set('view engine', 'jade');
// Enable jsonp
app.enable('jsonp callback');
//Bootstrap models
var models_path = __dirname + '/app/models';
var walk = function(path) {
fs.readdirSync(path).forEach(function(file) {
var newPath = path + '/' + file;
var stat = fs.statSync(newPath);
if (stat.isFile()) {
if (/(.*)\.(js|coffee)/.test(file)) {
require(newPath);
}
} else if (stat.isDirectory()) {
walk(newPath);
}
});
};
walk(models_path);
// Bootstrap Passport
require('./config/passport')(passport);
// App.configure
app.configure(function() {
// CookieParser should be above session
app.use(express.cookieParser());
// BodyParser should be above methodOverride
app.use(express.bodyParser());
app.use(express.methodOverride());
// Express/mongo session storage
app.use(express.session({
secret: 'secret',
cookie: {
maxAge: 864000000 // 10 Days in miliseconds
},
store: new MongoStore({
url: config.db,
auto_reconnect: true
})
}));
// Connect flash for flash messages
app.use(flash());
// Use Passport
app.use(passport.initialize());
app.use(passport.session());
// Route
app.use(app.router);
// Error Handler
app.use(function(err, req, res, next) {
if (err.uri) delete err.uri;
if (err.status) {
return res.status(err.status).jsonp(err);
} else {
res.status(500).jsonp({
name: 'InternalError',
message: 'Sorry, something went wrong on our end. We have been notified.',
code: 'server_error',
status: 500
});
}
});
}); // app.configure
//Bootstrap routes
require('./config/routes')(app, passport, auth);
// Start App
app.listen(config.port);
console.log('Express ' + env + ' app started on port: ' + config.port);
// Expose app
exports = module.exports = app;
If you use:
var supertest = require('supertest');
then you should use:
supertest(app)
and not:
request(app)
try this, maybe it will work
request('http://host:port')

Resources