In node.js my auth library (lockit) is not setting session after logging in successfully. (Disclaimer: I am not condoning the use of lockit, I think it is positively terrible in every way.)
I read lockit source code and found this code:
// create session and save the name and email address
req.session.name = user.name;
req.session.email = user.email;
Here is my express config:
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(cookieSession({
secret: 'this is my super secret string'
}));
app.use(lockit.router);
After I login with a user, I send another request with the same browser and the cookie is the same that gets set from the login request...
After successful login here is the Set-Cookies I get:
Set-Cookie express:sess=eyJmYWlsZWRMb2dpbkF0dGVtcHRzIjowLCJuYW1lIjoianVzdGluIiwiZW1haWwiOiJqdXN0aW5Ad2ViaW52ZXJ0ZXJzLmNvbSIsImxvZ2dlZEluIjp0cnVlfQ==; path=/; httponly
Set-Cookie express:sess.sig=FMcv9fswWmWG6A7hpOEnEysbqd4; path=/; httponly
Then my request after the login contains these cookies:
express:sess eyJmYWlsZWRMb2dpbkF0dGVtcHRzIjowLCJuYW1lIjoianVzdGluIiwiZW1haWwiOiJqdXN0aW5Ad2ViaW52ZXJ0ZXJzLmNvbSIsImxvZ2dlZEluIjp0cnVlfQ==
express:sess.sig FMcv9fswWmWG6A7hpOEnEysbqd4
But even though the cookie is there, I'm getting:
req.session === undefined
I've never used these technologies before, so it could be something really stupid that I am doing...
I'm the author of Lockit and I'm sure we will find a solution to your problem. Did you go through all the required steps to install Lockit? Here is what I just did and it works fine.
Create a new Express app (Express 4.2.0 in my case).
express
Install Express dependencies.
npm install
Install Lockit and Sessions.
npm install lockit cookie-session --save
Install CouchDB adapter.
npm install lockit-couchdb-adapter --save
Create a config.js with the URL of your CouchDB
// settings for local CouchDB
exports.db = 'http://127.0.0.1:5984/';
Initiate Lockit with your config.
// in your header
var cookieSession = require('cookie-session');
var Lockit = require('lockit');
var config = require('./config.js');
var lockit = new Lockit(config);
// after all your other middleware
app.use(cookieSession({
secret: 'my super secret String'
}));
app.use(lockit.router);
Start your app.
DEBUG=tmp ./bin/www
You can now navigate to http://localhost:3000/signup and create a new user. If you haven't set up any email server you have to look in your database (in my case at http://127.0.0.1:5984/_utils/database.html?_users) for your signup token. Then go to http://localhost:3000/signup/:token to activate your new user.
Great, you can now use your username (or email) and password to log in. To access the currently logged in user in your own routes use req.session.
// default routes from Express
app.use('/', routes);
app.use('/users', users);
// your own custom route
app.get('/awesome', function(req, res) {
console.log(req.session.name);
console.log(req.session.email);
res.send('awesome');
});
I hope I could help you. If you've got any other problems just let me know.
http://localhost:3000/signup/:token doesn't work, must be
http://localhost:3000/signup/token (without the column)
Related
I have a basic node.js express app using express-sessions.
Please can someone help with why the sessions are not persisting and why a new session is created for every request.
The app itself is quite large so i have added a reduced case of the important settings below.
const express = require('express');
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
// initialise express
const app = express();
// initialise db session store
const store = new MongoDBStore({
uri: MONGODB_URI,
collection: 'sessions',
// can also set expire here for auto cleanup by mongodb
});
app.use(session({
secret: 'secret password',
resave: false,
saveUninitialized: false,
httpOnly: false,
secure: app.get('env') === 'production',
store,
}));
...routes
in a user login route the app sets the request user to the session and saves the session. it is expected that this req,session.user will persist between pages but it does not. on each page request i can see a new session (or sometimes multiple sessions, 1 for each file request) being created.
UPDATE
TL:DR;
- robots.txt causes issues if not dealt with, set DEBUG env to express-session to troubleshoot
After a lot of hair pulling I've found a solution and some useful troubleshooting tips.
when running your app, run it with debug set to express-session.
so for those of you that are quite new to this like myself, run your app with a command similar to this:
DEBUG=express-session node 'bin/www.js'
or
DEBUG=express-session node app.js
depending on how you have your app entry point setup.
Doing this will print session related log msgs so you can troubleshoot if the cookie is actually getting sent with each request or not. the error messages will look like something this:
express-session fetching 0wgmO1264PsVvqeLqaIIXd6T0ink0zts +34s
express-session session found +49ms
To troubleshoot the issue of multiple requests causing multiple sessions per page load, Add a middleware at the top of your app before any other middleware. this will allow us to see the request URL and troubleshoot which requests may be interfering with our sessions.
// see what requests are being sent and which ones contain cookies
app.use((req, res, next) => {
const { url } = req;
const isCookieSent = req.headers.cookie;
console.log({ url });
console.log({ isCookieSent });
next();
});
from doing this I found out that the culprit was robots.txt file, Apparently the only path that is ignored by default is favicon.ico.
Because this robots.txt path wasn't handled properly, nor was it sending a cookie, it was causing the duplicate requests and also causing the cookies not to persist.
to fix this you either need to handle or ignore this request prior to getting to the session middleware.
i did this using this middleware, once again fairly high up.
app.get('/robots.txt', (req, res) => {
res.type('text/plain');
res.send('User-agent: *\nDisallow: /');
});
I am new to node.js so if there is anyone with more knowledge feel free to chip in with extra info or cleaner ways of solving this problem. Hopefully this saves some of you a lot of hassle!
I'm trying to implement the login functionality with passport.js and express-session, it works well,
var passport = require('passport'),
session = require('express-session'),
MongoDBStore = require('connect-mongodb-session')(session);
var sessionStore = new MongoDBStore({
uri: config.db,
collection: 'sessions'
});
var sessionOptions = {
name: 'very-secure-cookie',
secret: config.secret,
resave: false,
saveUninitialized: true,
cookie: {
secure: true,
maxAge: null
},
store: sessionStore
};
app.use(session(sessionOptions));
app.use(passport.initialize());
app.use(passport.session());
Here, maxAge is null, meaning that the client gets logged out when they close the browser window. However, I want to store an additional cookie with the client's email address in order to pre-fill the <input type="text" name="email"> when they want to log back in if they come later.
I've tried app.use()'ing another session object just to store the email information in another cookie, but for some reason the cookie is set only once (the first one).
app.use(session(returningSessionOptions));
Couldn't find any sensible solution anywhere :(
Oh, and I have to mention that I thought about using cookie-parser middleware alongside with express-session, but the docs state that it may result in conflicts.
As you didn't include the complete code you used, I assume you set the cookie using res.cookie(name, value[, options]); or res.setHeader('set-cookie', serializedCookie);.
There seems to be an issue with the set-cookie header. When adding multiple cookies, they are appended to the header, separated by comma, which doesn't seem to work in most current browsers. The header is only interpreted until the first comma and therefore only the first cookie is saved. In some browsers everything between the first equals sign and the following semicolon is used as value for that first cookie.
Possible solution
I could reproduce the issue with an old version of Express (3.0.0) using the code below. Using Express 4.13.4 (and also 3.21.2) the same code worked fine. If you are working with an old version of Express, I recommend that you update to the latest one as this should fix the problem. If you are already using a current version, please try if the example works for you. If not, please provide the code and the headers sent by your express-app. In some cases with a more complex application the problem exists even when using versions that work with the example code (I tried Express 3.21.2).
If there is any reason for you not to update to the latest version or the update doesn't fix the issue for you, there is an alternative solution for the problem:
Code to reproduce the issue
To eliminate possible side-effects by other code in the application, here is the minimal example that I used to reproduce your issue:
var express = require('express');
var session = require('express-session');
var passport = require('passport');
var app = express();
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.get('/', function (req, res) {
res.cookie('cookie', 'value');
res.send('Hello World!');
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
When accessing the express-app (running Express 3.0.x) via the browser, the following response headers are sent:
set-cookie: cookie=value; Path=/
set-cookie: cookie=value; Path=/,connect.sid=s%3ARPkxiLfy0dbeBs5eILYBLK6Yh-sOjABS.ykNXCmaOsxtIAXV%2Ba9GCW6YnBi0cBhvZIGvwlj%2F%2Fsy0; Path=/; HttpOnly
Using Express >=3.21.2 everything works fine for this example:
set-cookie: cookie=value; Path=/
set-cookie: connect.sid=s%3AR6TVTnUe3eyIvlpT0Hl5ikwcH_XMHXId.ouysyG5tGGekaVDxZMXJP4A8SJfsckLE4GZ3%2B1Eyd1o; Path=/; HttpOnly
Alternative fix
Borrowing the code of function setcookie from express-session for setting your cookies could do the trick for you, as it writes an array containing all the cookies to the header while preserving the ones already added to the response. To do this, install the cookie module, add var cookie = require('cookie'); and replace res.cookie('cookie', 'value'); in the above code with the following lines:
var data = cookie.serialize('cookie', 'value');
var prev = res.getHeader('set-cookie') || [];
var header = Array.isArray(prev) ? prev.concat(data)
: Array.isArray(data) ? [prev].concat(data)
: [prev, data];
res.setHeader('set-cookie', header);
Now the following headers are sent to the browser and all cookies are saved (tested using Google Chrome 49 on MacOS):
set-cookie:cookie=value
set-cookie:cookie=value
set-cookie:connect.sid=s%3Aa1ZPDmERtKwUaPjY__SrPtIrpYC7swQl.0KOs83RSmUTG%2FgPoyOLo4u5UFTjC89yS0Ch0ZVXWVo8; Path=/; HttpOnly
Unfortunately the header is duplicated for the first cookie when using Express 3.0.0, which shouldn't be done according to RFC6265 Section 4.1.1:
Servers SHOULD NOT include more than one Set-Cookie header field in
the same response with the same cookie-name. (See Section 5.2 for
how user agents handle this case.)
Helpful resources
The following articles may also be helpful:
Someone facing a similar problem, but with ASP.NET/Web API instead of node.js/express. However, in my opinion this page provides helpful information on the topic: Set-Cookie Header With Multiple Cookies
Another possible solution for setting multiple cookies with node.js/express that might help: http://www.connecto.io/blog/nodejs-express-how-to-set-multiple-cookies-in-the-same-response-object/
I am using Nodejs and express to create a web app. But i am finding some difficulty in maintaining session. i can use req.session.userid = userid , but it is not so reliable. if the server goes down for some time and it has to reboot, the session will be lost.. Is there any way to store the session more effectively?
You can either use a database as stated above, or use the in memory store, like redis. Redis is the preferred way to go when handling user session, since its several factors faster then reading from disk.
Additionally, you may want to look into Json Web Token, so you don't have to store sessions at all, rather just keep a reference to the user token in your database (or redis). This will allow you to easily authenticate on mobile. It can also help prevent csrf attacks if you store the token on a users localstorage (rather then cookie)
You can read about them here: https://scotch.io/tutorials/the-ins-and-outs-of-token-based-authentication, https://scotch.io/tutorials/the-anatomy-of-a-json-web-token, https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens
I prefer using the npm module called "connect-mongodb-session". It uses mongodb to store all the sessions. Go to your project directory and install "connect-mongodb-session" using
sudo npm install connect-mongodb-session
And add this to your package.json as dependencies. and this is how you can use it..
Sample code...
var express = require('express');
var session = require('express-session');
var MongoDBStore = require('connect-mongodb-session')(session);
var app = express();
var store = new MongoDBStore({
uri: 'mongodb://localhost:27017/connect_mongodb_session_test',
collection: 'mySessions'
});
// Catch errors
store.on('error', function(error) {
assert.ifError(error);
assert.ok(false);
});
app.use(require('express-session')({
secret: 'This is a secret',
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week
},
store: store
}));
server = app.listen(3000);
And you are good to go.. use req.session when ever you want, and your sesion will be stored save in mongodb.
for example..
app.post("/login",function(req,res){
//validate login
req.session.userid = userid;
})
even if the server has to reboot, your session will not be lost.
I've attached passportjs to authenticate user on my site. I use localStrategy and all works fine. But I have some problem (may be it's feature of passportjs) - after restart node, req.user is undefined and users should login again.
What should I do to initialize req.user on start server??
LocalStrategy stores user credentials in memory after login. This means that when you restart node, the credentials will inevitably be lost.
However, passport does use the session cookie functionality in express, so you can persist cookies in the standard express way.
This is quite easy using the RedisStore module, to which you pass the express object:
var express = require('express');
var RedisStore = require('connect-redis')(express);
Before you set up passport, enable session persistence to redis in express:
app.use(express.cookieParser());
app.use(express.session({
secret: "a-secret-token",
store : new RedisStore({
host : 'redis-hostname',
port : 'redis-port',
user : 'redis-username',
pass : 'redis-password'
}),
cookie : {
maxAge : 604800 // one week
}
}));
app.use(passport.initialize());
app.use(passport.session());
You'll need to install redis on your machine, and change the details above to match the redis instance you want to connect to.
You need to store user login info in Redis for e.g. because after restart, all your variables that you set will be undefined
I am building an authentication system using Passport.js using Easy Node Authentication: Setup and Local tutorial.
I am confused about what passport.session() does.
After playing around with the different middleware I came to understand that express.session() is what sends a session ID over cookies to the client, but I'm confused about what passport.session() does and why it is required in addition to express.session().
Here is how I set up my application:
// Server.js configures the application and sets up the webserver
//importing our modules
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var configDB = require('./config/database.js');
//Configuration of Databse and App
mongoose.connect(configDB.url); //connect to our database
require('./config/passport')(passport); //pass passport for configuration
app.configure(function() {
//set up our express application
app.use(express.logger('dev')); //log every request to the console
app.use(express.cookieParser()); //read cookies (needed for auth)
app.use(express.bodyParser()); //get info from html forms
app.set('view engine', 'ejs'); //set up ejs for templating
//configuration for passport
app.use(express.session({ secret: 'olhosvermelhoseasenhaclassica', maxAge:null })); //session secret
app.use(passport.initialize());
app.use(passport.session()); //persistent login session
app.use(flash()); //use connect-flash for flash messages stored in session
});
//Set up routes
require('./app/routes.js')(app, passport);
//launch
app.listen(port);
console.log("Server listening on port" + port);
passport.session() acts as a middleware to alter the req object and change the 'user' value that is currently the session id (from the client cookie) into the true deserialized user object.
Whilst the other answers make some good points I thought that some more specific detail could be provided.
app.use(passport.session());
is equivalent to
app.use(passport.authenticate('session'));
Where 'session' refers to the following strategy that is bundled with passportJS.
Here's a link to the file:
https://github.com/jaredhanson/passport/blob/master/lib/strategies/session.js
And a permalink pointing to the following lines at the time of this writing:
var property = req._passport.instance._userProperty || 'user';
req[property] = user;
Where it essentially acts as a middleware and alters the value of the 'user' property in the req object to contain the deserialized identity of the user. To allow this to work correctly you must include serializeUser and deserializeUser functions in your custom code.
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (user, done) {
//If using Mongoose with MongoDB; if other you will need JS specific to that schema.
User.findById(user.id, function (err, user) {
done(err, user);
});
});
This will find the correct user from the database and pass it as a closure variable into the callback done(err,user); so the above code in the passport.session() can replace the 'user' value in the req object and pass on to the next middleware in the pile.
From the documentation
In a Connect or Express-based application, passport.initialize()
middleware is required to initialize Passport. If your application
uses persistent login sessions, passport.session() middleware must
also be used.
and
Sessions
In a typical web application, the credentials used to authenticate a
user will only be transmitted during the login request. If
authentication succeeds, a session will be established and maintained
via a cookie set in the user's browser.
Each subsequent request will not contain credentials, but rather the
unique cookie that identifies the session. In order to support login
sessions, Passport will serialize and deserialize user instances to
and from the session.
and
Note that enabling session support is entirely optional, though it is
recommended for most applications. If enabled, be sure to use
express.session() before passport.session() to ensure that the login
session is restored in the correct order.
While you will be using PassportJs for validating the user as part of your login URL, you still need some mechanism to store this user information in the session and retrieve it with every subsequent request (i.e. serialize/deserialize the user).
So in effect, you are authenticating the user with every request, even though this authentication needn't look up a database or oauth as in the login response. So passport will treat session authentication also as yet another authentication strategy.
And to use this strategy - which is named session, just use a simple shortcut - app.use(passport.session()). Also note that this particular strategy will want you to implement serialize and deserialize functions for obvious reasons.
It simply authenticates the session (which is populated by express.session()). It is equivalent to:
passport.authenticate('session');
as can be seen in the code here:
https://github.com/jaredhanson/passport/blob/42ff63c/lib/authenticator.js#L233