Is there a way to control when a session starts with connect's session middleware?
For example, if I have express app config:
var app = express();
app.configure(function(){
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('secret'));
app.use(express.session({ store:sessionStore, ... }));
});
Then on every request, if no session cookie is given, a session is started. What if I wanted to start a session only when the user has been authenticated?
For example, say I have two routes /protected and /login.
If someone hits /protected without a session cookie, the middleware will NOT start a new session. (req.session is null)
If someone hits /protected with a session cookie, the middleware will CHECK to see if there is a matching active session for the cookie and set req.session, but will not start a new session. (req.session could have a value or be null)
If someone hits /login with the correct params, then a session is started explicitly and a cookie is set only then.
The only way to start a session should be explicitly:
app.post('/login', function(req, res, next) {
// connect to database and validate user...
db.authenticate( req.body.user, req.body.pass, function(allow) {
if (allow) {
// START SESSION HERE
// this will send set the cookie
}
});
}
Is there any way of accomplishing this with the existing connect session middleware?
What you want to do is to remove this line:
app.use(express.session({ store:sessionStore, ... }))
Now sessions are disabled by default and it's up to you to decide which controller is going to use them:
var useSessions = express.session({ store:sessionStore, ... });
var preCb = function (req, res, next) {
// authenticate and stuff
// ....
if (authenticated === true) {
next();
}
};
app.post('/login', useSessions, function(req, res, next) { ... });
app.post('/protected', preCb, useSessions, function(req, res, next) { ... });
Even if a session is started every time, it does not really matter because it will be empty. If you are attempting to use it to authenticate access (which seems to be the case), the simplest solution is to set an attribute in your session (such as req.session.authenticated = true;), and check that. This way technically ever visitor has a session, however you will only utilize the session if req.session.authenticated == true. It may not be exactly what you're looking for, but it is the easiest way to get this done.
Related
I am trying to access my session token from other routes after setting it in a route. I am currently unsuccessful. Following the relevant code of the three files.
server.js: It calls the routes thermostats, login and also sets session token.
var session = require('express-session');
var app = express();
app.use(session({secret: 'keyboard cat',cookie: { secure: true }}))
var router = express.Router();
var thermostats = require('./api/routes/thermostats')(router, app, session);
require('./api/routes/login')(router, app, session, thermostats);
login.js: When the user goes to localhost:3000/login/, the login token needs to be saved in the session
module.exports = function(router,app, session, thermostats){
router.get('/login/', function(req, res) {
list(req, res) //response of this function has session which needs to be saved.
console.log(res.session)
app.use(session(res.session)) //trying to save the res.session as session token
});
}
thermostat.js: Needs to access the session token before can display any information.
module.exports = function(router,app){
router.get('/thermostats/', function(req, res) {
console.log(req.session) //Set to default/null values and not the updated session values
});
}
It might be something small but I cannot figure out the issue. I would appreciate any help.
Thank you
Express-session should automatically save the session, based on the configuration.
Looking at the 'resave' config option in the express-session docs:
resave
Forces the session to be saved back to the session store, even if the session was never modified during the request. Depending
on your store this may be necessary, but it can also create race
conditions where a client makes two parallel requests to your server
and changes made to the session in one request may get overwritten
when the other request ends, even if it made no changes (this behavior
also depends on what store you're using).
This is by default, true, so it should already start working without you needing to add app.use(session(res.session).
Edit: You will be able to save to the session by adding fields to the req.session object:
router.get('/login/', function(req, res) {
getDataFromExternalApi(req, function(err, apiResponse) {
var data = apiResponse.data;
req.session.data = data;
// if resave option is true, this should automatically save to the session store after this request is done.
});
});
Generally, you shouldn't be using app.use in your request handlers. Those are generally reserved for setting up the server, as it defines what middleware express uses.
I'm using express-session for handling session variables in a NodeJS Application. I use session variables for handling login-authorization, for saving logged user information and other things.
The thing is that this session variables are being shared for all clients, it looks like they are working as NodeJS instance variables instead of session variables for every client. What I want to have is session variables working as they work in PHP.
This application is retrieving all the data from web services in a Laravel Back-End application.
This is my code:
Initializing session:
var sessionHelper = require('./helpers/session-helper');
var session = require('express-session');
app.use(session({
secret: config.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {secure: true}
}));
sessionHelper.init(session);
global.SESSION = sessionHelper;
session-helper module:
var _session = null;
module.exports = {
init: function (session) {
_session = session;
},
has: function (name) {
return ((_session[name]) ? true : false);
},
get: function (name) {
return _session[name];
},
set: function (name, value) {
_session[name] = value;
},
clear: function (name) {
_session[name] = undefined;
}
};
Using session variables:
SESSION.set('hotels', response.hotels);
SESSION.get('hotels');
Thanks,
The problem is that you've globally cached a specific instance of session in your helper object. As a general practice, that's not a good idea unless you are very sure about how that object's lifecycle and state are managed.
The way that express sessions work is that the express middleware maintains a separate instance of session per request. You should be accessing that session typically in the body of a request:
app.get('/', function(req, res, next) {
var sess = req.session;
// sess will have values specific to each unique browser session
console.log('This session has an id of ', sess.id);
});
If you still feel you want to setup a helper, you can make that available for every request by configuring Express with the use method before your app.get or any other router methods - here is a rough idea how:
// This should be AFTER the app.use statement for express sessions
app.use(function (req, res, next) {
var sessionHelper = require('./helpers/session-helper');
sessionHelper.init(req.session);
req.sessionHelper = sessionHelper;
next();
})
Now, in any subsequent route handler code, you will find that req.sessionHelper is available for use. This is because you've told Express to first add your helper to the request object for ALL requests. So, this will work:
app.get('/', function(req, res, next) {
console.log('Session ID: ', req.sessionHelper.get('id'));
});
Just remember that you are still responsible for session storage. You need to combine express-sessions with a store (like connect-redis or connect-mongo) in order to persist session-data between restarts. The full list is here: https://github.com/expressjs/session#compatible-session-stores
In my express app I've set static files to be served from the /public directory with this line:
app.use(express.static(__dirname + '/public'));
Now I need to add a middleware for authentication before serving the static content and if the user is not authenticated to be redirected to a route for authentication (e.g., /login).
I'm not really sure how I have to do it. Any ideas?
Since you didn't specify it, I'm going to assume that you already have some kind of authentication system.
In Express, the order of the middlewares in the code matters: if you want to have middleware 1 executed before middleware 2, you should place them accordingly in your code. Since express.static is a middleware, if you want authentication before serving your static files you can simply write your authentication middleware before the call to express.static
app.use(function (req, res, next) {
if (!userAuthenticated(req)) {
return res.redirect('/login');
}
next();
});
app.use(express.static(__dirname + '/public'));
I am assuming you have a userAuthenticated function which is for instance checking if the HTTP requests contains a valid access-token.
Read more about middlewares.
Check out Passport.
Passport has many authentication strategies.
Here's an example with basic HTTP authentication:
var express = require('express');
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var db = require('./db');
// Configure the Basic strategy for use by Passport.
//
// The Basic strategy requires a `verify` function which receives the
// credentials (`username` and `password`) contained in the request. The
// function must verify that the password is correct and then invoke `cb` with
// a user object, which will be set at `req.user` in route handlers after
// authentication.
passport.use(new BasicStrategy(
function(username, password, cb) {
db.users.findByUsername(username, function(err, user) {
if (err) { return cb(err); }
if (!user) { return cb(null, false); }
if (user.password != password) { return cb(null, false); }
return cb(null, user);
});
}));
// Create a new Express application.
var app = express();
var authenticate = passport.authenticate('basic', {
session: false,
failureRedirect: '/login'
});
app.use(authenticate, express.static(__dirname + '/public'));
Depends on what kind of authentication you are looking for, but if you just want some login-feature, this is what you need: http://passportjs.org/
It has support for local login strategies, as well as a whole bunch of 3rd party strategies like facebook, twitter, etc.
If you need something else, simpler or self-made, just write a middleware to use before you declare the static endpoint, and call next() if everything checks out, and res.redirect if user needs to retry.
I'm using sessions and cookies to authenticate the users. I would like to check for users having a cookie and if so i will set the sessions variables.
So basicly what i do is :
Check if sessions variables exist
If not, check if user has cookie
If he has a cookie, I compare the value in my database.
If everything's ok, I set up the session.
Now i'd like to have that process into a module so i don't have to paste that code into each routes of my site.
Let's say I've put all that code in a middleware route located at routes/middleware/check_auth.js.
How do I export this module so I can check in my route page if the user has auth or not, something like :
//routes/index.js
var check_auth = require('./middleware/check_auth');
module.exports = function(app){
app.get('/', check_auth, function(req, res){
if(variable_from_check_auth == true){
res.render('index_with_auth');
}else{
res.render('index_without_auth');
}
});
};
Btw, I'm not sure if it's the right way to do or if I simply have to :
Call the module on each routes.
Check for some sessions variables before rendering.
If someone could help me!
You can just export your middleware as simple as this(assuming you are using express session handler and cookie parser):
var userModel = require('./user');
module.exports = function check_auth(res, req, next) {
if (!res.session) {
req.send(401);
return;
}
userModel.isAuthenticated(req.session.id, function (result) {
if (!result) {
req.send(401);
return;
});
next();
});
};
I am trying to use connect/express cookieSession() in order to store my node.js sessions in cookies (and thus, avoiding a server-side session store). This would help me to 'remember' the user when they log in and keep sessions alive even after server restarts.
I would like to do this with cookieSession():
app.use( express.cookieSession( { secret: 'secret_key' } ) );
app.use( function (req, res, next) {
if ( req.method == 'POST' && req.url == '/login' ) {
if ( req.body.remember ) {
req.session.cookie.maxAge = 30*24*60*60*1000; // Rememeber 'me' for 30 days
} else {
req.session.cookie.expires = false;
}
}
next();
});
However, this does not work, because req.session.cookie is undefined. I also tried the following, but it didn't seem to work:
app.use( express.session( { secret: 'secret_key' } ) );
app.use( function (req, res, next) {
if ( req.method == 'POST' && req.url == '/login' ) {
if ( req.body.remember ) {
req.cookies['connect.sess'].maxAge = 30*24*60*60*1000; // Rememeber 'me' for 30 days
} else {
rreq.cookies['connect.sess'].expires = false;
}
}
next();
});
Starting out with
app.use(express.cookieSession({ secret: config.server.cookieSecret }));
And changing it to
app.use(function(req, res, next) {
express.cookieSession({
secret: config.server.cookieSecret,
cookie: {
maxAge: req.param('remember') ? 20000 : 3000
},
})(req, res, next);
})
So, we create our own middleware, wrapped around the cookieSession middleware, changing the maxAge based on a param.
So, whenever you change the session you'll need to pass a remember in the body, query, or params( that's where req.param() looks ). In most cases, you only set a user_id to the session once, at login.
It's 3 seconds or 20 seconds to test and ensure it works.
And again, it might be not very helpful if you're setting stuff to your session a lot, but if you just set a user_id to session at login, this is all you need.
If you are setting lots of stuff to your session, you should know that data get passed around at every request, and you should save only the minimum to the session, like user_id, then look up the data you need for each request, to keep the overhead down on the user.
I think this does what you want:
// Using express.session instead of express.cookieSession
app.use(express.session({ secret : 'secret_key' }));
app.use( function (req, res, next) {
if ( req.method === 'POST' && req.url === '/login' ) {
if ( req.body.remember )
{
req.session.cookie.maxAge = 30*24*60*60*1000;
// needed to make the session `dirty` so the session middleware re-sets the cookie
req.session.random = Math.random();
}
else
{
req.session.cookie.expires = false;
}
}
next();
});
cookieSession does some funky stuff, like del req.session.cookie (not sure why).
You have to first set req.session.cookie so that you can set maxAge. Trying to use it before you set it gives req.session.cookie is undefined
express.cookieSession has default values which it accepts, see here. You should mention all the parameters you are going to use. You can set cookie via the following :
app.use(express.cookieSession({ secret: 'secret_key', cookie :{ path: '/', httpOnly: true, maxAge: 30*24*60*60*1000} });
A little late to the table but I thought this answer may help people going forward...
I was using cookie-session which doesn't create a cookie object on request.session. To properly implement rememberMe functionality using request.session.cookie I switched cookie-session to express-session and that solved everything. So now there is a cookie object on session and doing this inside of a request is possible...
npm install express-session
app.post('/login', function(request, response, next) {
passport.authenticate('local', function(err, user, info) {
if(err) return next(err);
if(!user) {
request.flash('loginMessage', info.message);
return response.redirect('/account/login');
}
request.login(user, function(err) {
if(err) return next(err);
if(request.body.rememberMe)
request.session.cookie.maxAge = 2592000000;
else
request.session.cookie.expires = false;
return response.redirect(options.redirect);
});
})(request, response, next);
});
This is also pretty late but it might help other people.
It seems like to me the best way to persist your session data is to store it in something like redis. The question asked for a way that didn't use server storage, but I think he was referring more to MemoryStore. Maybe not but either way this is what I did.
I used express-session and connect-redis
npm install -g connect-redis
npm install -g express-session
Then you configure stuff.
// session modules
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session')
var redisStore = require('connect-redis')(session); // this sets up Redis to work with your session cookies
var app = express();
Then you just initiate your session with the store option set to your redisStore.
The maxAge part sets the lifetime of each session to an hour, the session middleware resets it when it's accessed.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
store: new RedisStore({
host:'127.0.0.1',
port:6380,
prefix:'sess'
}),
cookie: {maxAge: 3600000 },
secret: 'session_secret'
}));
Now when a client connects, express should store the session data automatically in a Redis data structure. Since it's not just cached in memory, your server can crash and still have all the relevant session data still available at the IP address and and port specified.
Yummy seems to allow modifying cookies expiry after creation.