On my node.js server (running express and socket.io), when a person connects, I add them to an array and save their location in the array into their session so each connection has access to their own information like so:
session.person_id = people.length;
session.save();
people.push(new Person());
//people[session.person_id] => Person
And the only thing that I save in the session is person_id. I have been using express.session() to handle this, which has been working fine until I started sending information to everyone who is connected at once. As I loop through their connections and get their sessions, sometimes (I can't figure out how to dupe the error) session exists but not session.person_id.
Anyways I'm hoping that by changing how I store the session, it can help me figure out my problem. So I have a few questions that I can't find answers to anywhere.
Where is the cookie from express.cookieSession() stored? Server-side or client-side?
Does express.cookieSession() allow for multiple servers running behind a load-balancer?
Is it possible for a user to manipulate session data when using express.cookieSession()?
1 - Where is the cookie from express.cookieSession() stored? Server-side or client-side?
The cookie is sent on the replies from the server, and the browser sends that cookie back with each request.
2 - Does express.cookieSession() allow for multiple servers running behind a load-balancer?
Yes, if you use a shared store (like RedisStore)
3 - Is it possible for a user to manipulate session data when using express.cookieSession()?
Not if you use signed cookies (the default for session cookies in express when you provide a secret when initializing the session.
var redis = require('redis').createClient();
app.use(express.session({
secret: "some random string",
store: new RedisStore({client: redis})
}));
Related
I have a very simple nodejs chatbot that learns things about a client that is connected to it. For example, if a client says "My name is Bob" and then asks the chatbot "What is my name?" the chatbot will say "Bob" the problem is that if another client connects to the server and then asks the chatbot the same question, the chatbot will also reply with "Bob". I understand I need to implement some way of dealing with sessions but everything I found online about sessions was explaining things about cookies and how to store user authentication.
All I want to do is let each client have his own instance of the server that knows nothing about the other clients.
You can use an in-memory database such as Redis to store information about connected users by identifying them by their id.
Let's suppose you are using messenger platform for your bot. Each message you receive from Facebook to your webhook comes along with some information, such as the id of user who sent the message, the id of the page to which the message was sent, etc. You can then use Redis to store any information about any user (userID, userName, etc). Once you receive a message, you just query information about the user using their id as the key.
Session is just the variable on server. you can store in variable, array or database (better option in case server restarts). so it totally depends upon your logic.
Let say for each connected user id you will have its data.
var userData = {1: {username: "bob" }, 2: {username: "alice", ... }}
You need to use cookies, sessions and a session store.
If you use a proper session library, it will create cookies automatically for the user on the client and a session on the server.
On the browser, only the sessionID is stored in the cookie, on the server, you can attach as many properties as you want to the session. To persist them, you can use Redis.
Use a combination of these three libraries (notice the first and second are actually different):
express-sessions
express-session
redis
Then your initialize it in this way ...
var redis = require('redis');
var client = redis.createClient();
var crypto = require('crypto');
var session = require('express-session');
app.use(session({
secret: 'A-SECRET-NOBODY-KNOWS',
resave: false,
saveUninitialized: true,
genid: (req) => {
return crypto.randomBytes(16).toString('hex');;
},
store: new (require('express-sessions'))({
storage: 'redis',
instance: client, // optional
collection: 'sessions' // optional
})
}));
Finally using the sessions is as simply as
//read
let token = req.session.token
//write
req.session.token= 'A very secret token';
I wrote a tutorial that has more information on how to use sessions, scroll down to part 6.
I am currently working on a project with socket.io, and i'm not sure to fully understand the mechanism of reconnection.
Since a disconnection could happen client side, i would like to know how to maintain the state of the socket on the server. I already know that socket.io-client will try to reconnect automatically, but i would like to know if it is possible to ensure the state of the socket on the server side.
I was thinking of a cookie based session, with express for example, but again i am not sure if i'm taking the good way about this. Is there another solution i should consider?
For the record, i successfully configured HAProxy with a cookie based sticky-sessions mechanism. Could it be possible to mix this mechanism with a cookie session on the socket.io server ?
Thanks
William
I think cookie based sessions are your best option. Look into the session.socket.io module. Looks like it was built specifically for this.
var SessionSockets = require('session.socket.io');
var sessionSockets = new SessionSockets(io, sessionStore, cookieParser);
sessionSockets.on('connection', function (err, socket, session) {
//your regular socket.io code goes here
//and you can still use your io object
session.foo = 'bar';
//at this point the value is not yet saved into the session
session.save();
//now you can read session.foo from your express routes or connect middlewares
});
Alternatively you could implement sessions yourself using express as you mentioned. I don't know of any easy way to integrate with HAProxy.
I have a node/socket.io/express server that's connected to a HTML file (like so). So visiting the web address connects you to the server. I am trying to set up a system where by, said server is being run on multiple computers at a time and by way of some sort of username and password authentication, visiting the webpage with specific credentials connects you to one of the computers with those same credentials running the server.
Ive seen mention of "Redis" from previous similar questions but they are pretty old and im wondering if there is a newer or better way of achieving this.
You won't find a lot of up-to-date documentation since Express 4 is kind of new, so let me try to remedy that here :
Authentication in Express 4.x and Socket.IO 1.x
Let's start with a confusion I think you're making:
What is Redis?
Redis is a data structure engine. It allows you to store key/values pairs, nothing more (In this context). The only thing it can do for you when building your authentication system is storing the data, user info, session ids, etc. In your case, you can share a store between multiple machines, the same way you'd share a database, or a text file.
Redis
Authenticate user to node/express server
One of the ways you can do that is by using passport. Passport is a middleware dedicated to authentication on Node.js. It is made for use with Express and relatively easy to setup. There is an excellent tutorial series on how to setup passport with your express application, so I won't detail this part, please take the time to go through the series, it's invaluable knowledge.
Here's the link to the first part, which is the one I'll focus on for the next step.
Add socket.io to the mix
Socket.io doesn't have access to the session cookies that you create in part 1. To remedy that, we will use the passport-socketio module.
Passport-socketio requires a local session store, as opposed to a memory store. This means we need some way to store the session data somewhere, does that ring a bell?
Exactly, Redis.
You can try other stores, like mongoDB or MySQL, but Redis is the fastest.
In this example, I'll assume that your express app and passport are already operational and will focus on adding socket.io to the app.
Setup :
var session = require('express-session'); //You should already have this line in your app
var passportSocketIo = require("passport.socketio");
var io = require("socket.io")(server);
var RedisStore = require('connect-redis')(session);
var sessionStore = new RedisStore({ // Create a session Store
host: 'localhost',
port: 6379,
});
app.use(session({
store: sessionStore, //tell express to store session info in the Redis store
secret: 'mysecret'
}));
io.use(passportSocketIo.authorize({ //configure socket.io
cookieParser: cookieParser,
secret: 'mysecret', // make sure it's the same than the one you gave to express
store: sessionStore,
success: onAuthorizeSuccess, // *optional* callback on success
fail: onAuthorizeFail, // *optional* callback on fail/error
}));
Connect-redis is a session store package that uses redis (in case the name isn't obvious).
Final step :
function onAuthorizeSuccess(data, accept){
console.log('successful connection to socket.io');
accept(); //Let the user through
}
function onAuthorizeFail(data, message, error, accept){
if(error) accept(new Error(message));
console.log('failed connection to socket.io:', message);
accept(null, false);
}
io.sockets.on('connection', function(socket) {
console.log(socket.request.user);
});
The user object found in socket.request will contain all the user info from the logged in user, you can pass it around, or do whatever you need with it from this point.
Note : This setup will be slightly different for Socket.IO < 1.x
i'm building a web application on NodeJS using express; the session store is a Redis instance which i talk to using connect-redis. the usual bits look like, well, usual:
RedisStore = ( require 'connect-redis' ) express
express_options =
...
'session':
'secret': 'xxxxxxxx'
'store': new RedisStore host: '127.0.0.1', port: 6379, ttl: 2 * weeks
'cookie': maxAge: 2 * weeks
app = express()
# Middleware
...
app.use express.cookieParser 'yyyyyy'
app.use express.session express_options[ 'session' ]
...
this does work well as such. however, i have not demonized Redis yet. after starting the server (but not Redis) and re-issuing an HTTP request from the browser, the application (apparently, naturally) failed to recognize yesterday's session cookie. to be more precise, the point of failure was
request.session.regenerate =>
request.session.user = uid_hint
in a login view, and the message was TypeError: Cannot call method 'regenerate' of undefined. now the question is:
(1) is my impression true that express won't balk at me when i try to use a session middleware that is configured to ask for data on a specific port, and yet that port is not served at all? if so, why is there no error message?
(2) what is a good way to test for that condition? i'd like a helpful message at that point.
(3) given that a DB instance may become unavailable at any one time—especially when it is separated by a network from the app server—what are best practices in such a case? fall back to memory-based sessions? refuse to serve clients?
(4) let us assume we fall back on another session storage mechanism. now all existing sessions have become invalid, right? unless we can decide whether a given signed SID coming in from a client is computationally valid in the absence of an existing record. those sessions will still be devoid of data, so it's not clear how useful that would be. we might as well throw away the old session and start a new one. but how? request.session = new ( require 'express' ).session.Session(), maybe?
Bonus Points (i'm aware some people will scoff at me for asking so many different things, but i think a discussion centered on sessions & cookies should include the below aspect)
thinking it over, i'm somewhat unhappy i'm using Redis at all—not because it's Redis, but because i have yet another DB make in the app. a theoretical alternative to using a session DB could be a reasonably secure way to keep all session data (NOT the user ID data, NO credit card numbers—just general stuff like which page did you come from etc) within the cookie. that way, any one server process can accept a request and has all the session data at hand to respond properly. i'm aware that cookie storage space is limited (like 4kB), but that might prove enough still. any middleware to recommend here? or is the idea dumb / insecure / too 1990?
connect-reddis listens to redis for the error event
./lib/connect-redis.js
self.client.on('error', function () { self.emit('disconnect'); });
So after creating the store, listen to the disconnect event
var store = new RedisStore({
host: 'localhost',
port: 6379,
db: 2,
pass: 'RedisPASS'
});
store.on('disconnect', function(){
console.log('disconnect');
});
i m creating application, using nodejs (0.8.15), express (>3.0) framework, socket.io and mongodb for register users.
1) Everyone knows that it is easy to create simple chat using only socket.io, without mongodb (or other). So, in this case where stores the messages? in session?
2) this second question is VERY IMPORTANT for me
i don't understand: why are MongoStore there for sessions? i read that it is "SessionStorage for connect's session middleware/Connect". As i know session destroys when user go away from site. So why do people store it and use require('connect-mongo') ??
var config = require('../config'),
express = require('express'),
MongoStore = require('connect-mongo'),
server = express.createServer();
server.configure(function() {
server.use(express.logger());
server.use(express.methodOverride());
server.use(express.static(config.staticPath));
server.use(express.bodyParser());
server.use(express.cookieParser());
server.use(express.session({
store: new MongoStore({
db: config.db
}),
secret: config.salt
}));
});
code i have just found. it seems it is for express 2.x
3) what is the difference between connect-mongo and connect-mongodb libs?
https://github.com/masylum/connect-mongodb
https://github.com/kcbanner/connect-mongo
4) do i have to make MongoStore for cookies and sockets?
thank you!
1) Nowhere? Server receives message, broadcasts it and forgets about it. Who sais anything about storing? Server can be just a proxy.
2) Because sessions don't have to be destroyed once a user leaves the site. For example consider a shop. You go to a shop, you put some things in your basket and then you close the browser. After 3 days you go back and you still see your items in the basket. This can only be done with sessions stored in DB ( at least in a reliable way ).
It really depends on your needs. In my case I hardly ever use DB based sessions. I use in-memory storages like Redis.
3) Who knows? Might be some subtle differences, I can't see any obvious.
4) No. As I said: it depends on your needs. If you want sessions to be active only when user is actually viewing the page, then you can stick with any in-memory storage. Or you might not need sessions at all, in which case you don't have to use it at all.
1) If you don't want to use a database for your chat, you should store the messages into a simple hash. But keep in mind, if you restart your node application, they will be lost.
2) MongoStore (or RedisStore) allows you to store the Express sessions into MongoDB/Redis instead of using the MemoryStore, which is not designed for a production environment.
(by the way the code you've found is for Express 2.x)
3) The two looks similar. Personnally I use connect-mongo.
4) No. Express handles the session cookies for you, and Socket.IO the sockets. If you want to work with MongoDB, you should try Mongoose http://mongoosejs.com/