Authentication with Node/Express/Socket.IO - node.js

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

Related

Creating and maintaining a Redis session store replica in a nodejs expressjs app

Pretty much, I'm wondering how to create, and maintain a replica of a Redis session store in a Nodejs app with a microservices architecture.
Short background (somewhat)
I'm planning the architecture of a project I'm gonna start working on, and have decided to use Redis store for storing user sessions. I'm trying out a microservices architecture, and essentially, there's gonna be an authentication service that writes, and reads the to the session store as needed. What instantiating the store looks like usually:
const express = require('express');
const session = require('express-session');
const redis = require('redis');
const redisStore = require('connect-redis')(session);
const redisClient = redis.createClient();
const { SESSION_OPTIONS } = require('./configs/session');
const app = express();
app.use(session({
...SESSION_OPTIONS,
store: new redisStore({ host: 'localhost', port: 6379, client: redisClient })
}))
However, throughout my app, almost all user actions will have to be authorised, which involves checking user credentials stored in the session. Usually, in my monolithic projects, it looks something like:
router.get('/someUserAction', ensureAuthorisation, (req, res) => {
...
})
where ensureAuthorisation is a function like:
ensureAuthorisation: (req, res, next) => {
if(req.user.isAuthorised) {
return next();
}
return res.status(401).json({success: false, msg: 'fail msg'});
}
Because almost every user action will require authorisation, hence reads to the Redis sessions store, I'm assuming this wouldn't be good for the authentication service, that uses the same store but writes and reads less frequently. Thus, I want to create a separate service, the authorisation service, which uses its own Redis store. This store for the authorisation service should be a copy of the authentication service's store, removing, and updating sessions accordingly.
My initial thought was to have some form of asynchronous communication between both stores, with the authentication store writing new sessions to the authorisations... Question is, is this even possible, or would the copy store be more of a cache?
This was a bit long, and maybe confusing, so please ask any questions if you need further explanation (be kind to me pls lol). ANY HELP IS APPRECIATED 🙏
You can go ahead using a single Redis only rather than creating a cache for other service.
use redis in sentinel mode, authorization service wirtes on the master and other read from the replicas.

How to separate nodejs sessions

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.

integration socket.io - express 4 ,same port and security session

I'm working with mean.js and I need to have some real time features in my app, to accomplish that I'm going to use socket.io library.
Here is my idea on how to integrate and still have a good structure in the app.
Mean is using a server.js file, that is the one that do a lot of configurations, so I want to do the following:
// Expose app
exports = module.exports = app;
// Add my reference to the socketServer
var io = require('/socketServer')(app);
The file '/socketServer.js' is going to be my starting point and my configuration point of my socket, could looks something like this:
var http = require('http');
var socketio = require('socket.io');
module.exports = function(express){
var server = http.Server(express);
var io = socketio(server);
io.path('/');
io.on('connect', function(socket){
socket.emit('connected', {msg: 'You are connected now.'});
socket.on('upvote', function(data){
socket.emit('upvoteR', 'newConnected');
socket.broadcast.emit('upvoteR', 'newCOnnected');
});
});
server.listen(8080);
return io;
};
I feel like could be useful for me separate the server default config, of my socket config, and use it file (socketServer.js) as my starting point to develop all my sockets logics injecting the dependencies I want.
I don't know if is out there a better approach to this problem, or some structure best practices that I should follow or inconveniences of doing this.
So besides this structure, this are other doubts:
How to use sockets and express server in the same port?
Seems like, with express 4 I'm not able to link the express server with socket, because express 4 server does not inherit any more of httpServer of node.js, so now I have to do a server.listen(socketPort) and if I use the same app.port of mean.js this just is an EADDRINUSE error. Is still possible to have it working in the same port ?
How to use express session to authenticate each socket connection? if not possible, what's the better approach ? An example or a document reference would be nice for me.
thanks in advance.
I would like to share my solution just in case someone in the future has the same requirement that I had.
How to authenticate each socket connection base on express session information.
First I configure express to use passport.js library the following way:
// CookieParser should be above session
var cp =cookieParser;
app.use(cp());
// Express MongoDB session storage
var mStore = new mongoStore({
db: db.connection.db,
collection: config.sessionCollection
});
app.use(session({
secret: config.sessionSecret,
store: mStore
}));
// use passport session
app.use(passport.initialize());
app.use(passport.session());
So far is the normal implementation of passport over express. be sides this configuration I added passport-socket.io.js to my project. This is my working configuration:
var server = http.Server(app);
var io = IO(server);
io.use(
function(socket,next){
passportSocketIo.authorize({
cookieParser: cp,
key: 'connect.sid', // the name of the express cookie
secret: config.sessionSecret, // the session_secret to parse the cookie
store: mStore, // mongo session storage
success: onAuthorizeSuccess, // *optional* callback on success
fail: onAuthorizeFail, // *optional* callback on fail/error
})(socket, next);
}
);
app.io=io;
server.listen(config.port);
Where "onAuthorizeSuccess" and "onAuthorizeFail" are functions to allow the conections and develop the sockets logics.. well,with this my socket.io connection is authenticated with my passport session information and if the user is not logged the socket would not connect..
And if we need some authorization logic based on user roles, the passport.socketio creates a socket.request.user where you can find yours users roles to use in your roles sockets logics..

NodeJS / express / connect-redis: no error on redis being down; session missing

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');
});

In node.js, how do I setup redis with socket.io and express? Specifically using RedisStore()

First Problem
I'm trying to figure out sessions, stores, authorization, and redis. If it's important, I am using Express#3.0.0rc4. I understand that I have two options to put my RedisStore(). Which one do I use? Do I use both?
express.session({secret: 'secret', key: 'key', store: new RedisStore()});
io.set('store', new RedisStore());
I already have node.js, express, and socket.io running. So now I'm trying to implement redis but I don't know how to implement authorization using sessions/stores and I don't know how to write to the redis database. I haven't found any documentation on this. I found a site that talks about sessions and stores using socket.io and express but no redis, and another one that talks about sessions and stores using all three, but it doesn't use io.set('store', ...).
I also don't know if I should use two different stores, one for express and one for socket.io, or if I should just use one. Look at the example for clarification:
//Redis Variables
var redis = require('socket.io/node_modules/redis');
var RedisStore = require('socket.io/lib/stores/redis');
var pub = redis.createClient();
var sub = redis.createClient();
var client = redis.createClient();
var redis_store = new RedisStore({
redisPub: pub,
redisSub: sub,
redisClient: client
});
app.configure(function(){
//...code goes here...
app.use(express.session({
secret: 'secret',
key: 'key',
store: redis_store //Notice I'm using redis_store
}));
//...more code...
});
io.configure(function(){
io.set('store', redis_store); //Notice it's the same RedisStore() being used
});
Do I use the same RedisStore() for each? Do I create seperate ones for each? Do I just use express or socket.io? What I really want is to be able to authenticate clients (I assume that's done through sessions) and have them update the redis database when they connect - keeping a log of when people accessed my site. Which leads to my second problem.
Second Problem
So I have no idea how to access and edit the redis database from this point. I haven't been able to test this because of my first problem but I assume it would be something like this:
io.sockets.on('connection', function(socket){
var session = socket.handshake.session;
redis.put(session);
});
I also haven't seen any documentation on how to update a redis database from within node.js so I highly doubt that redis.put() is the correct terminology haha. I have visited redis's website but I can't find commands for node.js. Just commands for using regular redis from the command line. Anyways, if someone could at least point me in the right direction that would be great. Thanks. :)
Express and Socket.IO have their own integration with Redis for session management, as you've seen. It is designed as a blackbox integration, the idea being that the session store implementation is independent from the rest of your code. Since it's independent, that means you can't go in and use express or socket.io to access Redis directly. You'll need to add a regular redis client like node_redis. The benefit is you don't have to worry about making all those redis calls yourself, instead you'll be interacting with express or socket.io's session store interfaces.
So in your #1 case, you could pass in a single new instance of RedisStore, not two new ones as you've done. Or you could follow your second link and have socket.io listen through express. In that case it would integrate with express session management. That's why you don't see the extra io.set('store') call in that example.
It'll probably seem redundant to you, but try to think of RedisStore as a special client designed only for session management. Even thought RedisStore probably relies on something like node_redis, you shouldn't try to access it. You have to include another library for accessing your redis database directly, assuming you wanted to store other non-session items in redis in the first place.

Resources