Passport.socketio has issues finding session

2019-07-16 12:20发布

问题:

I am trying to access the session from sockets, but can't seem to make a connection. Without fail, authorization fails and I get the fail callback with the following message:

failed connection to socket.io: No session found

I will place all my code here so that it might be easier to spot what I'm doing wrong.

var express  = require('express');
var app      = express();
var http     = require('http');
var socketio = require('socket.io')
var passportSocketIo = require('passport.socketio');
var port     = process.env.PORT || 3000;
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');
var MongoStore   = require('connect-mongo')(session);

var server   = http.createServer(app);
var io       = socketio.listen(server);

var dbConfig = require('./config/database.js');
mongoose.connect(dbConfig.url);
var sessionStore = new MongoStore({ db: mongoose.connection.db });

require('./config/passport')(passport);

app.use(morgan('dev'));
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.set('view engine', 'ejs');

app.use(session({
    key: 'connect.sid',
    secret: 'secret',
    store: sessionStore,
    resave: true,
    saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use(express.static(__dirname + '/public'));

require('./app/routes.js')(app, passport);

server.listen(3000, function() {
    console.log('App running on port: ' + port);
});

io.use(passportSocketIo.authorize({
    passport:     passport,
    cookieParser: cookieParser,
    key:          'connect.sid',
    secret:       'secret',
    store:        sessionStore,
    success:      onAuthorizeSuccess,
    fail:         onAuthorizeFail
}));

function onAuthorizeSuccess(data, accept){
  console.log('successful connection to socket.io');
  accept(null, true);
}

function onAuthorizeFail(data, message, error, accept){
  if(error)
    throw new Error(message);
  console.log('failed connection to socket.io:', message);
  accept(null, false);
}

io.sockets.on('connection', function(socket) {
    var date = new Date();
    var time = date.getHours() + ":" + date.getMinutes();
    socket.emit('message', {username: 'Server', message: 'welcome to the chat'});
    socket.on('send', function(data) {
        io.sockets.emit('message', data);
    });
});

How should I be establishing a connection to the session with socket.io?

Also, I have seen that the user data is accessed via socket.handshake.user. Would that be correct in this case?

And for clarity, the versions are as follows:

express: 4.8.5
passport: 0.2.0
socket.io: 1.0.6
passport.socketio: 3.2.0

EDIT

It appears that part of the issue was the localhost versus 127.0.0.1 bug that already exists. However, now I don't get any handshake data back.

回答1:

First off, as I had noted in my question edit, be sure to type in the IP directly, rather than using localhost: 127.0.0.1. This is a known bug that will not allow you to send the cookie over correctly when pointing to localhost.

The passport-socketio Github page was, at last, updated with the following for the authorization callbacks for socket.io 1.x.

function onAuthorizeSuccess(data, accept){
  console.log('successful connection to socket.io');
  accept();
}

function onAuthorizeFail(data, message, error, accept){
  console.log('failed connection to socket.io:', data, message);
  if(error)
    accept(new Error(message));
}

Also, the handshake data is no longer stored in the same place. Replace socket.handshake.user with socket.request.user.



回答2:

I had the exact same problem. For me the solution was not in the web server but due to calling localhost in the browser. Changing to 127.0.0.1 or to my acctual IP made life much happier.

See also answer and comments to: Javascript document.cookie always empty string



回答3:

passport.socketio doesn't work with the new version of socket.io.

You should be able to remove passport.socketio and replace your current io.use with this one :

var cookieParser = require('cookie-parser')('secret'); // <- your secret here
io.use(function(socket, next){
    cookieParser(socket.handshake, {}, function(err){
        if (err) {
            console.log("error in parsing cookie");
            return next(err);
        }
        if (!socket.handshake.signedCookies) {
            console.log("no secureCookies|signedCookies found");
            return next(new Error("no secureCookies found"));
        }
        sessionStore.get(socket.handshake.signedCookies["connect.sid"], function(err, session){
            socket.session = session;
            if (!err && !session) err = new Error('session not found');
            if (err) {
                 console.log('failed connection to socket.io:', err);
            } else {
                 console.log('successful connection to socket.io');
            }
            next(err);
        });
    });
});


回答4:

The passport.socketio package works with previous version of socket.io. At least this code works with socket.io v0.9.17 - https://github.com/vodolaz095/hunt/blob/v_0_2_x/lib/http/socket.io.js



回答5:

Ther is my full code: express, mongoDB session store, socket.io, passport-local, passport.socketio:

package.json

{
  "name": "application-name",
  "version": "0.0.1",
  "devDependencies": {
    "body-parser": "^1.15.0",
    "connect-ensure-login": "^0.1.1",
    "connect-mongo": "^1.1.0",
    "cookie-parser": "^1.4.1",
    "express": "^4.13.4",
    "express-session": "^1.13.0",
    "mongoose": "^4.4.5",
    "passport": "^0.3.2",
    "passport-local": "^1.0.0",
    "passport.socketio": "^3.6.1",
    "socket.io": "^1.4.5"
  }
}

server.js

"use strict";

const COOKIE_SECRET = 'keyboard cat!?!';
const MONGO_CFG = { url: 'mongodb://localhost:27017/db', autoReconnect: true };

var path = require("path");
var Server = require("http").Server;
var express = require('express');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require("express-session");
var MongoStore = require("connect-mongo/es5")(session);

// ToDo: make control it by DB
var user = {id:1, username:'1!', perm: '1,2,3'};

var passport = require('passport');
var Strategy = require('passport-local').Strategy;
passport.use(new Strategy( function(username, password, cb) { return cb(null, user); }));
passport.serializeUser(function(user, cb) { cb(null, user.id); });
passport.deserializeUser(function(id, cb) { cb(null, user); });

var sessionStore = new MongoStore(MONGO_CFG);
var sessionMiddleware = session({
    resave: false, // true - всегда записывать сессию в хранилище, даже если ничего не изменилось
    unset: 'destroy', // Удалять сессию из БД при логауте
    saveUninitialized: false, // Не сохранять в стор новую и пока не изменившуюся запись
    store: sessionStore,
    secret: COOKIE_SECRET
});

var app = express();
var server = Server(app);

app.use(cookieParser(COOKIE_SECRET));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(sessionMiddleware);

app.use(passport.initialize());
app.use(passport.session());

app.get('/',
    function(req, res) {
        var html;
        if (req.user) {
            html = `<p>Hello, ${req.user.username}. View your <a href="/profile">profile</a>.</p>`;
        } else {
            html = '<p>Welcome! Please <a href="/login">log in</a>.</p>'
        }
        res.send(html + '<script src="/socket.io/socket.io.js"></script><script>var socket = io();</script>');
    });

app.get('/login',
    function(req, res){
        res.send(`
            <form action="/login" method="post">
                <div><label>Username:</label><input type="text" name="username"/><br/></div>
                <div><label>Password:</label><input type="password" name="password"/></div>
                <div><input type="submit" value="Submit"/></div>
            </form>`
        );
    });

app.post('/login',
    passport.authenticate('local', { failureRedirect: '/login' }),
    function(req, res) {
        res.redirect('/');
    });

app.get('/logout',
    function(req, res){
        req.logout();
        res.redirect('/');
    });

app.get('/profile',
    require('connect-ensure-login').ensureLoggedIn(),
    function(req, res){
        res.send(`<p>ID: ${req.user.id}<br/>Username: ${req.user.username}</p><a href="/logout">Log out</a>`);
    });

server.listen(3000);

var io = require("socket.io")(server);
var passportSocketIo = require("passport.socketio");

io.use(passportSocketIo.authorize({
    cookieParser: cookieParser,
    secret:       COOKIE_SECRET,
    store:        sessionStore,
    success:      onAuthorizeSuccess,
    fail:         onAuthorizeFail
}));

function onAuthorizeSuccess(data, accept){
    console.log('successful connection to socket.io');
    accept();
}

function onAuthorizeFail(data, message, error, accept){
    if(error)
        throw new Error(message);
    console.log('failed connection to socket.io:', message);
    return accept(new Error(message));
}