Reputation: 14299
I've gotten pretty far with express and express-session and express-sql-session. I've got it creating a row in the database for a session when a user logs in. This is how I set it:
//login route handler
this.bcrypt.compare(password, row.hashed, function(err, passwordsMatch) {
if (passwordsMatch === true) {
console.log("user now logged in");
req.session.user = row;
req.session.success = 'User successfully logged in';
res.send(row);
res.end();
}
});
Hunky dory! I can hop into my session table and get the row from the database. Here it is:
{"cookie":{"originalMaxAge":600000,"expires":"2015-08-24T23:16:20.079Z","httpOnly":false,"path":"/"},
"user":{"userID":24,"userName":"g","email":"g","joinDate":"2015-08-24T07:15:33.000Z"},"success":"User successfully logged in"}
Notice that you can see the custom use object is set. However, on the next request to get some data, I check for the user
object on the session:
// some other route called after login.
if (!req.session.user) {
console.log('user not authorized' + JSON.stringify(req.session));
res.send('not authorized');
return;
}
but that logs an (apparently) empty session.
user not authorized{"cookie":{"originalMaxAge":600000,"expires":"2015-08-24T23:27:13.455Z","httpOnly":false,"path":"/"}}
Going into the browser, I also see no cookie is set in the resources panel. Shouldn't this be automatically generated with express 4 and session? The docs say you do not need expressCookie() with express 4 anymore. How do I get the correct session on subsequent requests?
Also, if I login again, it just creates a duplicate row in the sessions table. How do I properly set a cookie in the response to make this work for the next request?
Here's my session config if it helps:
// at the beginning of my node server
import express = require('express');
import bodyParser = require('body-parser');
import Q = require('q');
import mysql = require('mysql');
var app = express();
import bcrypt = require('bcrypt');
import userModule = require('./userModule')
var UserRepository = new userModule.UserNamespace.UserRepository(connectToMySQL, bcrypt, Q );
import session = require('express-session');
var SessionStore = require('express-sql-session')(session);
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Credentials', 'true');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
var storeOptions = {
client: 'mysql',
connection: {
host:SQLHOST,
port:SQLPORT,
user:SQLUSER,
password: SQLPASS,
database: SQLDB
},
table: SESSION_TABLE,
expires: 365 * 24 * 60 * 60 * 1000
};
var sessionStore = new SessionStore( storeOptions );
app.use(session({
secret: 'meeogog',
resave: false,
saveUninitialized: false,
cookie: { maxAge: 600000,httpOnly: false },
store: sessionStore
}));
...
app.post('/users/login/', function (req, res) {
UserRepository.loginHashed(req, res);
});
..and then more routes, and so forth
Upvotes: 14
Views: 15449
Reputation: 541
I have been feeling the same pain, the solution that worked for me was similar to FlavorScope.
I realized that the express-session library was creating a cookie on the server but it was not being saved in the local cookies. The response header would say set-cookie but nothing was saving. When I would hit another route, log back in, etc. it would not work and would not recognize the old session(because the client/browser wasn't sending one lol). That is why it was making new sessions on every route. This issue seemed to only be fixed by 1.) setting the cookie up a certain way on the server, and 2.) setting up any get/post requests a certain way on the client/browser.
I got mine to work without changing to 127.0.0.1. Localhost worked okay for me. I was also using the FETCH api which is specific for how you set up the headers. Also this was for my development environment. I had to change the secure flag to false. Localhost is http not https, so it breaks if the secure flag is true for the dev environment. Also one final thing, when you have credentials: "include" on the front-end you also need to have the cors set up with a specific domain(here I use my localhost), "*" wildcard domain doesn't work. In my words I would say the credentials: "include" just says to the browser "hey you are okay to save this cookie if we get one back from the server"
Server
app.use(cors({
origin: "http://localhost:3000",
credentials: true
}));
app.use(session({ name: "mySession",
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: false,
maxAge: 60000 * 60 * 24
},
store: new RedisStore({ client: redisClient })
}))
Client/Browser for login and also another example for a get request
fetch(
"http://localhost:3001/login",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: email,
password: password
}),
credentials : "include"
}
)
Get Request Example, still need the credentials: "include"
<pre><code>
fetch(
"http://localhost:3001/accounts",
{
method: "GET",
headers: {"Content-Type": "application/json"},
credentials: "include"
}
)
</pre></code>
Upvotes: 0
Reputation: 14299
Right after i bountied this i discovered that it was a combination of using localhost and not setting useCredentials on the xhr request. The localhost is what tripped me up you have to use the fully qualified 127.0.0.1 and to add to the headache, the http files are served on a different port, so had to change the wildcard to reflect that.
so...
//where the server runs on 127.0.0.1:3000 but the http runs from :9000
app.use(session({
name:'some_session',
secret: 'lalala',
resave: true,
saveUninitialized: false,
cookie: { maxAge: 365 * 24 * 60 * 60 * 1000,httpOnly: false , domain:'127.0.0.1:9000'},
store: sessionStore
}));
res.header("Access-Control-Allow-Origin", "http://127.0.0.1:9000");
//important
$http request (angular): useCredentials: true
Upvotes: 6
Reputation: 9753
I used to have this issues.
ps: All the following code is provided by:
just started to use passport: http://passportjs.org/
"passport": "~0.2.0",
"passport-facebook": "~1.0.2",
"passport-github": "~0.1.5",
"passport-google-oauth": "~0.1.5",
"passport-linkedin": "~0.1.3",
"passport-local": "~1.0.0",
"passport-twitter": "~1.0.2",
basically I just do: express.js
// CookieParser should be above session
app.use(cookieParser());
// Express MongoDB session storage
app.use(session({
saveUninitialized: true,
resave: true,
secret: config.sessionSecret,
store: new mongoStore({
db: db.connection.db,
collection: config.sessionCollection
})
}));
app.use( function (req, res, next) {
if ( req.method === 'POST' && (req.url === '/auth/signin'||req.url === '/auth/login') ) {
if ( req.body.rememberme ) {
req.session.cookie.maxAge = 2592000000; // 30*24*60*60*1000 Rememeber 'me' for 30 days
} else {
req.session.cookie.expires = false;
}
}
next();
});
// use passport session
app.use(passport.initialize());
app.use(passport.session());
create a passport.js
'use strict';
/**
* Module dependencies.
*/
var passport = require('passport'),
User = require('mongoose').model('User'),
path = require('path'),
config = require('./config');
/**
* Module init function.
*/
module.exports = function() {
// Serialize sessions
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// Deserialize sessions
passport.deserializeUser(function(id, done) {
User.findOne({
_id: id
}, '-salt -password', function(err, user) {
done(err, user);
});
});
// Initialize strategies
config.getGlobbedFiles('./config/strategies/**/*.js').forEach(function(strategy) {
require(path.resolve(strategy))();
});
};
inside of strategies folder I did those files:
locals.js
`
'use strict';
/**
* Module dependencies.
*/
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
User = require('mongoose').model('User');
module.exports = function() {
// Use local strategy
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(email, password, done) {
User.findOne({
email: email
}).select('-__v -notes -tags').exec(function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
message: 'Unknown user or invalid password'
});
}
if (!user.authenticate(password)) {
return done(null, false, {
message: 'Unknown user or invalid password'
});
}
user.password = undefined;
user.salt = undefined;
user.notes = undefined;
user.tags = [];
return done(null, user);
});
}
));
};
and finally to login I just do this:
user.auth.js
/**
* Signin after passport authentication
*/
exports.signin = function(req, res, next) {
console.log(req.body);
passport.authenticate('local', function(err, user, info) {
if (err || !user) {
res.status(400).send(info);
} else {
// Remove sensitive data before login
user.password = undefined;
user.salt = undefined;
user.notes = undefined;
user.tags = [];
user.resetPasswordToken = undefined;
req.login(user, function(err) {
if (err) {
res.status(400).send(err);
} else {
res.json(user);
}
});
}
})(req, res, next);
};
Upvotes: 2