From 24b50a332cdcecda671910f2a4fcad2ab0ee0cbe Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 21 Nov 2021 16:06:13 +0100 Subject: [PATCH] lib/client: make authenticate() return a promise This lets the caller handle the success/failure. --- lib/client.js | 51 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/lib/client.js b/lib/client.js index 2bd574a..2353a74 100644 --- a/lib/client.js +++ b/lib/client.js @@ -316,19 +316,6 @@ export default class Client extends EventTarget { case irc.RPL_ENDOFWHO: this.endPendingList("WHO", msg); break; - case irc.RPL_SASLSUCCESS: - console.log("SASL authentication success"); - break; - case irc.ERR_NICKLOCKED: - case irc.ERR_SASLFAIL: - case irc.ERR_SASLTOOLONG: - case irc.ERR_SASLABORTED: - case irc.ERR_SASLALREADY: - this.dispatchEvent(new CustomEvent("error", { detail: "SASL error (" + msg.command + "): " + msg.params[1] })); - if (this.status !== Client.Status.REGISTERED) { - this.disconnect(); - } - break; case "PING": this.send({ command: "PONG", params: [msg.params[0]] }); break; @@ -396,19 +383,36 @@ export default class Client extends EventTarget { throw new Error(`${mechanism} authentication not supported by the server`); } console.log(`Starting SASL ${mechanism} authentication`); - this.send({ command: "AUTHENTICATE", params: [mechanism] }); + + // Send the first SASL response immediately to avoid a roundtrip + let initialResp = null; switch (mechanism) { case "PLAIN": let respStr = btoa("\0" + params.username + "\0" + params.password); - this.send({ command: "AUTHENTICATE", params: [respStr] }); + initialResp = { command: "AUTHENTICATE", params: [respStr] }; break; case "EXTERNAL": - this.send({ command: "AUTHENTICATE", params: [btoa("")] }); + initialResp = { command: "AUTHENTICATE", params: [btoa("")] }; break; default: - this.send({ command: "AUTHENTICATE", params: ["*"] }); throw new Error(`Unknown authentication mechanism '${mechanism}'`); } + + let startMsg = { command: "AUTHENTICATE", params: [mechanism] }; + let promise = this.roundtrip(startMsg, (msg) => { + switch (msg.command) { + case irc.RPL_SASLSUCCESS: + return true; + case irc.ERR_NICKLOCKED: + case irc.ERR_SASLFAIL: + case irc.ERR_SASLTOOLONG: + case irc.ERR_SASLABORTED: + case irc.ERR_SASLALREADY: + throw msg; + } + }); + this.send(initialResp); + return promise; } who(mask, options) { @@ -583,11 +587,20 @@ export default class Client extends EventTarget { if (this.status !== Client.Status.REGISTERED) { if (this.availableCaps["sasl"] !== undefined) { + let promise; if (this.params.saslPlain) { - this.authenticate("PLAIN", this.params.saslPlain); + promise = this.authenticate("PLAIN", this.params.saslPlain); } else if (this.params.saslExternal) { - this.authenticate("EXTERNAL"); + promise = this.authenticate("EXTERNAL"); } + (promise || Promise.resolve()).catch((msg) => { + if (msg.command) { + this.dispatchEvent(new CustomEvent("error", { + detail: "Authentication error (SASL " + msg.command + "): " + msg.params[1], + })); + } + this.disconnect(); + }); } if (this.availableCaps["soju.im/bouncer-networks"] !== undefined && this.params.bouncerNetwork) {