From 25dd6aabf6ad9313509a368cba0ad47f6ee1191b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 21 Nov 2021 13:21:42 +0100 Subject: [PATCH] lib/client: remove one roundtrip during SASL auth Instead of waiting for the server's empty challenge, send two AUTHENTICATE commands in a row. --- lib/client.js | 55 +++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/lib/client.js b/lib/client.js index a8c43e4..4c159cf 100644 --- a/lib/client.js +++ b/lib/client.js @@ -271,7 +271,12 @@ export default class Client extends EventTarget { this.handleCap(msg); break; case "AUTHENTICATE": - this.handleAuthenticate(msg); + // Both PLAIN and EXTERNAL expect an empty challenge + let challengeStr = msg.params[0]; + if (challengeStr != "+") { + this.dispatchEvent(new CustomEvent("error", { detail: "Expected an empty challenge, got: " + challengeStr })); + this.send({ command: "AUTHENTICATE", params: ["*"] }); + } break; case irc.RPL_LOGGEDIN: console.log("Logged in"); @@ -381,6 +386,23 @@ export default class Client extends EventTarget { } } + authenticate(mechanism, params) { + console.log(`Starting SASL ${mechanism} authentication`); + this.send({ command: "AUTHENTICATE", params: [mechanism] }); + switch (mechanism) { + case "PLAIN": + let respStr = btoa("\0" + params.username + "\0" + params.password); + this.send({ command: "AUTHENTICATE", params: [respStr] }); + break; + case "EXTERNAL": + this.send({ command: "AUTHENTICATE", params: [btoa("")] }); + break; + default: + this.send({ command: "AUTHENTICATE", params: ["*"] }); + throw new Error(`Unknown authentication mechanism '${mechanism}'`); + } + } + who(mask, options) { let params = [mask]; @@ -580,12 +602,12 @@ export default class Client extends EventTarget { cap = cap.toLowerCase(); this.enabledCaps[cap] = true; - if (cap == "sasl" && this.params.saslPlain) { - console.log("Starting SASL PLAIN authentication"); - this.send({ command: "AUTHENTICATE", params: ["PLAIN"] }); - } else if (cap == "sasl" && this.params.saslExternal) { - console.log("Starting SASL EXTERNAL authentication"); - this.send({ command: "AUTHENTICATE", params: ["EXTERNAL"] }); + if (cap === "sasl" && this.status !== Client.Status.REGISTERED) { + if (this.params.saslPlain) { + this.authenticate("PLAIN", this.params.saslPlain); + } else if (this.params.saslExternal) { + this.authenticate("EXTERNAL"); + } } }); break; @@ -598,25 +620,6 @@ export default class Client extends EventTarget { } } - handleAuthenticate(msg) { - let challengeStr = msg.params[0]; - - if (challengeStr != "+") { - this.dispatchEvent(new CustomEvent("error", { detail: "Expected an empty challenge, got: " + challengeStr })); - this.send({ command: "AUTHENTICATE", params: ["*"] }); - return; - } - - if (this.params.saslPlain) { - let respStr = btoa("\0" + this.params.saslPlain.username + "\0" + this.params.saslPlain.password); - this.send({ command: "AUTHENTICATE", params: [respStr] }); - } else if (this.params.saslExternal) { - this.send({ command: "AUTHENTICATE", params: [btoa("")] }); - } else { - throw new Error("Received AUTHENTICATE for unknown mechanism"); - } - } - send(msg) { if (!this.ws) { throw new Error("Failed to send IRC message " + msg.command + ": socket is closed");