socket.io connect role authentication - node.js

I was thinking to implement a role/auth model on top of socket.io connections. There is a lot out there about this, especially for token authentication. But what about what I am attaching below? What would be wrong with this approach?
The idea with the code below is to give access to socket.io connections only to specific roles.
var role = true;
module.exports = function (io) {
if (role == true){
'use strict';
io.on('connection', function (socket) {
socket.on('chat message', function(msg){
io.emit('chat message', msg);
});
});
};
};

You can use Passport.socketio to authentify your socket connections. It works really well with express. Here is a sample app :
var express = require('express');
var passport = require('passport');
var passportSocketIo = require("passport.socketio");
var cookieParser = require('cookie-parser');
var session = require('express-session');
var app = express();
var RedisStore = require('connect-redis')(session);
var sessionStore = new RedisStore({
host: 'localhost',
port: 6379,
db: 2,
});
app.use(session({
store: sessionStore,
secret: 'YOUR_SECRET_HERE'
}));
app.use(passport.initialize());
app.use(passport.session());
var io = require('socket.io').listen(http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
}));
io.use(passportSocketIo.authorize({
cookieParser: cookieParser,
secret: 'YOUR_SECRET_HERE',
store: sessionStore,
}));
In your case, you'd have to add the roles to your passport users. The user info can then be accessed in socket.request.user
io.sockets.on('connection', function(socket) {
var user = socket.request.user;
if (user.role == true){
//do something
}
});
Please note that you have to use a sessionStore, local storage doesn't work with passport. Refer to the Git page above for different possibilities.

Related

Troubles with express and socket.io sessions

I seem to not be able to share sessions between express and socket io . It works when on localhost but not when on my server. There is something I am missing.
The data I store in socket.handshake.session ('age' here) is not saved on the express side.
My app.js goes like this:
var cookieParser = require('cookie-parser');
var session = require('express-session');
var app = express();
app.use(cookieParser()); // read cookies (needed for auth)
var sessionMiddleware = session({
secret: 'keyboard cat',
resave: true,
name: 'sessionId',
saveUninitialized: true,
cookie: { maxAge: 60000 },
// store: new redisStore({ host: 'localhost', port: 6379, client: redisClient, ttl: 86400 }),
});
app.use(sessionMiddleware);
app.set("sessionMW", sessionMiddleware);
module.exports = app;
My bin\www goes like this:
var app = require('../app');
var debug = require('debug')('testserver:server');
var http = require('http');
var sharedsession = require("express-socket.io-session");
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var session = app.get("sessionMW")
io.use(sharedsession(session, {autosave:true}));
server.listen(port);
I then have a serverEvent.js that goes like this:
var serverEvents = module.exports = function(io){
io.sockets.on('connection', function (socket) {
...
...
...
socket.on('updateAgeSession', function (message) {
socket.handshake.session.age = "18";
socket.handshake.session.save();
console.log(socket.handshake.session);
})
})
}
And finaly I have this index.js for my routes:
module.exports = function (app, passport) {
// show the home page (will also have our login links)
app.get('/', function (req, res, next) {
res.cookie('mycookies', 'express');
res.cookie('age', req.session.age);
cookie1= req.cookies;
console.log(req.session);
console.log(req.sessionID);
console.log(req.sessionStore.sessions);
if (req.session.views) {
req.session.views++;
console.log(req.session.views);
} else {
req.session.views = 1
console.log(req.session.views);
}
if(req.session.age){
console.log("OOOOOOOOOOOOOOOOOO");
} else {
console.log("XXXXXXXXXXXXXXXXXXXXXX");
}
res.render('homePage'); // ejs template
});
}
So my problem is that after I enter that Im over 18 when I refresh the page , the req.session.age will be undefined, but only when my code runs on server, it does work on localhost. I dont get it.
Please help
For anyone that might have got the same problem I had, here is the solution :
I also have a client side code that goes like this :
var socket = io.connect("http://ip:3000");
var globalVar = [];
// Sent on connection/searchValidatedFromClient by server
socket.on("loadHomePageFromServer", function(message) {
...}
Problem was io.connect("http://ip:3000");.
By changing it to io.connect("http://namesite.com"); or io.connect("http://ip:80");, it works!

socket.io passport session undefinded

I want to use
req.user.id
inside
io.sockets.on('connection', module.exports = function(socket){}
so I tried to using this line
socket.request.client.user
like that on my
io.socket
io.sockets.on('connection', module.exports = function(socket,client,req){
connections.push(socket);
app.use(session({ secret: 'keyboard cat',resave: true, saveUninitialized:true})); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
console.log('connected: %s socket connected -session',socket.request.client.user ,connections.length);
but i get undefined on console
that's are my full code full code
var authController = require('./app/controllers/authcontroller.js');
var auth = require('./app/routes/auth.js');
var express = require('express')
var app = express()
var passport = require('passport')
var session = require('express-session')
var bodyParser = require('body-parser')
var env = require('dotenv').load()
var exphbs = require('express-handlebars')
app.use(session({secret: 'ssshhhhh'}));
app.use(passport.initialize())
app.use(passport.session())
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
database: 'db_users',
user: 'root',
password: '',
});
users = [];
connections = [];
app.use('/cssFiles', express.static('/opt/lampp/htdocs/x/regtest/using-passport-with-sequelize-and-mysql-master/app/routes/assets'));
//For BodyParser
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// For Passport
app.use(session({ secret: 'keyboard cat',resave: true, saveUninitialized:true})); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
//passport.authenticate('local-signin');
//For Handlebars
app.set('views', './app/views')
app.engine('hbs', exphbs({extname: '.hbs'}));
app.set('view engine', '.hbs');
app.get('/', function(req, res){
console.log('idddddd',req.sessionID);
res.send('Welcome to Passport with Sequelize');
});
//Models
var models = require("./app/models");
//Routes
var authRoute = require('./app/routes/auth.js')(app,passport);
//load passport strategies
require('./app/config/passport/passport.js')(passport,models.user);
//Sync Database
models.sequelize.sync().then(function(){
console.log('Nice! Database looks fine')
}).catch(function(err){
console.log(err,"Something went wrong with the Database Update!")
});
server.listen(process.env.PORT || 5000, function(err){
if(!err)
console.log("Site is live"); else console.log(err)
});
console.log('Server running...');
app.get('/', function(req, res){
res.sendFile('/opt/lampp/htdocs/x/regtest/using-passport-with-sequelize-and-mysql-master/app/routes/page.html');
});
//connection.connect();
io.sockets.on('connection', module.exports = function(socket,client,req){
//con
connections.push(socket);
//app.use(session);
app.use(session({ secret: 'keyboard cat',resave: true, saveUninitialized:true})); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
console.log('connected: %s socket connected session',socket.request.client.user ,connections.length);
req.user is only available in express middleware unfortunately. You'll have to use something like https://www.npmjs.com/package/passport.socketio if you want to access passports user info.
You will have to use some sort of persistent store as well such as redis or mongodb

I cannot send chat messages with socket.io

I could send messages before, but I shared passport's sessions with express and socket.io using redis by following this tutorial http://www.scotthasbrouck.com/blog/2016/3/18/passportjs-express-session-with-sockeio and now I can no longer send any chat messages.
Also, in my routes req.isAuthenticated() always returns false but I didn't have this problem before.
This is my server.js.
// server.js
// set up ======================================================================
// get all the tools we need
var express = require('express');
var app = express();
var server = require('http').Server(app);
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var configDB = require('./config/database.js');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var passportSocketIo = require('passport.socketio');
var session = require('express-session');
var chatdb = require('./app/models/chat.js');
var user = require('./app/models/user.js');
var redis = require('redis');
var client = redis.createClient();
var RedisStore = require('connect-redis')(session);
// configuration ===============================================================
mongoose.connect(configDB.url); // connect to our database
require('./config/passport')(passport); // pass passport for configuration
// set up our express application
app.use(morgan('dev')); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser()); // get information from html forms
app.set('view engine', 'ejs'); // set up ejs for templating
var sessionStore = new RedisStore({ host: 'localhost', port: 6379, client: client, ttl: 260 });
// required for passport
app.use(session({
secret: 'secret',
store: sessionStore,
resave: true,
saveUninitialized: true,
cookie: {
secure: process.env.ENVIRONMENT !== 'development' && process.env.ENVIRONMENT !== 'test',
maxAge: 2419200000
},
}));
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
app.use(express.static(__dirname + '/views'));
app.use(express.static(__dirname + '/public'));
io.use(passportSocketIo.authorize({
key: 'connect.sid',
secret: 'secret',
store: sessionStore,
passport: passport,
cookieParser: cookieParser,
}));
var eventSocket = io.of('/chat');
// socket.io
io.on('connection', function (socket) {
console.log('a user connected');
socket.on('chat message', function(msg){
var name = "efe";
chatdb.saveMsg({name: name, msg: msg}, function(err){
if(err) throw err;
io.emit('chat message', msg);
});
if (socket.request.user && socket.request.user.logged_in) {
console.log(socket.request.user);
}
});
});
// routes ======================================================================
require('./app/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport
// launch ======================================================================
http.listen(port);
console.log('The magic happens on port ' + port);
I had the same problem. I tried to implement passport.socketio like you but I solved my problem without it.
Here is my middleware :
// Express session middleware
var sessionMiddleware = session({ secret: 'secret-key', resave: true, saveUninitialized: true });
// Send Express sessionMiddleware through socket.io
io.use(function(socket, next){
// Params: request object, response object and callback.
sessionMiddleware(socket.request, {}, next);
});
The second parameter (response object) of sessionMiddleware function is empty. This will renders the session read-only.
And Here is how I use passport js session variable to get user informations :
io.on("connection", function(socket){
socket.on('chat message', function(msg){
var passport = socket.request.session.passport; // Get the passport variable
// Check if passport session exists
if (passport && typeof passport !== 'undefined'){
var userSession = passport.user; // Get the user session
// Build JSON with user session data
var message = {
message : msg,
name : userSession.name,
avatar : userSession.avatar
};
io.emit('chat message', message); // Emit my socket
}
});
});
Hope it help.
This was how i was able to solve it.
var express = require('express');
var app = express();
var server = require('http').Server(app);
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
// Add connect-mongo to project - npm install connect-mongo
var MongoStore = require('connect-mongo')(session);
var configDB = require('./config/database.js');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var chatdb = require('./app/models/chat.js');
// configuration ===============================================================
// NOTE: This might need to be put into a callback/promise inside an initialize function
var db = mongoose.connect(configDB.url); // connect to our database
require('./config/passport')(passport); // pass passport for configuration
// set up our express application
app.use(morgan('dev')); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser()); // get information from html forms
app.set('view engine', 'ejs'); // set up ejs for templating
var mongoStore = new MongoStore({
mongooseConnection: db.connection,
});
app.use(session({
secret: 'secret',
clear_interval: 900,
cookie: { maxAge: 2 * 60 * 60 * 1000 },
store: mongoStore,
}));
// required for passport
app.use(session({ secret: 'secret' })); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
app.use(express.static(__dirname + '/views'));
app.use(express.static(__dirname + '/public'));
// Intercept Socket.io's handshake request
io.use(function(socket, next) {
// Use the 'cookie-parser' module to parse the request cookies
cookieParser('secret')(socket.request, {}, function(err) {
// Get the session id from the request cookies
var sessionId = socket.request.signedCookies ? socket.request.signedCookies['connect.sid'] : undefined;
if (!sessionId) return next(new Error('sessionId was not found in socket.request'), false);
// Use the mongoStorage instance to get the Express session information
mongoStore.get(sessionId, function(err, session) {
if (err) return next(err, false);
if (!session) return next(new Error('session was not found for ' + sessionId), false);
// Set the Socket.io session information
socket.request.session = session;
// Use Passport to populate the user details
passport.initialize()(socket.request, {}, function() {
passport.session()(socket.request, {}, function() {
// This will prohibit non-authenticated users from connecting to your
// SocketIO server.
if (socket.request.user) {
next(null, true);
} else {
next(new Error('User is not authenticated'), false);
}
});
});
});
});
});
// socket.io
io.on('connection', function(socket) {
socket.on('chat message', function(msg) {
console.log(socket.request.user.facebook.name || socket.request.user.local.email);
var name = socket.request.user.facebook.name || socket.request.user.local.email;
chatdb.saveMsg({
name: name,
msg: msg
}, function(err) {
if (err) throw err;
io.emit('chat message', msg);
});
});
socket.on('disconnect', function() {
console.log('user disconnected');
});
});
// routes ======================================================================
require('./app/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport
// launch ======================================================================
http.listen(port);
console.log('The magic happens on port ' + port);

Update express session through socket.io

I have a Node.js server using express framework with a session system. The program is a simple 1vs1 game. In order to play, a user can create a room and an other user can join it so the game can start. The room ID is stored into the user's session.
If you take a look at the following code, you'll see how I manage to access express sessions in a "socket code". This is great, it works. However, I cannot modify values in the session object.
var express = require('express');
var session = require('express-session');
var sessionMiddleware = session({
secret: 'my secret',
resave: false,
saveUninitialized: false
});
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);
app.use(sessionMiddleware);
io.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
io.on('connection', function(socket) {
// some stuff
socket.on('disconnect', function() {
socket.request.session.room = undefined; // this is not working
});
});
server.listen(8080);
Is there a way to do what I want or should I rethink the way rooms are managed ?
Thank you

Using RedisStore in nodejs / expressjs

I'm trying get access to session data in express so I thought I would try declaring a connect-redis session store when configuring express. However, I cannot see why this doesn't work:
var express = require('express');
var http = require('http');
var RedisStore = require('connect-redis')(express);
var app = express();
app.set('port', process.env.PORT || 3000);
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));
//app.use(express.session({ secret: "keyboard cat" }));
app.use(app.router);
app.get('/', function(req, res){
console.log('/');
req.session.items = [ 'apple', 'orange' ];
res.end('items configured');
});
app.get('/items', function(req, res){
console.log('/items: ', req.session.items);
var s = JSON.stringify(req.session.items);
res.end('items: ' + s);
});
var server = http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
The '/' route simply configures items with the session.
The '/items' route displays the list of items in the session.
It works using the standard expressjs session store.
It doesn't work using connect-redis (req.session is undefined)
I'm assuming the redis store will be instantiated and destroyed as the app loads/unloads (or do I need it running outside of node/express app?)
Any ideas?
req.session will be undefined if RedisStore can't connect to your Redis server. So it's either not running, or it's not running on the default location that RedisStore is looking for it (127.0.0.1:6379).
In case of the latter, you can configure the location using the options argument to the RedisStore constructor.
Give this a try.
var express = require('express');
var redis = require("redis");
var session = require('express-session');
var redisStore = require('connect-redis')(session);
var bodyParser = require('body-parser');
var client = redis.createClient();
var app = express();
app.set('views', __dirname + '/views');
app.engine('html', require('ejs').renderFile);
app.use(session({
secret: 'ssshhhhh',
// create new redis store.
store: new redisStore({ host: 'localhost', port: 6379, client: client,ttl : 260}),
saveUninitialized: false,
resave: false
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.get('/',function(req,res){
// create new session object.
if(req.session.key) {
// if email key is sent redirect.
res.redirect('/admin');
} else {
// else go to home page.
res.render('index.html');
}
});
app.post('/login',function(req,res){
// when user login set the key to redis.
req.session.key=req.body.email;
res.end('done');
});
app.get('/logout',function(req,res){
req.session.destroy(function(err){
if(err){
console.log(err);
} else {
res.redirect('/');
}
});
});
app.listen(3000,function(){
console.log("App Started on PORT 3000");
});
link : https://codeforgeek.com/2015/07/using-redis-to-handle-session-in-node-js/
You should invoke RedisStore constructor (with ())
app.use(express.session({ secret: "keyboard cat", store: new RedisStore()}));

Resources