How to access express.session.MemoryStore via socket.io objects? - node.js

I am doing this in a login function
app.post('/teacherlogin', function(request, response) {
var username = request.body.username;
var password = request.body.password;
con.query('SELECT t_id from login_teacher where username="'+username+'" and password="'+password+'"',function(err,results){
if(results.length > 0) {
request.session.regenerate(function(){
request.session.user = username;
request.session.type = 'teacher';
request.session.id = results[0].t_id;
response.redirect('/teacherhome');
});
} else {
response.redirect('teacherlogin');
}
});
});
now I want to emit the 'id' and 'type' I have stored to the session object. How should I do this? I have read this article but being inexperienced I am facing difficulty in using it. I have used it in my code
var MemoryStore = express.session.MemoryStore;
var sessionStore = new MemoryStore();
app.use(express.bodyParser());
app.use(express.cookieParser('secret text'));
app.use(express.session({
store: sessionStore,
secret: 'secret',
key: 'express.sid'}
));
and
var Session = require('connect').middleware.session.Session;
io.set('authorization', function (data, accept) {
if (data.headers.cookie) {
data.cookie = require('cookie').parse(data.headers.cookie);
data.sessionID = data.cookie['express.sid'].split('.')[0];
console.log('data.sessionID "'+data.sessionID);
data.sessionStore = sessionStore;
sessionStore.get(data.sessionID, function (err, session) {
if (err || !session) {
accept('Error', false);
} else {
data.session = new Session(data, session);
accept(null, true);
}
});
} else {
return accept('No cookie transmitted.', false);
}
});
I am not getting any thing in the session object. I tried to log the contents of the sessionStore and it seems to be empty! Does that mean the information I am storing in the session isn't being stored in the sessionStore? If yes, what should I do to store it there? and if it is stored there why isn't the sessionStore.get function unable to find it?

I am not sure if you're still working on this, but you can access session data with just a MemoryStore. After all how else would Express use it if it didn't work? :)
A simple way to demonstrate MemoryStore working is this:
var express = require("express")
, app = express()
, sessionStore = new express.session.MemoryStore();
// middleware
app.use(express.cookieParser());
app.use(express.session({store: sessionStore, secret: "mysecret"}));
// any endpoint sets a cookie
app.get("/", function(req,res) {
res.send('ok');
});
// This endpoint reveals it
app.get("/session", function(req, res){
sessionStore.get(req.sessionID, function(err, data) {
res.send({err: err, data:data});
});
});
app.listen(3000);
Hitting / followed by /session results in a response of:
{
"err": null,
"data": {
"cookie": {
"originalMaxAge": null,
"expires": null,
"httpOnly": true,
"path": "/"
}
}
}
I suspect your issue may be how you are getting the sessionID from the socket, but it is definitely possible to extract a session from a MemoryStore. Also, remember that restarting the Express server will destroy all of your sessions so you'll need a new cookie after each restart.

You have to use a database to store your express session, then parse the cookie data inside the socket.io definition and with the information obtained get the session info from the database, here is a complete example:
https://stackoverflow.com/a/13098742/218418
You can also use the session ID parsed from the cookie and join the user into a "chat room" with the name of the session.

Related

Sending cookies to browser fails in Express

I am able to successfully create my cookies and I can clearly see them in my console. Now the problem is that I want to send those cookies to the browser and I am not able to do that. When I open my Chrome and go to cookies they are not present there
I have set the secure option to false and also httponly to false but it does not seem to work
req.session.cart = cart;
var cookieValue = JSON.stringify([req.session.cart],{secure:false, maxAge: 180 * 60 * 1000, httpOnly: false });
var cookie = req.cookies.cookieName;
// no: set a new cookie
res.cookie('cookieName',cookieValue);
console.log('cookie created successfully');
// yes, cookie was already present
console.log('cookie exists', cookieValue);
res.redirect('/');
When I create the cookie they must appear in the browser
Like #Mike 'Pomax' Kamermans mentioned, I think this may have to do with res.redirect. Try adding them to your / route as well. Something like this:
// Requires cookie-parser
// https://expressjs.com/en/api.html#req.cookies
// https://github.com/expressjs/cookie-parser
app.post('/cart', function(req, res){
req.session.cart = cart;
var cookieValue = JSON.stringify([req.session.cart],{secure:false, maxAge: 180 * 60 * 1000, httpOnly: false });
var cookie = req.cookies.cookieName;
if(!cookie) {
res.cookie('cookieName',cookieValue);
console.log('cookie created successfully');
} else {
console.log('cookie exists', cookieValue);
}
res.redirect('/');
});
app.get('/', function(req, res){
if((typeof req.cookies == "object")&&(Object.keys(req.cookies).length>0)) {
console.log("Cookies are present");
// copy cookies from req to res
for(var o=0; o<Object.keys(req.cookies).length; o++) {
var key = Object.keys(req.cookies)[o];
res.cookie(key,req.cookies[key]);
}
}
res.status(200).send("OK");
});

How to only allow one user connection alive at the same time with Nodejs and Expressjs?

I just need to manage the concurrence on my app built with nodejs on the top of the nestjs framework.
As a far as I know, the most simplest way to do that is controlling that online one session user in expressjs is alive.
I am not taking care about security or whatever other issues, just want to know how many users are connected and restricting it to only one user session till its session is expired.
Here is my codebase
var express = require('express');
var session = require('express-session');
var app = express();
var numConnections = 0;
app.use(session({
cookieName: 'sessionTest',
secret: 'eg[isfd-8yF9-7w2315df{}+Ijsli;;to8',
cookie: {
secure: false,
maxAge: 1000 * 10,
sameSite: true
}
}));
app.use((req, res, next) => {
console.log(req.session.store)
console.log(req.session.ip)
console.log(req.session.useragent)
console.log(req.connection.remoteAddress)
console.log(req.headers['user-agent'])
if (numConnections === 0
// && req.session
) {
req.session.ip = req.connection.remoteAddress;
req.session.useragent = req.headers['user-agent'];
req.session.page_views = 1;
res.send("Welcome to this page for the first time!");
numConnections++;
console.log(req.session);
next();
}
else if (numConnections == 1 &&
req.session.ip === req.connection.remoteAddress
&& req.session.useragent === req.headers['user-agent']
) {
req.session.page_views++;
res.send("You visited this page " + req.session.page_views + " times");
console.log('TEST');
next();
} else {
console.log('There is someone using the app!!!');
return res.sendStatus(401);
}
})
app.listen(3001);
I really appreciate if someone can help me
You could use the store to retrieve the current amount of open sessions. The doc says stores may implement length and/or all methods. However, it appears that only the default MemoryStore handles these. You can look at all the compatible store implementations at the bottom of the page and pick the one that fits your environment.
It probably (i.e. not tested) looks like this:
var session = require('express-session');
var memoryStoreThatWillBeChangedBeforeLiveEnvironment = new MemoryStore();
...
app.use(session({
...
store: memoryStoreThatWillBeChangedBeforeLiveEnvironment
}));
app.use((req, res, next) => {
memoryStoreThatWillBeChangedBeforeLiveEnvironment.length((err, size) => {
if (err) return res.status(418).send("I'm a teapot");
var numConnections = size;
// call your code here
})
});
(Quite obviously, if an implementation only offers the all method, you can count the returned array of sessions.)

Prefix getting repeated in connect-redis

I am trying to use connect-redis: "~3.0.1" with express-session: "~1.12.1" in my web app to store user sessions. But when I give the prefix field in RedisStore, its getting repeated two times. Please tell if I am doing something wrong.
"mySession:mySession:yzO1mRhloENUMYLkAz2nZprcfvcFMNHY"
"mySession:mySession:0L8prCJAoq0CmJ9tTwTJ_smQ4fH2R_H9"
While searching I came across similar issue with laravel code:
https://github.com/laravel/framework/issues/5353
Please tell if something similar is happening here and if yes, what is the workaround.
Below is the code I am using:
var sessionMiddleware = session({
secret : 'secretPass',
store: new RedisStore({
prefix:'mySession:',
ttl: 1800}),
resave: false,
saveUninitialized: false
});
app.use(function (req, res, next) {
var tries = 3;
function lookupSession(error) {
if (error) {
return next(error);
}
tries -= 1
if (req.session !== undefined) {
return next();
}
if (tries < 0) {
var errorMsg = 'Error in getting session. Please refresh the page.';
return next(new Error(errorMsg));
}
sessionMiddleware(req, res, lookupSession);
}
lookupSession();
})
I was able to fix the issue by creating redis client before hand and passing it to the session object as mentioned below and not depend on connect-redis to create it for me.
redis_client = require('redis').createClient();
var sessionMiddleware = session({
secret : 'secretPass',
store: new RedisStore({
prefix:'mySession:',
ttl: 1800,
client: redis_client}),
resave: false,
saveUninitialized: false
});
If i dont pass the client, the below code in connect-redis.js is passing the options while creating the redis client and then is adding one extra prefix.
// convert to redis connect params
if (options.client) {
this.client = options.client;
}
else if (options.socket) {
this.client = redis.createClient(options.socket, options);
}
else {
this.client = redis.createClient(options);
}

Strongloop passport component returns "active" from loopback.getCurrentContext() empty

I have this var context = loopback.getCurrentContext(); that returns me under context.active.acccessToken the current Token used in the call (tested in the Explorer).
Now, when trying to use the Passport component, I copied the code form the server.js example git and put it on my boot/aaa-scripts.js the context.active varible is an empty {}.
// Passport configurators..
var loopbackPassport = require('loopback-component-passport');
var PassportConfigurator = loopbackPassport.PassportConfigurator;
var passportConfigurator = new PassportConfigurator(app);
// attempt to build the providers/passport config
var config = {};
try {
config = require('../../providers.json');
} catch (err) {
console.trace(err);
process.exit(1); // fatal
}
// The access token is only available after boot
app.middleware('auth', loopback.token({
model: app.models.AccessToken
}));
app.middleware('session:before', loopback.cookieParser(app.get('cookieSecret')));
app.middleware('session', loopback.session({
secret: 'kitty',
saveUninitialized: true,
resave: true
}));
passportConfigurator.init();
passportConfigurator.setupModels({
userModel: app.models.Member,
userIdentityModel: app.models.UserIdentity,
userCredentialModel: app.models.UserCredential
});
for (var s in config) {
var c = config[s];
c.session = c.session !== false;
passportConfigurator.configureProvider(s, c);
}
var ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn;
app.get('/auth/account', ensureLoggedIn('/'), function(req, res, next) {
res.send(req.user);
});
I have commented out parts of the copied code, and the part that's getting me trouble is:
// The access token is only available after boot
app.middleware('auth', loopback.token({
model: app.models.AccessToken
}));
app.middleware('session:before', loopback.cookieParser(app.get('cookieSecret'));
app.middleware('session', loopback.session({
secret: 'kitty',
saveUninitialized: true,
resave: true
}));
I have tried both AccessToken and accessToken
What I'm missing?
You may need to attach a User-related models to your datasource first:
app.models.AccessToken.attachTo(dataSource);
Since I'm Using an Angular app, I ended up commenting this 3 lines
app.middleware('auth', loopback.token({
model: app.models.AccessToken
}));
Everything seens to be working all right.

Force share.js use the same express session data

I have a simple express app that use session middleware together with passport-local middleware. Then I use share.js with browserchannel to stream data to server via share.listen(stream). All in align with documentation here.
My problem is that I cannot access session data (modified by passport-local and containing userID that was logged in) within stream. I need it to be able to restrict/grant access within client.on('message', function(data) {..}); based on some logic, but what of first importance is to check that the message came from logged in user. There, if I try to read ID it will be different from what potencialy is inside req.user._id. It seems that there share.js or browserchannel uses some different session, maybe?..
Here's the code:
var app = express();
var express = require('express');
...
// SETUP AND INIT
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true,
limit: 1024 * 1024 * 10
}));
app.use(methodOverride());
app.use(session({
secret: global.CONFIG.session.secret,
maxAge: new Date(Date.now() + 1000 * 60 * 60 * 24 * 2),
store: new MongoStore(global.CONFIG.mongo),
resave: true,
saveUninitialized: true
}));
app.use(express.static(__dirname + '/build'));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
// Create the sharejs server instance.
var backend = livedb.client(livedbMongo(global.CONFIG.mongo.url, false));
var share = sharejs.server.createClient({
db: backend
});
app.use(browserChannel(function(client) {
var stream = new Duplex({objectMode: true});
stream._write = function(chunk, encoding, callback) {
if (client.state !== 'closed') {
client.send(chunk);
}
callback();
};
stream._read = function() {
};
stream.headers = client.headers;
stream.remoteAddress = stream.address;
client.on('message', function(data) {
console.log(client.id) // <- I wish it was the same as in req.user._id..
stream.push(data);
});
stream.on('error', function(msg) {
client.stop();
});
client.on('close', function(reason) {
stream.emit('close');
stream.emit('end');
stream.end();
});
// Actually pass the stream to ShareJS
share.listen(stream);
}));
It seems to me, from looking at the code, that there might be a solution that won't require hacking the module:
var browserChannel = require('browserchannel').server;
var middleware = browserChannel(options, function(session, req) {
if (req.user) {
session.user = req.user;
}
});
app.use(middleware);
See here.
I have the same problem and I solved it by wrapping the browserchannel middleware constructor in a custom constructor:
function myMiddlewareConstructor () {
var request;
var bcMiddleware = browserChannel(function (client) {
//here you see the request
});
return function (req,res,next) {
request = req;
bcMiddleware(req,res,next);
}
}
app.use(myMiddlewareConstructor());
It avoids having to change the browserchannel code.
After several days of inspecting the code I have found a solution. If we look at this line in browserchannel/dist/server.js we can see that the session is being created using some information from initial request. We can modify this part of code by adding
session = createSession(req.connection.remoteAddress, query, req.headers);
// ----------- we add this ------------
session.user = {};
if( req.user )
session.user = req.user;
// ------------------------------------
This will add user session details from initial request to the session variable.

Resources