Skip to content

Instantly share code, notes, and snippets.

@sorensen
Created February 12, 2013 15:25
Show Gist options
  • Save sorensen/4770629 to your computer and use it in GitHub Desktop.
Save sorensen/4770629 to your computer and use it in GitHub Desktop.
Socket.io / Express sessions with keep alive and validation
/*!
* Dependencies
*/
var http = require('http')
, express = require('express')
, connect = require('connect')
, store = new connect.middleware.session.MemoryStore()
, app = express()
, server = http.createServer(app)
, secret = 'nyan-cat'
, key = 'connect.sid'
, cookieParser = express.cookieParser('nyan-cat')
/**
* Configure express server to use sessions and cookie parsing with
* the same config values we will use for socket-sessions
*/
app.use(express.session({
secret: secret
, store: store
, key: key
}))
app.use(cookieParser)
/**
* Global io connections, get the session if possible, always allow
* the connection even if the session cannot be found (ie: missing cookie)
*/
socketSession({
required: false
, io: io
, store: store
, cookieParser: cookieParser
, key: key
})
/**
* Admin namespace, require admin session to connect, the `validate` method
* will be used in place of the socket-session internals for more fine grained
* control on authorizing a connection
*
* io.of('/admin')
*/
socketSession({
io: io
, store: store
, namespace: '/admin'
, cookieParser: cookieParser
, key: key
, validate: function(err, data, accept) {
if (data.session.isAdmin) {
return accept(null, true)
}
accept(null, false)
}
})
'use strict';
/**
* See https://github.com/LearnBoost/socket.io/wiki/Authorizing
*/
/**
* Cookie parsing helper
*
* @param {Object} socket handshake object
* @param {String} cookie key name used with server
*/
function findCookie(handshake, key) {
return (handshake.secureCookies && handshake.secureCookies[key])
|| (handshake.signedCookies && handshake.signedCookies[key])
|| (handshake.cookies && handshake.cookies[key]);
}
/**
* Socket session helper
*
* @param {Object} options hash
*/
module.exports = function(options) {
var key = options.key || 'connect.sid'
, store = options.store
, io = options.io
, ns = options.namespace
, parser = options.cookieParser
, required = options.required === undefined ? true : options.required
, keepAlive = options.keepAlive === undefined ? false : options.keepAlive
, interval = options.interval || 120000
, validate = options.validate
function callback(data, accept) {
parser(data, {}, function(err) {
data.sessionID = findCookie(data, key)
if (err || !data.sessionID) {
if (validate) {
validate(err, data, accept)
}
return required
? accept(err || 'No cookie found.', false)
: accept(null, true)
}
store.load(data.sessionID, function(err, session) {
if (err || !session) {
if (validate) {
return validate(err, data, accept)
}
return required
? accept(err || 'No session.', false)
: accept(null, true)
}
data.session = session
console.log('Socket connected SID: ', data.sessionID)
return validate
? validate(err, data, accept)
: accept(null, true)
});
});
}
io.sockets.on('connection', function(socket) {
socket.getSession = function(next) {
var sess = socket.handshake.session
if (!next) return
if (!sess) return next(new Error('No session'))
sess.reload(function(err) {
next(err, hs.session)
})
}
})
// Keep session alive while the client is still active
if (keepAlive) {
io.sockets.on('connection', function(socket) {
var hs = socket.handshake
, iid
if (!hs.sessionID || !hs.session) {
return
}
iid = setInterval(function() {
hs.session.reload(function() {
console.log('Socket KeepAlive: SID ' + hs.sessionID)
hs.session.touch().save()
})
}, interval)
socket.on('disconnect', function() {
console.log('Socket disconnect: SID ' + hs.sessionID)
clearInterval(iid)
})
})
}
// Check for namespaced configuration
if (ns) {
io.of(ns).authorization(callback)
} else {
io.set('authorization', callback)
}
return callback
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment