NodeJS: session variables shared for all clients (express-session) - node.js

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

Related

Keycloak node.js adapter doesn't invalidate connect.sid session cookie on logout

Node.js keycloak-nodejs-connect adapter (version 4.3) is used in an application gateway for protecting microservices' endpoints according to docs:
var session = require('express-session');
var Keycloak = require('keycloak-connect');
var memoryStore = new session.MemoryStore();
var keycloak = new Keycloak({ store: memoryStore });
However, after a user log in/ log out flow, connect.sid cookie originating from express-session is still stored inside browser. It causes unexpected issues if another user logs in via the same browser afterwards.
How to clear connect.sid express-session cookie correctly?
Overriding adapter's session store code by adding response.clearCookie('connect.sid', { path: '/' }); to unstore function helped. However, it seems too complicated:
var SessionStore = require('keycloak-connect/stores/session-store');
let store = (grant) => {
return (request, response) => {
request.session[SessionStore.TOKEN_KEY] = grant.__raw;
};
};
let unstore = (request, response) => {
delete request.session[SessionStore.TOKEN_KEY];
response.clearCookie('connect.sid', { path: '/' });
};
SessionStore.prototype.wrap = (grant) => {
if (grant) {
grant.store = store(grant);
grant.unstore = unstore;
}
};
Does some keycloak adapter or express-session configuration achieve the goal better?
Your thinking is correct, I'm not sure overriding Keycloak's unstore method is the best way to go about it though (might mess things up if you upgrade Keycloak, or if you want to use unstoreGrant to remove just the grant, but keep the rest of the session).
A better approach would be to create a new middleware that triggers on your logout route:
app.use('/logout', (req, res, next) => {
req.session.destroy();
res.clearCookie('connect.sid', { path: '/' });
res.redirect(keycloak.logoutUrl()); // optional
});

Storing variable in session with ejs and node js

Im trying to think of a solution to the following issue:
I have a variable that needs to be separate for every unique visitor on the page. Its just simple list that is filled when a visitor clicks on some items on the page. It would be empty for each unique visit.
Currently Im keeping that in the backend, it is pretty bad solution as everyone currently using the app is adding to the same list
The backend code:
thelist=[]
app.get("/show/:id", function (req, res) {
var id = req.params.id;
thelist.push(id)
console.log(thelist);
res.redirect('/')
});
app.get("/run", function(req, res) {
res.render("data", {data: thelist})
thelist = []
});
The main point is that list thelist is then passed to a python script which is also connected via node.js into the app. Therefore i need that variable to stay in the backend.
Also I was thinking of keeping it simple, so I don't want to force users to create account to use the app, so some sort of cookies/cache/any other form of session storage is what im looking for.
You can create a session map(like hashmaps). I have integrated the same in one of my projects and it is working flawlessly. Below is the code for it and how you can access it:
hashmap.js
var hashmapSession = {};
exports.auth = auth = {
set : function(key, value){
hashmapSession[key] = value;
},
get : function(key){
return hashmapSession[key];
},
delete : function(key){
delete hashmapSession[key];
},
all : function(){
return hashmapSession;
}
};
app.js
var hashmap = require('./hashmap');
var testObj = { id : 1, name : "john doe" };
hashmap.auth.set('test', testObj);
hashmap.auth.get('test');
hashmap.auth.all();
hashmap.auth.delete('test');
You can use this approach as a session storage for your node application. Keep in mind that this storage will only be persistent for the current session only.
The best solution here would be express session (https://github.com/expressjs/session)
As I see your code I can figure out you are using express. And if you are using express that is enough for below code.
Here is the easiest way with minimal code to achieve what you are looking for:
Example:
app.set('oneSetting', 'one');
console.log(app.settings.oneSetting);
OR
app.set('port', 3000);
app.get('port'); //3000
You can access the same variables in the req object too, example:
req.app.get('port')
Why don't you use express-session.
You can use it like the following :
App.js
var app = express();
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
Controller.js
//Sets the id in the session object.
app.get('/show/:id', function (request, response, next) {
request.session.id = id;
})
//Retrieve the id from the session object.
app.get('/showMyId',function(request,response,next)){
response.send(request.session.id;);
})
This way you do not have to manage the global list and all your ids would be present inside their respective session object.
This approach uses a cookie session , there are also alternative strategies available for storing your session object like a proper storage server.
From my understanding you are trying to count no.of unique visitors for your application.
You can use simple cookie to track use and set expiry long time so cookie is not erased.
Express had cookie parser module which will automatically parse cookie.
thelist={}
var express = require('express')
var cookieParser = require('cookie-parser')
var app = express()
app.use(cookieParser())
app.get("/show/:id", function (req, res) {
var visitorId = req.cookies.visitorid;
var id = req. params.id
if(!req.cookies[id]){
///create unique id like GUID or something which allows you to uniquely identify user
if(!visitorId){
visitorId = '<GUID>';
}
if(!thelist[visitorId]){
thelist[visitorId] = [];
}
thelist[visitorId].push(id);
console.log(thelist);
}
res.cookie('visitorid',visitorId, { maxAge: 900000, httpOnly: true }); //You can extend max age as required. Setting up every time will also extend expiry.
res.cookie(id,'visited', { maxAge: 900000, httpOnly: true }); //set cookie with id saying this is allready visited
res.redirect('/')
});
app.get("/run", function(req, res) {
res.render("data", {data: thelist})
thelist = []
});
you can use sessionstorage on your window session
sessionStorage.setItem(KEY,VALUE);

Accessing session token from other routers in node js

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.

How to check session in Node.js Express?

I try to check if session in Express 4 is exist:
if(req.session.user == undefined) {}
It gives me error:
Cannot read property 'user' of undefined
How I can check if exist value in session?
From the source:
How to use Express Session ?
Before heading to actual code, i want to put few words about
express-session module. to use this module, you must have to include
express in your project. Like for all packages, we have to first
include it.
server.js
var express = require('express');
var session = require('express-session');
var app = express();
After this, we have to initialize the session and we can do this by
using following.
app.use(session({secret: 'ssshhhhh'}));
Here ‘secret‘ is used for cookie handling etc but we have to put some
secret for managing Session in Express.
Now using ‘request‘ variable you can assign session to any variable.
Just like we do in PHP using $_SESSION variable. for e.g
var sess;
app.get('/',function(req,res){
sess=req.session;
/*
* Here we have assign the 'session' to 'sess'.
* Now we can create any number of session variable we want.
* in PHP we do as $_SESSION['var name'].
* Here we do like this.
*/
sess.email; // equivalent to $_SESSION['email'] in PHP.
sess.username; // equivalent to $_SESSION['username'] in PHP.
});
After creating Session variables like sess.email , we can check
whether this variable is set or not in other routers and can track the
Session easily.
Firstly it depends on the library you are using, maybe some libraries have utilities for that, I assume you're using express-session.
This is just Okay:
if(req.session.user){}
They are useless:
if(typeof req.session.user !== "undefined"){}
if(typeof req.session.user !== "undefined" || req.session.user === true){}
The reason: req.session is an object, just like normal objects:
var obj = { name : "adam" }
If you try to get obj.age which it doesn't exist and defined,
the getter function of the object, firstly check if it exists or not, if it's not, it wouldn't produce a fatal error and instead it assigns that property to undefined value.
That's cool, so obj.age get's undefined ( JS has undefined as a value type), moreover undefined is a falsy value (when you coerce it to boolean it becomes false, so it's falsy), which means you can simply check it in conditional statements like this: if(obj.age){}
You can't just create a session without any middleware (Im assuming this is what you've tried).
Read up on the express-session middleware docs, found here:
https://github.com/expressjs/session
Basic implementation example:
Create a session:
app.use(session({
genid: function(req) {
return genuuid() // use UUIDs for session IDs
},
secret: 'keyboard cat'
}))
To read a session:
// Use the session middleware
app.use(session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}))
// Access the session as req.session
app.get('/', function(req, res, next) {
var sess = req.session
if (sess.views) {
sess.views++
res.setHeader('Content-Type', 'text/html')
res.write('<p>views: ' + sess.views + '</p>')
res.write('<p>expires in: ' + (sess.cookie.maxAge / 1000) + 's</p>')
res.end()
} else {
sess.views = 1
res.end('welcome to the session demo. refresh!')
}
})
There are a number of tutorials you can find online, e.g:
https://www.thepolyglotdeveloper.com/2015/01/session-management-expressjs-web-application/
The issue you are facing is maybe you are not using the session middleware in ALL of your requests.
You can check it the following way :
Add this to all of your routes :
app.use(session({ secret: 'keyboard cat',resave:false,saveUninitialized:false, cookie: { maxAge: 60000 }}));
Authentication Route :
router.post('/', function(req, res, next) {
//login logic here
//if login successfull
req.session.user = username //your unique identifier
req.send("Hurray! logged in");
//else
req.send("Credentials error");
});
Check in any route:
router.get('/dashboard', function(req, res, next) {
if (req.session.user)
//do stuff here
else
//redirect to login page
})
This works :)

Controlling Session Start with express and connect middleware

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.

Resources