lib/client: remove one roundtrip during SASL auth

Instead of waiting for the server's empty challenge, send two
AUTHENTICATE commands in a row.
This commit is contained in:
Simon Ser 2021-11-21 13:21:42 +01:00
parent 0af40a1a8e
commit 25dd6aabf6

View file

@ -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");