web-dev-qa-db-fra.com

Comment utiliser un passeport avec express et socket.io?

J'essaie actuellement de mettre en place un système d'authentification de base pour mon application node.js. Pour l'instant, j'utilise express (3.0.0rc5), passport (0.1.12) et socket.io (0.9.10) avec Mongoose as Store pour les données de session. J'ai aussi joué avec everyauth mais je n'aimais pas travailler avec des promesses.

Situation actuelle:

L'authentification par passeport (stratégie facebook) est réussie, un session.sid un cookie est installé sur le client après la redirection et je peux voir un document de session dans ma base de données. Je peux accéder au cookie de session dans socket.io à travers socket.handshake.headers.cookie.

Si j'ai bien compris le concept de passeport, après avoir réussi authentication passport.serializeUser est appelé, ce qui me permet d'ajouter des informations à la session. Dans mon cas, les informations les plus importantes sont l'e-mail, j'ai donc configuré le sérialiseur de la manière suivante:

passport.serializeUser(function(user, done) {
  done(null, user.email);
});

Maintenant, je suis au point où je dois utiliser uniquement les informations de cookie dans mon événement socket.io pour extraire l'adresse e-mail de la session.

var connect = require('connect'),
    parseSignedCookie = connect.utils.parseSignedCookie,
    cookie            = require('express/node_modules/cookie');

io.on('connection', function(socket) {
    if(socket.handshake.headers.cookie) {
        var cookie = cookie.parse(socket.handshake.headers.cookie);
        var sessionID = parseSignedCookie(cookie['connect.sid'], 'secret');
    }
});

passport.deserializeUser(function(id, done) {
    // so, what is supposed to be done here?
});

Donc, si je ne me trompe pas, la tâche consiste maintenant à utiliser deserializeUser pour accéder à l'adresse e-mail correspondante.

Comment ferais-je ça? Tous les pointeurs sont très appréciés.

43
Patrick

Voici une solution utilisant Socket.IO 1.0 et Express 4.0 . Elle est similaire dans son esprit à la réponse de Patrick. L'astuce est que, puisque Socket.IO 1.0 a une nouvelle API de middleware, nous pouvons envelopper le middleware d'Express et le placer dans le pipeline Socket.IO sans plonger dans l'implémentation de bas niveau des magasins de sessions.

// Set up the Session middleware using a MongoDB session store
expressSession = require("express-session");
var sessionMiddleware = expressSession({
    name: "COOKIE_NAME_HERE",
    secret: "COOKIE_SECRET_HERE",
    store: new (require("connect-mongo")(expressSession))({
        url: "mongodb://localhost/DATABASE_NAME_HERE"
    })
});

// Set up the Express server
var app = require("express")()
    .use(sessionMiddleware)
    .use(passport.initialize())
    .use(passport.session())
    // ... more middleware ...
    .listen(8000);

// Set up the Socket.IO server
var io = require("socket.io")(app)
    .use(function(socket, next){
        // Wrap the express middleware
        sessionMiddleware(socket.request, {}, next);
    })
    .on("connection", function(socket){
        var userId = socket.request.session.passport.user;
        console.log("Your User ID is", userId);
    });

La variable sessionMiddleware est une fonction conçue pour s'intégrer directement dans le pipeline Express. Il prend exactement trois arguments: l'objet de demande, l'objet de réponse et un rappel.

Le pipeline de Socket.IO s'attend cependant à ce que son middleware ne prenne que deux arguments: l'objet socket (qui contient l'objet request à socket.request) et un rappel. Heureusement sessionMiddleware ne nécessite pas que l'objet de réponse lise la session depuis le magasin, nous lui passons donc simplement un objet vide comme deuxième argument.

Notez que certains commentaires ci-dessous indiquent que ce code rend la session en lecture seule. C'est la fonctionnalité que nous perdons en n'ayant pas d'objet réponse approprié avec Socket.IO.

Dans l'exemple ci-dessus, j'utilise un magasin de sessions MongoDB (connect-mongo). Vous pouvez utiliser le magasin de sessions qui vous convient. Reportez-vous à le wiki Connect pour une liste des magasins de sessions.

64
sffc

Je l'ai fait fonctionner. Ce que je devais faire, c'est avoir accès à la sessionStore. Voici le code au cas où quelqu'un d'autre tomberait sur ce problème particulier:

// # app.js

var express     = require('express'),
    routes      = require('./routes'),
    http        = require('http'),
    path        = require('path'),
    app         = express(),
    passport    = require('passport'),
    SessionMongoose = require("session-mongoose"),
    mongooseSessionStore = new SessionMongoose({
        url: "mongodb://localhost/login",
        interval: 120000 
    });

var config       = require('game/config.js'), // holds my whole server configuration
    server       = require('game/lib/server.js');

// save sessionStore to config for later access
config.setSessionStore(mongooseSessionStore);

// configure express to use passport and mongooseSessionStore
app.configure(function(){
    app.set('port', config.port);
    app.set('env', config.environment);
    app.set('dbPrefix', config.dbPrefix);
    app.set('views', __dirname + '/views');
    app.set('view engine', 'jade');
    app.use(express.favicon());
    app.use(express.bodyParser());
    app.use(express.cookieParser());
    app.use(express.session({secret : 'totallysecret', store : mongooseSessionStore })),
    app.use(express.methodOverride());
    app.use(passport.initialize());
    app.use(passport.session());    
    app.use(app.router);
    app.use(express['static'](path.join(__dirname, 'public')));
});

app.get('/', routes.index);

app.get('/auth/facebook', passport.authenticate('facebook', { scope: 'email' }));
app.get('/auth/facebook/callback', 
    passport.authenticate('facebook', { successRedirect: '/',
                                        failureRedirect: '/' })); 

// #### Init httpserver
var httpServer = http.createServer(app);
httpServer.listen(app.get('port'));

// #### Server startup
server.init(httpServer);

Mes fonctions de sérialisation semblent simples comme ceci:

passport.serializeUser(function(user, done) {
    // saves user.email to session.passport.user
    done(null, user.email);
});

passport.deserializeUser(function(obj, done) {
    done(null, obj);
});

Et enfin l'implémentation socket.io:

var util              = require('util'),
    connect           = require('connect'),
    parseSignedCookie = connect.utils.parseSignedCookie,
    cookie            = require('express/node_modules/cookie'),
    io                = require('socket.io').listen(httpServer);

var config = require('../config.js');  

io.configure(function () {
    io.set('authorization', function (data, callback) {
        if(data.headers.cookie) {
            // save parsedSessionId to handshakeData
            data.cookie = cookie.parse(data.headers.cookie);
            data.sessionId = parseSignedCookie(data.cookie['connect.sid'], 'totallysecret');
        }
        callback(null, true);
    });

    io.on('connection', function(socket) {
        // reference to my initialized sessionStore in app.js
        var sessionStore = config.sessionStore;
        var sessionId    = socket.handshake.sessionId;

        sessionStore.get(sessionId, function(err, session) {
            if( ! err) {
                if(session.passport.user) {
                    console.log('This is the users email address %s', session.passport.user);
            }
        });
    });
});

En utilisant le module session-mangouste, j'ai accès à:

sessionStore.get(sessionId, callback)
sessionStore.set(sessionId, data, callback) 
sessionStore.destroy(sessionId, callback) 
sessionStore.all(callback)    // returns all available sessions
sessionStore.clear(callback)  // deletes all session data
sessionStore.length(callback) // returns number of sessions in the 
31
Patrick

Vous pouvez utiliser express-socket.io-session . Vous pouvez obtenir l'utilisateur de la session de passeport avec socket.handshake.session.passport.user. Il prend en charge la lecture et l'écriture dans le magasin de sessions.

NB: Fonctionne avec express> 4.0.0 et socket.io> 1.0.0 et ne sera pas rétrocompatible

0
nonybrighto