Node Express Session not being saved - node.js

Im trying to get sessions working with node. Im using Express 4.8.7. Im following th tutorial here.
I have built an API in node running on port 8080. I have an Angular app running on port 8081.
At the top of my server.js file i have:
var session = require('express-session');
var bodyParser = require('body-parser');
app.use(session({
secret: 'ssshhhhh'
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
var sess;
When I login, I do:
app.post('/login', function(req, res) {
var successCallback = function(res, status, content) {
sess = req.session;
sess.email = req.body.email;
console.log('in LOGIN and req.session is ', req.session);
sendJsonResponse(res, status, content);
}
ctrlUsers.signin(req, res, successCallback);
});
And after login, the frontend does a request to a /files endpoint:
app.get('/files', function(req, res){
console.log('in files route and req.session is ', req.session);
});
In the example I have linked to above, the req.session should contain a property with the email address. But it doesnt, it just contains:
{ cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true } }
Any ideas why the session isn't getting saved correctly?

change
app.get('/files', function(req, res){
console.log('in files route and req.session is ', req.session);
});
to ==>
var usersession = req.session;
app.get('/files', function(req, res){
console.log('in files route and req.session is ', usersession);
});

Related

nodejs: "req.session.save is not a function" error during authentication

I am using PassportJS to authenticate users in my application. After a user logs in, the session is created, but soon after being redirected, the session appears to become undefined once again because it hasn't been saved. I found online that often times with redirects, the redirect completes before the session is saved, and so it's as if authentication never happened. The apparent solution is to use the req.session.save function so that redirects will only happen after the session is saved. However, I am getting an error log of "TypeError: req.session.save is not a function." Can somebody please help?
Here is my code for app.js.
var express = require('express'),
passport = require('passport'),
session = require('express-session'),
bodyParser = require('body-parser'),
RedisStore = require('connect-redis')(session),
redis = require('redis'),
logger = require('morgan'),
errorHandler = require('express-error-handler'),
site = require('./site'),
oauth2 = require('./oauth2'),
port = process.env.PORT || 8080;
var app = express();
var redisClient = redis.createClient(8080, 'localhost');
// use sessions for tracking logins
app.use(session({
secret: 'keyboard cat',
resave: true,
saveUninitialized: true,
store: new RedisStore({
client: redisClient,
host: "pub-redis-14280.us-central1-1-1.gce.garantiadata.com",
port: 12543,
ttl: 260
})
}));
app.use(logger('dev'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json({ type: 'application/json' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(errorHandler({ dumpExceptions: true, showStack: true }));
// use ejs as file extension for views
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/views'));
// use passport
require('./auth');
// Account linking
app.get('/', site.index);
app.get('/login', site.loginForm);
app.post('/login', site.login);
app.get('/logout', site.logout);
app.get('/authorize', oauth2.authorization);
app.post('/authorize/decision', oauth2.decision);
// set up local server
if (module === require.main) {
// [START server]
// Start the server
var server = app.listen(process.env.PORT || 8080, function () {
var port = server.address().port;
console.log('App listening on port %s', port);
});
// [END server]
}
module.exports = app;
site.js:
var passport = require('passport');
var login = require('connect-ensure-login');
// get layout
exports.index = function (req, res) {
console.log("layout loaded");
res.render('layout');
}
// get login form
exports.loginForm = function (req, res) {
console.log("login page loaded");
res.render('login');
}
// post login form
exports.login = [
passport.authenticate('local'),
function (req, res) {
req.session.save(function (err) {
res.redirect('/');
});
}
]
// logout
exports.logout = function (req, res) {
req.logout();
res.redirect('/');
}
Passport serialize/deserialize user:
passport.serializeUser(function(id, done) {
console.log("serializing user");
done(null, id);
});
passport.deserializeUser(function(id, done) {
console.log("deserializing user");
done(null, id);
});
In my passport authentication, I return the user id for simplicity, since that's all I need to represent users in my system.
In case anybody else is still having this issue (like me), try following Nathan's comment above and debug your connection to your redis/mongo/etc store.
What worked for me was I had my redis host set to http://localhost so I swapped it to 127.0.0.1 (local development of course) and everything immediately worked.

Exporting middleware globally to all routes

So I have been working with PassportJS in adding authentication to my site. I am interested in selectively displaying content that reflects whether or not a user is logged in or if he/she is an owner of a document.
I have the following files
app.js (entry point)
//Express set up
let express = require("express");
app = express();
serverPort = 8000;
io = require('socket.io')();
//BodyParser set up
bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: true }));
//Mongoose set up
mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/Woof");
//Models settings
Dog = require("./models/dog");
User = require("./models/user");
//Seed file set up and run!
seed = require("./models/seed");
seed();
//Routes
app.use('/public', express.static(__dirname + '/public'));
app.use(require('./routes/dogOwners'));
app.use(require('./routes/landing'));
app.use(require('./routes/loginAndRegister'));
app.use(require('./routes/map'));
app.use(require('./routes/dog'));
//req.user avalaible to all routes
app.use(function(req,res,next){
res.locals.currentUser = req.user;
next();
});
//App settings
app.set('port', serverPort);
app.set('view engine', 'ejs');
app.set('views', 'views');
app.set('sockets', []);
//Servloger listen:
let server = app.listen(app.get('port'), function() {
console.log('Listening on port ' + app.get('port'));
});
loginAndRegistration.js These are the authentication routes
let express = require('express');
router = express.Router({ mergeParams: true });
User = require("../models/user")
//AUTHENTICATION
let passport = require("passport");
LocalStratergy = require("passport-local");
//PASSPORT CONFIGURATION
app.use(require("express-session")({
secret: "I wanna go poopie",
resave: false,
saveUninitialized: false,
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(function(req,res,next){
res.locals.currentUser = req.user;
next();
});
passport.use(new LocalStratergy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
router.get('/login', function(req,res){
res.render('login');
});
router.post('/login', passport.authenticate('local', {
successRedirect: "/main",
failureRedirect: "/login",
}), function(req, res) {
});
router.get('/logout', function(req,res){
req.logout();
res.redirect("/");
});
router.get('/register', function(req, res) {
res.render('register', {});
});
router.post("/register", function(req, res) {
User.register(new User({ username: req.body.username }), req.body.password, function(error, newlyCreatedUser) {
if (error) {
console.log("COULD NOT REGISTER USER IN THE POST ROUTE");
res.render("register");
console.log(error);
} else {
passport.authenticate("local")(req, res, function() {
let user = req.body.user;
newlyCreatedUser.name = user.name;
newlyCreatedUser.nickname = user.nickname;
newlyCreatedUser.address = user.address;
newlyCreatedUser.email = user.email;
newlyCreatedUser.numberOfDogs = user.numberOfDogs;
newlyCreatedUser.url = "/user/" + newlyCreatedUser.id;
newlyCreatedUser.save(function(error, savedUser) {
console.log("USER REGISTERED");
res.render('maps', {
gmapsCredential: credentials.gmaps,
'authorized': true
});
});
});
}
});
});
function isLoggedIn (req,res,next){
if(req.isAuthenticated()){
return next();
}
res.redirect("/login");
}
module.exports = router;
I wanna be able to call isLoggedIn in all my routes, but I can only use it in the auth routes. How do I selectively export that function alone to all my routes? I also wanna be able to refer to the varaible currentUser in all my .ejs files, however it only works in .ejs files associated with the auth routes. How do I check for currentUser in all my .ejs files?
The crux of your problems are you’ve got too much buried in your loginAndRegistration.js file.
First and foremost, if you want to run isLoggedIn for every route you need to make sure Passport is configured first i.e.
app.use(passport.initialize());
app.use(passport.session());
app.use(function(req,res,next){
res.locals.currentUser = req.user;
next();
});
passport.use(new LocalStratergy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
Needs to be setup before you configure any auth middleware. This will also solve your .ejs problem as user will now be getting serialised for every route and not just the auth ones.
Finally, you can configure your middleware before any routes you want to enforce authentication on
app.use(isLoggedIn);

Passport authentication not work for one specific route with express router

I'm trying to access 'testpage' route. But the req.isAuthenticated() returns false only for this route. (This route was there before I started to add authentication).
I'm able to go to login page and authenticate with google. Then I can access 'signup' or 'user_profile' route without problems.
After login if I try:
localhost:8080/testpage
the server sends me to "/". But if I try:
localhost:8080/testpage#
with hash sign in the end, the page is rendered.
// routes/users.js
var express = require('express');
var router = express.Router();
module.exports = function (passport) {
router.get('/login', function (req, res) {
res.render('login', { message: req.flash('loginMessage') });
});
router.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }));
router.get('/auth/google/callback',
passport.authenticate('google', {
successRedirect: '/',
failureRedirect: '/'
}));
router.get('/user_profile', isLoggedIn, function (req, res) {
res.render('user_profile');
});
router.get('/signup', isLoggedIn, function (req, res) {
res.render('signup');
});
router.get('/testpage', isLoggedIn, function (req, res) {
res.render('testpage');
});
return router;
};
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/');
}
Any ideas why this is happening?
* update *
Here my app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var passport = require('passport');
var flash = require('connect-flash');
var session = require('express-session');
var db = require('./mongoose');
var app = express();
require('./config/passport')(passport);
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: 'secret123',
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
var users = require('./routes/users')(passport);
app.use('/', users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
It could be due to the express-session middleware that is needed for passport. you can fix it by using middleware in following order.
var session = require('express-session')
var app = express()
app.set('trust proxy', 1) // trust first proxy
app.use(session({
secret: 'yoursecret',
resave: true,
saveUninitialized: true,
cookie: { secure: true },
// you can store your sessions in mongo or in mysql or redis where ever you want.
store: new MongoStore({
url: "mongourl",
collection: 'sessions' // collection in mongo where sessions are to be saved
})
}))
// Init passport
app.use(passport.initialize());
// persistent login sessions
app.use(passport.session());
See https://github.com/expressjs/session for more details.
Also I think so you have not config google strategy.
try some thing like following
var GoogleStrategy = require('passport-google-oauth').OAuthStrategy;
// Use the GoogleStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a token, tokenSecret, and Google profile), and
// invoke a callback with a user object.
passport.use(new GoogleStrategy({
consumerKey: GOOGLE_CONSUMER_KEY,
consumerSecret: GOOGLE_CONSUMER_SECRET,
callbackURL: "http://www.example.com/auth/google/callback"
},
function(token, tokenSecret, profile, done) {
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return done(err, user);
});
}));
Finally after one entire day I just realized that when I was typing localhost:8000/testpage in the url bar it was been changed to www.localhost:8000/testpage. And the auth dos not work with www*. Another thing is that google chrome tries to predict what url you will type and this could cause this type of error, and it is annoying at debugging. So I unchecked this options at chrome's settings, preventing prediction.

Trouble with Express 4 and CSRF Token posting

I think I'm misunderstanding how the token is supposed to post. I'm just getting a 403 every time, even though it's actually attempting to pass the token.
Here's the server code
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var redis = require('redis');
var session = require('express-session');
var RedisStore = require('connect-redis')(session);
var ejs = require('ejs');
var csrf = require('csurf');
var util = require('./public/javascripts/utilities');
var routes = require('./routes/index');
var users = require('./routes/users');
var login = require('./routes/login');
var loginProcess = require('./public/javascripts/login.js').loginProcess;
// var loginProcess = require('./public/javascripts/login.js')
var client = redis.createClient();
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, '/public')));
app.use(cookieParser('secret'));
app.use(session(
{
store: new RedisStore({ host: 'localhost', port: 6379, client: client }),
secret: 'secret',
saveUninitialized: true,
resave: false
}
));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(csrf());
app.use(util.csrf);
app.use(util.authenticated);
app.use('/', routes);
app.use('/users', users);
app.use('/login',
login,
loginProcess);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
The login route is
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.render('login', {title: 'Login'});
next();
});
Here is what I've got in var util
module.exports.csrf = function csrf(req, res, next){
res.locals.csrftoken = req.csrfToken();
next();
};
I'm also using ejs, and have this after my form method='post'
<input type="hidden" name="_csrf" value="<%= csrfToken %>>"
Whenever it returns 403, the form data is at least getting the name of the input
_csrf:
username:Test
password:>9000
But as you can see, it's blank
I also wasn't sure if the res.locals.csrftoken was being passed to the login route, so I also tried adding it directly there with a router.post, but got this error
Error: Can't set headers after they are sent.
I've gone through nearly every post concerning this I could find. I'm either not making the logical connection for what I'm missing, or am wholly misunderstanding something. Both are entirely plausible, my money is on the second one. Feel free to make any, why in the world are you doing that - that way - comments, because chances are I'm doing it out of ignorance, and those comments are good for the learning process. Thanks in advance.
edit: Removing my utility function and following correct 'csurf' docs successfully passed the csrf token to my /login view.
I'm getting closer, still wrong, but this may shed some light as to where I'm getting confused.
var express = require('express');
var router = express.Router();
/* GET login listing. */
router.get('/', function(req, res, next) {
res.render('login', {title: 'Login', csrfToken: req.csrfToken() });
});
function loginProcess(req, res, next){
console.log(req.body);
res.send(req.body.username + ' ' + req.body.password);
res.json(req.csrfToken());
next();
};
router.post('/', loginProcess);
module.exports = router;
Why would this redirect me to a 404 page?
Because I didn't remove my authentication step before testing.
Also, I know this is sending un & pw in plain text along with the csrf token and that's no bueno. I'll get to that eventually.
Something I did is attempting to set headers when submitting username and password.
Error: Can't set headers after they are sent.
I thought it was my loginProcess function, but removing next(), or adding res.end(); didn't help
function loginProcess(req, res, next){
console.log(req.body);
res.send(req.body.username + ' ' + req.body.password);
res.json(req.csrfToken());
res.end();
};
edit You can't use res.send and res.json like that because they're both technically sending, and you can't send headers+body and then send headers+body again.
The token is automatically sent so I removed res.json(req.csrfToken();
But somewhere I'm not redirecting correctly on post. I'm just getting a blank page with the username and passwords that were entered.
edit:
Hokay. So everything appears to be working properly. Here is the updated code.
login.js
var express = require('express');
var router = express.Router();
/* GET login listing. */
router.get('/', function(req, res, next) {
res.render('login', {title: 'Login', csrfToken: req.csrfToken() });
});
function loginProcess(req, res, next){
var isAuth = auth(req.body.username, req.body.password, req.session)
if (isAuth){
res.redirect('/chat');
}else{
res.redirect('/login');
}
};
router.post('/', loginProcess);
router.get('/logout', out);
module.exports = router;
app.js
var routes = require('./routes/index');
var users = require('./routes/users');
var login = require('./routes/login');
var chat = require('./routes/chat');
//var loginProcess = require('./public/javascripts/login.js').loginProcess;
var client = redis.createClient();
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, '/public')));
app.use(cookieParser('secret'));
app.use(session(
{
secret: 'secret',
store: new RedisStore({ host: 'localhost', port: 6379, client: client }),
saveUninitialized: true,
resave: false
}
));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(csrf({ cookie: true }));
// app.use(util.csrf);
app.use(util.authenticated);
app.use('/', routes);
app.use('/users', users);
app.use('/login', login);
app.use('/chat', [util.requireAuthentication], chat);
I've still got a ton of cleanup, but it's at least functional.
Much thanks to #Swaraj Giri
What is app.use(util.csrf);? Guess you need to remove it.
From the docs of csurf,
You need to set csrf({ cookie: true }). This sets the crsf value in req.body._csrf.
Then you need to pass { csrfToken: req.csrfToken() } to the view of login page.
In login.js
router.get('/', function(req, res, next) {
res.render('login', {title: 'Login', csrfToken: req.csrfToken()});
next();
});

passport and JWT

So i managed to get passport-twitter working together with jsonwebtoken library, but in order for it to work properly I have to use express-session as the middleware. I don't want to add session because I'm using jsonwebtoken to return the token.
Here's the code
autheticate.js
router.get('/twitter', function(req, res, next){
passport.authenticate('twitter', {session: false}, function(err, user, info){
if(err){ return next(err); }
if(user){
var token = createToken(user);
console.log(token);
return res.json({token: token});
} else {
return res.status(401).json(info);
}
})(req, res, next);
});
I already added session: false as the argument, but on server.js it keeps spitting error, that i need to use express-session.
server.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var passport = require('passport');
var session = require('express-session');
var config = require('./config');
mongoose.connect('mongodb://localhost', function() {
console.log("Connected to the database");
})
require('./passport')(passport);
var app = express();
var authenticate = require('./routes/authenticate')(app, express, passport);
var api = require('./routes/api') (app, express, passport);
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
secret: config.TOKEN_SECRET,
resave: true,
saveUninitialized: true,
}));
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use('/auth', authenticate);
app.use('/api', api);
app.get('*', function(req, res) {
res.sendFile(__dirname + '/public/app/views/index.html');
});
app.listen(3000, function(err) {
if(err) {
return res.send(err);
}
console.log("Listening on port 3000");
});
So whenever i delete app.use(session()) and try to authenticate with passport-twitter. I will get this error
error Oauth Strategy requires app.use(express-session));
I know that the obvious solution is to add that line, but I dont want to use session. Does Oauth 0.1 really need to use session?
Passports OAuth based strategies use the session middleware to keep track of the login process. You do not need to use the session middleware for anything else, just base your authentication on your token and ignore the session.

Resources