From 5c5290dce26f5225e82afc03021dcb40417c6de0 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Mon, 15 Oct 2018 17:21:37 -0700 Subject: [PATCH] Fixed ClickOnce support & improved websocket authentication --- meshcentral.js | 7 +- meshrelay.js | 64 ++++------- package.json | 2 +- .../MeshMiniRouter.application | 8 +- .../MeshMiniRouter.exe.config.deploy | 0 .../MeshMiniRouter.exe.deploy | Bin 193536 -> 186368 bytes .../MeshMiniRouter.exe.manifest | 10 +- .../MeshMiniRouter.ico.deploy | Bin .../minirouter/MeshMiniRouter.application | 8 +- public/clickonce/minirouter/publish.htm | 2 +- views/default.handlebars | 2 +- webserver.js | 107 ++++++++---------- 12 files changed, 87 insertions(+), 123 deletions(-) rename public/clickonce/minirouter/Application Files/{MeshMiniRouter_2_0_0_16 => MeshMiniRouter_2_0_0_17}/MeshMiniRouter.application (85%) rename public/clickonce/minirouter/Application Files/{MeshMiniRouter_2_0_0_16 => MeshMiniRouter_2_0_0_17}/MeshMiniRouter.exe.config.deploy (100%) rename public/clickonce/minirouter/Application Files/{MeshMiniRouter_2_0_0_16 => MeshMiniRouter_2_0_0_17}/MeshMiniRouter.exe.deploy (74%) rename public/clickonce/minirouter/Application Files/{MeshMiniRouter_2_0_0_16 => MeshMiniRouter_2_0_0_17}/MeshMiniRouter.exe.manifest (90%) rename public/clickonce/minirouter/Application Files/{MeshMiniRouter_2_0_0_16 => MeshMiniRouter_2_0_0_17}/MeshMiniRouter.ico.deploy (100%) diff --git a/meshcentral.js b/meshcentral.js index ddd46245..18d28ee4 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -1055,7 +1055,8 @@ function CreateMeshCentralServer(config, args) { o.time = Math.floor(Date.now() / 1000); // Add the cookie creation time var iv = new Buffer(obj.crypto.randomBytes(12), 'binary'), cipher = obj.crypto.createCipheriv('aes-256-gcm', key, iv); var crypted = Buffer.concat([cipher.update(JSON.stringify(o), 'utf8'), cipher.final()]); - return Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + var cookie = Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + return cookie; } catch (e) { return null; } }; @@ -1067,11 +1068,11 @@ function CreateMeshCentralServer(config, args) { var decipher = obj.crypto.createDecipheriv('aes-256-gcm', key, cookie.slice(0, 12)); decipher.setAuthTag(cookie.slice(12, 16)); var o = JSON.parse(decipher.update(cookie.slice(28), 'binary', 'utf8') + decipher.final('utf8')); - if ((o.time == null) || (o.time == null) || (typeof o.time != 'number')) { return null; } + if ((o.time == null) || (o.time == null) || (typeof o.time != 'number')) { Debug(1, 'ERR: Bad cookie due to invalid time'); return null; } o.time = o.time * 1000; // Decode the cookie creation time o.dtime = Date.now() - o.time; // Decode how long ago the cookie was created (in milliseconds) if (timeout == null) { timeout = 2; } - if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) return null; // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right) + if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) { Debug(1, 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right) return o; } catch (e) { return null; } }; diff --git a/meshrelay.js b/meshrelay.js index f6c93ccf..88b65fc6 100644 --- a/meshrelay.js +++ b/meshrelay.js @@ -13,11 +13,13 @@ /*jshint esversion: 6 */ "use strict"; -module.exports.CreateMeshRelay = function (parent, ws, req, domain) { +module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie) { var obj = {}; obj.ws = ws; obj.req = req; obj.peer = null; + obj.user = user; + obj.cookie = cookie; obj.parent = parent; obj.id = req.query.id; obj.remoteaddr = obj.ws._socket.remoteAddress; @@ -69,49 +71,25 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) { return false; }; - if (req.query.auth == null) { - // Use ExpressJS session, check if this session is a logged in user, at least one of the two connections will need to be authenticated. - try { if ((req.session) && (req.session.userid) || (req.session.domainid == obj.domain.id)) { obj.authenticated = true; } } catch (e) { } - if ((obj.authenticated != true) && (req.query.user != null) && (req.query.pass != null)) { - // Check user authentication - obj.parent.authenticate(req.query.user, req.query.pass, obj.domain, function (err, userid, passhint) { - if (userid != null) { - obj.authenticated = true; - // Check if we have agent routing instructions, process this here. - if ((req.query.nodeid != null) && (req.query.tcpport != null)) { - if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one. - var command = { nodeid: req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: req.query.tcpport, tcpaddr: ((req.query.tcpaddr == null) ? '127.0.0.1' : req.query.tcpaddr) }; - if (obj.sendAgentMessage(command, userid, obj.domain.id) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); } - } - } else { - obj.parent.parent.debug(1, 'Relay: User authentication failed (' + obj.remoteaddr + ')'); - obj.ws.send('error:Authentication failed'); - } - performRelay(); - }); - } else { - performRelay(); - } - } else { - // Get the session from the cookie - var cookie = obj.parent.parent.decodeCookie(req.query.auth); - if (cookie != null) { - obj.authenticated = true; - if (cookie.tcpport != null) { - // This cookie has agent routing instructions, process this here. - if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one. - // Send connection request to agent - var command = { nodeid: cookie.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: cookie.tcpport, tcpaddr: cookie.tcpaddr }; - if (obj.sendAgentMessage(command, cookie.userid, cookie.domainid) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); } - } - } else { - obj.id = null; - obj.parent.parent.debug(1, 'Relay: invalid cookie (' + obj.remoteaddr + ')'); - obj.ws.send('error:Invalid cookie'); - } - performRelay(); - } + // Mark this relay session as authenticated if this is the user end. + obj.authenticated = (obj.user != null); + // Kick off the routing, if we have agent routing instructions, process them here. + if ((obj.cookie != null) && (obj.cookie.nodeid != null) && (obj.cookie.tcpport != null) && (obj.cookie.domainid != null)) { + // We have routing instructions in the cookie, Send connection request to agent + if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one. + var command = { nodeid: obj.cookie.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: obj.cookie.tcpport, tcpaddr: obj.cookie.tcpaddr }; + obj.parent.parent.debug(1, 'Relay: Sending agent tunnel command: ' + JSON.stringify(command)); + if (obj.sendAgentMessage(command, obj.cookie.userid, obj.cookie.domainid) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); } + } else if ((req.query.nodeid != null) && (req.query.tcpport != null)) { + // We have routing instructions in the URL arguments, Send connection request to agent + if (obj.id == null) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one. + var command = { nodeid: req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: req.query.tcpport, tcpaddr: ((req.query.tcpaddr == null) ? '127.0.0.1' : req.query.tcpaddr) }; + obj.parent.parent.debug(1, 'Relay: Sending agent tunnel command: ' + JSON.stringify(command)); + if (obj.sendAgentMessage(command, userid, obj.domain.id) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); } + } + performRelay(); + function performRelay() { if (obj.id == null) { try { obj.close(); } catch (e) { } return null; } // Attempt to connect without id, drop this. ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive diff --git a/package.json b/package.json index cc38f8d9..9a7ef93f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.2.2-e", + "version": "0.2.2-f", "keywords": [ "Remote Management", "Intel AMT", diff --git a/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.application b/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.application similarity index 85% rename from public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.application rename to public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.application index f22c13df..4a987888 100644 --- a/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.application +++ b/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.application @@ -1,20 +1,20 @@  - + - - + + - uaxqCrqKPjDkZMXMlJ9pIvARsSxYXXLci7n8z3Q8hUU= + nyBHr6mVUVhjU6l4Bmrfa0juzDDrPD6BiiYzVMhKKVA= diff --git a/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.exe.config.deploy b/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.exe.config.deploy similarity index 100% rename from public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.exe.config.deploy rename to public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.exe.config.deploy diff --git a/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.exe.deploy b/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.exe.deploy similarity index 74% rename from public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.exe.deploy rename to public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.exe.deploy index 0a90de1385b8a9ca107772bec52b1f7daaac21b6..0d9a434875213fb2e658aa1907fdbd65f189d7ae 100644 GIT binary patch delta 26011 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 HPx}7=f6KXB diff --git a/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.exe.manifest b/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.exe.manifest similarity index 90% rename from public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.exe.manifest rename to public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.exe.manifest index 71e3dac7..41e8fa6d 100644 --- a/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.exe.manifest +++ b/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.exe.manifest @@ -1,10 +1,10 @@  - + - + @@ -43,14 +43,14 @@ - - + + - 2K6tEre6rIjqc6bZn7uhWlXLgAnZ82UP3jYzxNJ7WIk= + H+qrBKAsVVx/APIHP2Tq2cK3/FUh4SIShsjM6eo0fUw= diff --git a/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.ico.deploy b/public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.ico.deploy similarity index 100% rename from public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_16/MeshMiniRouter.ico.deploy rename to public/clickonce/minirouter/Application Files/MeshMiniRouter_2_0_0_17/MeshMiniRouter.ico.deploy diff --git a/public/clickonce/minirouter/MeshMiniRouter.application b/public/clickonce/minirouter/MeshMiniRouter.application index f22c13df..4a987888 100644 --- a/public/clickonce/minirouter/MeshMiniRouter.application +++ b/public/clickonce/minirouter/MeshMiniRouter.application @@ -1,20 +1,20 @@  - + - - + + - uaxqCrqKPjDkZMXMlJ9pIvARsSxYXXLci7n8z3Q8hUU= + nyBHr6mVUVhjU6l4Bmrfa0juzDDrPD6BiiYzVMhKKVA= diff --git a/public/clickonce/minirouter/publish.htm b/public/clickonce/minirouter/publish.htm index 6ed872d2..49e4503b 100644 --- a/public/clickonce/minirouter/publish.htm +++ b/public/clickonce/minirouter/publish.htm @@ -59,7 +59,7 @@ FONT.key {font-weight: bold; color: darkgreen}
-
 
Name:MeshCentral Mini-Router
 
Version:2.0.0.16
 
Publisher:Meshcentral.com
 
+
 
Name:MeshCentral Mini-Router
 
Version:2.0.0.17
 
Publisher:Meshcentral.com
 
diff --git a/views/default.handlebars b/views/default.handlebars index 275e4fd1..6f24583f 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -1271,7 +1271,7 @@ case 'getcookie': { if (message.tag == 'clickonce') { var basicPort = "{{{serverRedirPort}}}"==""?"{{{serverPublicPort}}}":"{{{serverRedirPort}}}"; - rdpurl = "http://" + window.location.hostname + ":" + basicPort + "/clickonce/minirouter/MeshMiniRouter.application?WS=wss%3A%2F%2F" + window.location.hostname + "%2Fmeshrelay.ashx%3Fauth=" + message.cookie + "&CH={{{webcerthash}}}&AP=" + message.protocol + "&HOL=1"; + var rdpurl = "http://" + window.location.hostname + ":" + basicPort + "/clickonce/minirouter/MeshMiniRouter.application?WS=wss%3A%2F%2F" + window.location.hostname + "%2Fmeshrelay.ashx%3Fauth=" + message.cookie + "&CH={{{webcerthash}}}&AP=" + message.protocol + "&HOL=1"; window.open(rdpurl, '_blank'); } break; diff --git a/webserver.js b/webserver.js index 48ee91e4..1672e15d 100644 --- a/webserver.js +++ b/webserver.js @@ -1059,26 +1059,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { }; // Handle a web socket relay request - function handleRelayWebSocket(ws, req) { - var domain = checkUserIpAddress(ws, req); - if (domain == null) return; - // Check if this is a logged in user - var user, peering = true; - if (req.query.auth == null) { - // Use ExpressJS session - if (!req.session || !req.session.userid) { return; } // Web socket attempt without login, disconnect. - if (req.session.domainid != domain.id) { console.log('ERR: Invalid domain'); return; } - user = obj.users[req.session.userid]; - } else { - // Get the session from the cookie - if (obj.parent.multiServer == null) { return; } - var session = obj.parent.decodeCookie(req.query.auth); - if (session == null) { console.log('ERR: Invalid cookie'); return; } - if (session.domainid != domain.id) { console.log('ERR: Invalid domain'); return; } - user = obj.users[session.userid]; - peering = false; // Don't allow the connection to jump again to a different server - } - if (!user) { console.log('ERR: Not a user'); return; } + function handleRelayWebSocket(ws, req, domain, user, cookie) { + if (!(req.query.host)) { console.log('ERR: No host target specified'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket Debug(1, 'Websocket relay connected from ' + user.name + ' for ' + req.query.host + '.'); ws.pause(); // Hold this socket until we are ready. @@ -1086,13 +1068,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Fetch information about the target obj.db.Get(req.query.host, function (err, docs) { - if (docs.length == 0) { console.log('ERR: Node not found'); return; } + if (docs.length == 0) { console.log('ERR: Node not found'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket var node = docs[0]; - if (!node.intelamt) { console.log('ERR: Not AMT node'); return; } + if (!node.intelamt) { console.log('ERR: Not AMT node'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket // Check if this user has permission to manage this computer var meshlinks = user.links[node.meshid]; - if ((!meshlinks) || (!meshlinks.rights) || ((meshlinks.rights & MESHRIGHT_REMOTECONTROL) == 0)) { console.log('ERR: Access denied (2)'); return; } + if ((!meshlinks) || (!meshlinks.rights) || ((meshlinks.rights & MESHRIGHT_REMOTECONTROL) == 0)) { console.log('ERR: Access denied (2)'); try { ws.close(); } catch (e) { } return; } // Check what connectivity is available for this node var state = parent.GetConnectivityState(req.query.host); @@ -1100,7 +1082,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (!state || state.connectivity == 0) { Debug(1, 'ERR: No routing possible (1)'); try { ws.close(); } catch (e) { } return; } else { conn = state.connectivity; } // Check what server needs to handle this connection - if ((obj.parent.multiServer != null) && (peering == true)) { + if ((obj.parent.multiServer != null) && (cookie == null)) { // If a cookie is provided, don't allow the connection to jump again to a different server var server = obj.parent.GetRoutingServerId(req.query.host, 2); // Check for Intel CIRA connection if (server != null) { if (server.serverid != obj.parent.serverId) { @@ -1810,10 +1792,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.app.post(url + 'uploadmeshcorefile.ashx', handleUploadMeshCoreFile); obj.app.get(url + 'userfiles/*', handleDownloadUserFiles); obj.app.ws(url + 'echo.ashx', handleEchoWebSocket); - obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { try { obj.meshRelayHandler.CreateMeshRelay(obj, ws, req, getDomain(req)); } catch (e) { console.log(e); } }); + obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, function (ws1, req1, domain, user, cookie) { obj.meshRelayHandler.CreateMeshRelay(obj, ws1, req1, domain, user, cookie); }); }); obj.app.get(url + 'webrelay.ashx', function (req, res) { res.send('Websocket connection expected'); }); - obj.app.ws(url + 'webrelay.ashx', function (ws, req) { PerformSessionAuth(ws, req, handleRelayWebSocket); }); - obj.app.ws(url + 'control.ashx', function (ws, req) { PerformSessionAuth(ws, req, function (ws1, req1, domain) { obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws1, req1, obj.args, domain); }); }); + obj.app.ws(url + 'webrelay.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, handleRelayWebSocket); }); + obj.app.ws(url + 'control.ashx', function (ws, req) { PerformWSSessionAuth(ws, req, function (ws1, req1, domain, user, cookie) { obj.meshUserHandler.CreateMeshUser(obj, obj.db, ws1, req1, obj.args, domain); }); }); // Server picture obj.app.get(url + 'serverpic.ashx', function (req, res) { @@ -1847,47 +1829,50 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } // Authenticates a session and forwards - function PerformSessionAuth(ws, req, func) { + function PerformWSSessionAuth(ws, req, func) { try { + // Check IP filtering and domain var domain = checkUserIpAddress(ws, req); - if (domain != null) { - var loginok = false; - // Check if the user is logged in - if ((!req.session) || (!req.session.userid) || (req.session.domainid != domain.id)) { - // If a default user is active, setup the session here. - if (obj.args.user && obj.users['user/' + domain.id + '/' + obj.args.user.toLowerCase()]) { - if (req.session && req.session.loginmode) { delete req.session.loginmode; } - req.session.userid = 'user/' + domain.id + '/' + obj.args.user.toLowerCase(); - req.session.domainid = domain.id; - func(ws, req, domain); - loginok = true; + if (domain == null) { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); return; } catch (e) { return; } } + + // A web socket session can be authenticated in many ways (Default user, session, user/pass and cookie). Check authentication here. + if ((req.query.user != null) && (req.query.pass != null)) { + // A user/pass is provided in URL arguments + obj.authenticate(req.query.user, req.query.pass, domain, function (err, userid) { + if ((err == null) && (obj.users[userid])) { + // We are authenticated + func(ws, req, domain, obj.users[userid]); } else { - // See the the user/pass is provided in URL arguments - if ((req.query.user != null) && (req.query.pass != null)) { - loginok = true; - obj.authenticate(req.query.user, req.query.pass, domain, function (err, userid) { - var loginok2 = false; - if (err == null) { - var user = obj.users[userid]; - if (user) { - req.session.userid = userid; - req.session.domainid = domain.id; - func(ws, req, domain); - loginok2 = true; - } - } - // If not authenticated, close the websocket connection - if (loginok2 == false) { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { } } - }); - } + // If not authenticated, close the websocket connection + Debug(1, 'ERR: Websocket bad user/pass auth'); + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { } } + }); + return; + } else if (req.query.auth != null) { + // This is a encrypted cookie authentication + var cookie = obj.parent.decodeCookie(req.query.auth, null, 60); // Cookie with 60 minute timeout + if ((cookie != null) && (obj.users[cookie.userid])) { + // Valid cookie, we are authenticated + func(ws, req, domain, obj.users[cookie.userid], cookie); } else { - func(ws, req, domain); - loginok = true; + // This is a bad cookie + Debug(1, 'ERR: Websocket bad cookie auth: ' + req.query.auth); + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { } } - // If not authenticated, close the websocket connection - if (loginok == false) { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { } } + return; + } else if (obj.args.user && obj.users['user/' + domain.id + '/' + obj.args.user.toLowerCase()]) { + // A default user is active + func(ws, req, domain, obj.users['user/' + domain.id + '/' + obj.args.user.toLowerCase()]); + return; + } else if (req.session && (req.session.userid != null) && (req.session.domainid == obj.domain.id)) { + // This user is logged in using the ExpressJS session + func(ws, req, domain, req.session.userid); + return; } + // If not authenticated, close the websocket connection + Debug(1, 'ERR: Websocket no auth'); + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth' })); ws.close(); } catch (e) { } } catch (e) { console.log(e); } }