diff --git a/README.md b/README.md index c619bf7..17ffda8 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,17 @@ var socketioJwt = require("socketio-jwt"); io.set('authorization', socketioJwt.authorize({ secret: 'your secret or public key' })); + +io.on('connection', function (socket) { + console.log('hello! ', socket.handshake.decoded_token.name); +}) ``` For more validation options see [auth0/jsonwebtoken](https://github.com/auth0/node-jsonwebtoken). __Client side__: -For now the only way to append the jwt token is using query string: +Append the jwt token using query string: ```javascript var socket = io.connect('http://localhost:9000', { @@ -31,7 +35,35 @@ var socket = io.connect('http://localhost:9000', { }); ``` -Take care as URLs has a lenght limitation on Internet Explorer. I opened a [issue in engine-io-client](https://github.com/LearnBoost/engine.io-client/issues/228) to support headers. +## Second method, without querystrings + +The previous approach send the token through querystring which could be logged by intermediary HTTP proxies. This second method doesn't but it requires an extra roundtrip. __Take care with this method to filter unauthenticated sockets when broadcasting.__ + +```javascript +// set authorization for socket.io +io.sockets.on('connection', socketioJwt.authorize({ + secret: 'your secret or public key', + timeout: 15000 // 15 seconds to send the authentication message +}, function(socket) { + //this socket is authenticated, we are good to handle more events from it. + console.log('hello! ' + socket.decoded_token.name); +})); +``` + +__Client side__: + +For now the only way to append the jwt token is using query string: + +```javascript +var socket = io.connect('http://localhost:9000'); +socket.on('connect', function (socket) { + socket + .on('authenticated', function () { + //do other things + }) + .emit('authenticate', {token: jwt}); //send the jwt +}); +``` ## Contribute diff --git a/lib/index.js b/lib/index.js index 715a999..f395c3c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,9 +1,30 @@ var xtend = require('xtend'); var jwt = require('jsonwebtoken'); var UnauthorizedError = require('./UnauthorizedError'); -var url = require('url'); -function authorize(options) { +function noQsMethod(options, onConnection) { + return function (socket) { + var auth_timeout = setTimeout(function () { + socket.disconnect('unauthorized'); + }, options.timeout || 5000); + + socket.on('authenticate', function (data) { + clearTimeout(auth_timeout); + jwt.verify(data.token, options.secret, options, function(err, decoded) { + if (err) { + return socket.disconnect('unauthorized'); + } + + socket.user = decoded; + socket.emit('authenticated'); + onConnection(socket); + }); + }); + + }; +} + +function authorize(options, onConnection) { var defaults = { success: function(data, accept){ accept(null, true); @@ -15,6 +36,10 @@ function authorize(options) { var auth = xtend(defaults, options); + if (onConnection) { + return noQsMethod(options, onConnection); + } + return function(data, accept){ var token, error; diff --git a/test/authorizer_noqs.test.js b/test/authorizer_noqs.test.js new file mode 100644 index 0000000..6ec7f33 --- /dev/null +++ b/test/authorizer_noqs.test.js @@ -0,0 +1,67 @@ +var fixture = require('./fixture'); +var request = require('request'); +var io = require('socket.io-client'); + +describe('authorizer without querystring', function () { + + //start and stop the server + before(function (done) { + fixture.start({ + noQS: true + } , done); + }); + + after(fixture.stop); + + describe('when the user is not logged in', function () { + + it('should close the connection after a timeout if no auth message is received', function (done){ + var socket = io.connect('http://localhost:9000'); + socket.on('disconnect', function () { + done(); + }); + }); + + it('should not respond echo', function (done){ + var socket = io.connect('http://localhost:9000', { + 'force new connection':true, + }); + + socket.on('echo-response', function () { + done(new Error('this should not happen')); + }).emit('echo', { hi: 123 }); + + setTimeout(done, 1200); + }); + + }); + + describe('when the user is logged in', function() { + + beforeEach(function (done) { + request.post({ + url: 'http://localhost:9000/login', + form: { username: 'jose', password: 'Pa123' }, + json: true + }, function (err, resp, body) { + this.token = body.token; + done(); + }.bind(this)); + }); + + it('should do the handshake and connect', function (done){ + var socket = io.connect('http://localhost:9000', { + 'force new connection':true, + }); + var token = this.token; + socket.on('connect', function(){ + console.log('connected'); + socket.on('echo-response', done) + .on('authenticated', function () { + socket.emit('echo'); + }).emit('authenticate', { token: token }); + }); + }); + }); + +}); \ No newline at end of file diff --git a/test/fixture/index.js b/test/fixture/index.js index 2dff026..a5ace0a 100644 --- a/test/fixture/index.js +++ b/test/fixture/index.js @@ -17,7 +17,10 @@ exports.start = function (options, callback) { options = {}; } - options = xtend({ secret: 'aaafoo super sercret'}, options); + options = xtend({ + secret: 'aaafoo super sercret', + timeout: 1000 + }, options); var app = express(); @@ -45,13 +48,23 @@ exports.start = function (options, callback) { var sio = socketIo.listen(server); sio.configure(function(){ - this.set('authorization', socketio_jwt.authorize(options)); + if (!options.noQS) { + this.set('authorization', socketio_jwt.authorize(options)); + } this.set('log level', 0); }); - sio.sockets.on('echo', function (m) { - sio.sockets.emit('echo-response', m); - }); + if (!options.noQS) { + sio.sockets.on('echo', function (m) { + sio.sockets.emit('echo-response', m); + }); + } else { + sio.sockets.on('connection', socketio_jwt.authorize(options, function (socket) { + socket.on('echo', function (m) { + socket.emit('echo-response', m); + }); + })); + } server.listen(9000, callback); }; diff --git a/test/mocha.opts b/test/mocha.opts index 35a00c1..d82740e 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,2 +1,3 @@ --require should ---reporter spec \ No newline at end of file +--reporter spec +--timeout 15000 \ No newline at end of file