Allow Comms websocket auth to be done via token header

Fixes #2642
This commit is contained in:
Nick O'Leary
2020-07-09 19:07:51 +01:00
parent 32163d5f21
commit 1df2f5e96a
4 changed files with 74 additions and 28 deletions

View File

@@ -21,6 +21,7 @@ var log = require("@node-red/util").log; // TODO: separate module
var Tokens;
var Users;
var Permissions;
var Strategies;
var server;
var settings;
@@ -44,6 +45,7 @@ function init(_server,_settings,_runtimeAPI) {
Tokens.onSessionExpiry(handleSessionExpiry);
Users = require("../auth/users");
Permissions = require("../auth/permissions");
Strategies = require("../auth/strategies");
}
function handleSessionExpiry(session) {
@@ -63,17 +65,18 @@ function generateSession(length) {
return token.join("");
}
function CommsConnection(ws) {
function CommsConnection(ws, user) {
this.session = generateSession(32);
this.ws = ws;
this.stack = [];
this.user = null;
this.user = user;
this.lastSentTime = 0;
var self = this;
log.audit({event: "comms.open"});
log.trace("comms.open "+self.session);
var pendingAuth = (settings.adminAuth != null);
var preAuthed = !!user;
var pendingAuth = !this.user && (settings.adminAuth != null);
if (!pendingAuth) {
addActiveConnection(self);
@@ -199,8 +202,8 @@ function start() {
var commsPath = settings.httpAdminRoot || "/";
commsPath = (commsPath.slice(0,1) != "/" ? "/":"") + commsPath + (commsPath.slice(-1) == "/" ? "":"/") + "comms";
wsServer = new ws.Server({ noServer: true });
wsServer.on('connection',function(ws) {
var commsConnection = new CommsConnection(ws);
wsServer.on('connection',function(ws, request, user) {
var commsConnection = new CommsConnection(ws, user);
});
wsServer.on('error', function(err) {
log.warn(log._("comms.error-server",{message:err.toString()}));
@@ -209,8 +212,26 @@ function start() {
server.on('upgrade', function upgrade(request, socket, head) {
const pathname = url.parse(request.url).pathname;
if (pathname === commsPath) {
if (Users.tokenHeader() !== null && request.headers[Users.tokenHeader()]) {
// The user has provided custom token handling. For the websocket,
// the token could be provided in two ways:
// - as an http header (only possible with a reverse proxy setup)
// - passed over the connected websock in an auth packet
// If the header is present, verify the token. If not, use the auth
// packet over the connected socket
//
Strategies.authenticateUserToken(request).then(user => {
wsServer.handleUpgrade(request, socket, head, function done(ws) {
wsServer.emit('connection', ws, request, user);
});
}).catch(err => {
log.audit({event: "comms.auth.fail"});
socket.destroy();
})
return
}
wsServer.handleUpgrade(request, socket, head, function done(ws) {
wsServer.emit('connection', ws, request);
wsServer.emit('connection', ws, request, null);
});
}
// Don't destroy the socket as other listeners may want to handle the