I'm a Node-beginner and have the following problem. I use express, socket.io and passport as well as passport.socketio and a MongoStore.
I need to authenticate socket.io with passport (I use passport.socketio for this), but the handshake is always unauthorized even after successfull login. Here is my code so far:
var http = require('http')
var socketio = require('socket.io')
var express = require('express')
var stylus = require('stylus')
var passport = require('passport')
var passportSocketIo = require('passport.socketio')
var LocalStrategy = require('passport-local').Strategy
var mongo = require('mongoskin')
var MongoStore = require('connect-mongo')(express)
var DB = mongo.db('mongo://127.0.0.1/accounts', {safe:false})
DB.bind('accounts')
DB.bind('SessionStore')
SessionStore = new MongoStore({ db: 'SessionStore' })
var app = express()
app.configure(function () {
app.set('views', __dirname + '/views')
app.set('view engine', 'jade')
app.use(express.cookieParser())
app.use(express.bodyParser())
app.use(express.session({
secret: 'somekey',
store: SessionStore
}));
app.use(passport.initialize())
app.use(passport.session())
app.use(app.router)
})
Here is where I login:
app.post('/login', passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login_failed',
failureFlash: false }))
And here I try to access a page ('/content'), for which I need authorization:
app.get('/content', ensureAuthenticated, function(req, res){
res.render('content', { user: req.user })
})
var server = http.createServer(app).listen(5050)
var io = socketio.listen(server)
io.set('authorization', passportSocketIo.authorize({
cookieParser: express.cookieParser,
key: 'express.sid',
secret: 'somekey',
store: SessionStore,
fail: function(data, accept) {
accept(null, false);
},
success: function(data, accept) {
accept(null, true);
}
}))
But if a client now tries to access '/content', the server always responds 'handshake unauthorized':
// on client side
socket = io.connect('http://localhost:5050');
// on server side
io.sockets.on( 'connection', function( socket ) {
console.log(socket.handshake.user._id)
}
-> handshake unauthorized
Does anyone know what I do wrong here?
Yes that's an issue known since two years and not resolve yet :
https://github.com/LearnBoost/socket.io/issues/545
As koenigsbier said in this github issue :
In my client side I had var socket =
io.connect('http://localhost:8080'); and it's works only if you put
localhost:8080 in your browser.
I changed it by var socket = io.connect('http://127.0.0.1:8080');
and now it works with localhost:8080 and 127.0.0.1:8080 in your
browser.
It's clearly a bug, why socket.io should accept address mapping just
in one way? localhost is localhost and is the same as 127.0.0.1
On client side, try to put 127.0.0.1 instead of localhost
You need to use:
key: 'connect.sid',
instead of
key: 'express.sid',
for the key. Check out the cookie in your browser, you won't see express.sid, but connect.sid will have a value. The only other difference that I see between yours and my working example is I set a var to express.cookieParser and then use that both in app config and in io.set authorization.
var cookieParser = express.cookieParser();
Related
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!
I am facing an issue with handling session using latest version of Express, Node.js.
My requirement is to store users email id in /login route, and need to get that email id thought out
all routes like /home. But email id is not printing in /home route with my current code. Your help is much appreciate.
My demo code is here
var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');
var session = require('express-session');
var RedisStore = require('connect-redis')(session);
app.set('port',process.env.PORT || 3000);
app.use(cookieParser());
app.use(session({
resave: false,
saveUninitialized: false,
store: new RedisStore({
host: 'localhost',
port: 6379
}),
secret: 'some string/hash secret'
}));
var counter=0;
app.get('/login', function(request, response){
//adding some value to request.session
request.session.email = 'jak#amt.in';
console.log('sessionID', request.sessionID)
response.send('email: '+request.session.email);
});
app.get('/home', function (request, response) {
console.log('home login', request.session.email); // Email not priting here
console.log('sessionID - home', request.sessionID); // Session ID is showing
response.send('home');
});
if (!module.parent) {
console.info('Listening ', process.env.PORT || 5000);
app.listen(process.env.PORT || 5000);
}
module.exports = app;
I am getting following error
"ReplyError: ERR wrong number of arguments for 'set' command
at parseError (/home/dibeesh/obpnode6/testproject/node_modules/redis/node_modules/redis-parser/lib/parser.js:161:12)
at parseType (/home/dibeesh/obpnode6/testproject/node_modules/redis/node_modules/redis-parser/lib/parser.js:222:14)"
Make sure your redis server is running when you run your app.
I have this Node API that frontends a backend OAuth server. At the end of the SAML OAuth dance, I set the Bearer Token in a browser cookie.
// need cookieParser middleware before we can do anything with cookies
app.use(express.cookieParser());
// set a cookie
app.use(function (req, res, next) {
// check if client sent cookie
var cookie = req.cookies.cookieName;
if (cookie === undefined)
{
// no: set a new cookie
var randomNumber=Math.random().toString();
randomNumber=randomNumber.substring(2,randomNumber.length);
res.cookie('cookieName',randomNumber, { maxAge: 900000, httpOnly: true });
console.log('cookie created successfully');
}
else
{
// yes, cookie was already present
console.log('cookie exists', cookie);
}
next();
});
app.use(express.static(__dirname + '/public'));
Now I was introduced to a fancy NPM which does pretty much the same thing https://github.com/mozilla/node-client-sessions
While I was almost inclined on using this NPM, I bumped into express-session. https://github.com/expressjs/session - this is for server side sessions. But this also sets a cookie
var express = require('express');
var session = require("express-session");
var app = express();
app.use(session({
resave: true,
saveUninitialized: true,
secret: 'ABC123',
cookie: {
maxAge: 60000
}
}));
app.get("/test", function(req, res) {
req.session.user_agent = req.headers['user-agent'];
res.send("session set");
});
If my need to set only a bearer token in the browser cookie for subsequent API calls, which option should be my choice?
express-session is my go to.
If you look at what it took to accomplish the same thing with the two different methods, I think the answer is clear.
If all you want to do is set a client cookie that will enable the server to correctly authenticate future requests, express-session is awesome.
Here is an example set from another question I answered that uses MongoDB as a backend to store your sessions:
'use strict';
var express = require('express'),
session = require('express-session'),
cookieParser = require('cookie-parser'),
mongoStore = require('connect-mongo')(session),
mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/someDB');
var app = express();
var secret = 'shhh';
app.use(session({
resave: true,
saveUninitialized: true,
secret: secret,
store: new mongoStore({
mongooseConnection: mongoose.connection,
collection: 'sessions' // default
})
}));
// ROUTES, ETC.
var port = 3000;
app.listen(port, function() {
console.log('listening on port ' + port + '.')
});
I spent 2 days on accessing sessions (log user in) trough sockets.
I already have users in my mongodb, and i can indeed login/register them without sockets (trough post or get). All i need now is the same for sockets.
I have tried these SO solutions:
socket.io and express 4 sessions
How to share sessions with Socket.IO 1.x and Express 4.x?
socket.io and session?
And spend a lot of time googling. And trying different things.
I typically get undefined errors, or deprecated error, or nothing happens at all without errors.
After following the tutorials above, and doing some tweaks myself, my code went from "hard to read" to "too hard to read, must start from scratch". So i wont put up my code here.
Instead, can someone share their bare-minimum code on what it takes to access the sessions inside sockets? With explanations would would be highly appreciated.
UPDATE
I followed this: How to share sessions with Socket.IO 1.x and Express 4.x?
And i got a few issues. It still does not work. the session is empty. Am i doing something wrong? Full code:
var express = require("express");
var Server = require("http").Server;
var session = require("express-session");
var RedisStore = require("connect-redis")(session);
var SESSION;
var app = express();
var server = Server(app);
var sio = require("socket.io")(server);
var sessionMiddleware = session({
store: new RedisStore(),
secret: "keyboard cat",
resave: true,
saveUninitialized: true
});
sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
app.use(sessionMiddleware);
app.get("/*", function(req, res){
SESSION = req.session
req.session.name="THE NAME"; // <<<< SHOULDN'T THIS WORK? ITS UNDEFINED
if(req.path=="/"){
res.sendFile(__dirname+"/index.html");
}else{
res.sendFile(__dirname+req.path);
};
});
sio.sockets.on("connection", function(socket) {
SESSION = socket.request.session
console.log("The SESSION variable on socket connection:");
console.log(SESSION); //<<<<<<<<<<<< UNDEFINED
socket.on("get session status",function(){
socket.emit("session status",{ SESSION:SESSION }); // <<<<< EMPTY OBJECT
})
});
server.listen(80);
The accepted answer at the second link works fine for me:
app.js:
var session = require("express-session");
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var sessionMiddleware = session({
secret: "keyboard cat",
resave: true,
saveUninitialized: true
});
app.use(sessionMiddleware);
app.use(function(req, res) {
req.session.name = "THE NAME";
res.sendFile(__dirname + "/index.html");
});
io.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
io.on("connection", function(socket) {
console.log('socket.io connection');
console.dir(socket.request.session);
// above outputs:
// socket.io connection
// { cookie:
// { path: '/',
// _expires: null,
// originalMaxAge: null,
// httpOnly: true },
// name: 'THE NAME' }
});
server.listen(8000);
index.html:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost:8000');
</script>
Perhaps there is an issue with timing where the session data isn't saved to Redis yet when the socket.io connection is made? You might try delaying the socket.io connection at least until the page is fully rendered (window.onload).
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()}));