I've recently picked up NodeJS again after an extensive break of a couple of years. Last time around I never really understood the proper way of including modules (dependencies) and now that I'm trying to integrate authentication I'm really stuck. That is to say, the code below is working fine, but I can't shake the feeling that I'm making it a lot harder then it should be.
This question might be borderline off-topic, but I'm looking for an answer containing guidelines and/or a clear example of best practice for organizing NodeJS code. Ideally of course, using my example of authenticating sockets.
Relevant part of app.js. Runs first.
var core = require('./core/core')();
app.set('orangeCore', core);
Core.js contains my "main" method and mainly does async API calls to other services. The idea is to submit responses from these services onto my (socket) channel as they tick in.
function core() {
var self = this;
init();
self.getOrangeDevices = getOrangeDevices;
self.bindSocket = bindSocket;
return self;
function init() {...
}
function bindSocket(socket) {
socket.on('hello', function (msg) {
console.log('ello! ' + msg);
})
socket.emit('important-stuff', {
importantData: fromSomewhereElseInsideCore
});
}
}
module.exports = core;
Then there is the module for handling JWT authentication. I've based this on the socketio-jwt middleware. What I don't really like and what's causing me trouble (I do leave a lot of room here for me misunderstanding) is the auth method being done async. Since the authenticated socket doesn't get "exposed" before auth is complete, I don't understand how to bind to it in a good way. Thus I used a promise ...
var io;
var socketioJwt;
var Promise = require('bluebird');
function setup(server, app) {
return new Promise(function (resolve, reject) {
io = require('socket.io')(server);
socketioJwt = require("socketio-jwt");
io.on('connection', socketioJwt.authorize({
secret: app.get('superSecret'),
timeout: 15000
})).on('authenticated', function (socket) {
console.log('client authenticated');
resolve(socket);
});
});
}
module.exports = {
listen: function (server, app) {
return setup(server, app);
}
}
Finally, the www.js file which kicks of the server.
var server = http.createServer(app);
// Sockets.io
var io = require('../socket/socket-channels');
io.listen(server, app).then(function(authSocket){
require('../core/core')().bindSocket(authSocket);
}, function(err){
console.error(err);
});
I'm really uncertain if this is a viable approach. I'm also wondering how it will look further down the road once the app grows. Nor can I see a good way to implement the namespace feature of sockets without having to setup JWT for that as well.
While your target client platform is unclear, Maybe you could benefit from this small lib that allows two parties that are connected by a WebSocket to communicate using promises.
and where the authentication is done externally to that layer. I don't know if its viable for you to actually use it, but perhaps it can give you some inspiration.
here is the lib on github
Here is a small example:
Server:
var io = require('socket.io')(server);
var Hook = require('./Hook');
var hook = new Hook();
var validatedSockets = {};
io.on('connection', function (socket) {
// listen for messages
hook.attach(socket);
// avoid memory leak and security holes
socket.on('disconnect', function () {
delete validatedSockets[socket.id]
})
});
hook.onHello(function (message, socket) {
var token = message && message.token;
return loadClient(token)
.then(function (clientData) {
if (!clientData) {
throw {error: "You don't have access"}
}
// mark socket as validated
validatedSockets[socket.id] = clientData;
return {ok: 1}
})
});
hook.onHook(function (event, message, socket) {
if (!validatedSockets[socket.id]) {
throw {error: "You are not validated"}
}
// this will be added as an extra argument for every event
return validatedSockets[socket.id];
});
hook.on('myEvent', function (message, socket, clientData) {
// do something and return something/Promise
});
Client:
// assuming commonJS but it can also be used in the browser directly
var Hook = require('./Hook');
var hook = new Hook();
var socket = io.connect("http://myServer.com");
hook
.attach(socket)
.bindSocket(socket) // enables 'send(message, data)' instead of 'sendTo(socket, message, data)' and 'sendHello' instead of 'sendHelloTo'
.sendHello({token: '123456789abcdefg'})
.then(function (message) {
console.log("Successful hello", message);
hook
.send('myEvent', {payload: '123'})
.then(function (message) {
console.log("Successful hook", message);
})
.catch(function (err) {
console.log("failed hook", err);
});
})
.catch(function (err) {
if (err === hook.TIMEOUT) {
// timeout
}
else {
console.log("hello failed", err);
}
});
(The author is myself)