lib/client: make authenticate() return a promise

This lets the caller handle the success/failure.
This commit is contained in:
Simon Ser 2021-11-21 16:06:13 +01:00
parent adefc620de
commit 24b50a332c

View file

@ -316,19 +316,6 @@ export default class Client extends EventTarget {
case irc.RPL_ENDOFWHO: case irc.RPL_ENDOFWHO:
this.endPendingList("WHO", msg); this.endPendingList("WHO", msg);
break; 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": case "PING":
this.send({ command: "PONG", params: [msg.params[0]] }); this.send({ command: "PONG", params: [msg.params[0]] });
break; break;
@ -396,19 +383,36 @@ export default class Client extends EventTarget {
throw new Error(`${mechanism} authentication not supported by the server`); throw new Error(`${mechanism} authentication not supported by the server`);
} }
console.log(`Starting SASL ${mechanism} authentication`); 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) { switch (mechanism) {
case "PLAIN": case "PLAIN":
let respStr = btoa("\0" + params.username + "\0" + params.password); let respStr = btoa("\0" + params.username + "\0" + params.password);
this.send({ command: "AUTHENTICATE", params: [respStr] }); initialResp = { command: "AUTHENTICATE", params: [respStr] };
break; break;
case "EXTERNAL": case "EXTERNAL":
this.send({ command: "AUTHENTICATE", params: [btoa("")] }); initialResp = { command: "AUTHENTICATE", params: [btoa("")] };
break; break;
default: default:
this.send({ command: "AUTHENTICATE", params: ["*"] });
throw new Error(`Unknown authentication mechanism '${mechanism}'`); 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) { who(mask, options) {
@ -583,11 +587,20 @@ export default class Client extends EventTarget {
if (this.status !== Client.Status.REGISTERED) { if (this.status !== Client.Status.REGISTERED) {
if (this.availableCaps["sasl"] !== undefined) { if (this.availableCaps["sasl"] !== undefined) {
let promise;
if (this.params.saslPlain) { if (this.params.saslPlain) {
this.authenticate("PLAIN", this.params.saslPlain); promise = this.authenticate("PLAIN", this.params.saslPlain);
} else if (this.params.saslExternal) { } 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) { if (this.availableCaps["soju.im/bouncer-networks"] !== undefined && this.params.bouncerNetwork) {