lib/client: add support for AUTHENTICATE chunking

SASL responses need to be split into 400 byte chunks before being
sent to the server.
This commit is contained in:
Simon Ser 2024-06-30 23:44:14 +02:00
parent d9f7faad88
commit 6c324d44a1
2 changed files with 25 additions and 8 deletions

View file

@ -1,5 +1,4 @@
import * as irc from "./irc.js";
import * as base64 from "./base64.js";
// Static list of capabilities that are always requested when supported by the
// server
@ -467,18 +466,16 @@ export default class Client extends EventTarget {
console.log(`Starting SASL ${mechanism} authentication`);
// Send the first SASL response immediately to avoid a roundtrip
let initialResp = null;
let initialResp;
switch (mechanism) {
case "PLAIN":
let respStr = base64.encode("\0" + params.username + "\0" + params.password);
initialResp = { command: "AUTHENTICATE", params: [respStr] };
initialResp = "\0" + params.username + "\0" + params.password;
break;
case "EXTERNAL":
initialResp = { command: "AUTHENTICATE", params: ["+"] };
initialResp = "";
break;
case "OAUTHBEARER":
let raw = "n,,\x01auth=Bearer " + params.token + "\x01\x01";
initialResp = { command: "AUTHENTICATE", params: [base64.encode(raw)] };
initialResp = "n,,\x01auth=Bearer " + params.token + "\x01\x01";
break;
default:
throw new Error(`Unknown authentication mechanism '${mechanism}'`);
@ -497,7 +494,9 @@ export default class Client extends EventTarget {
throw new IRCError(msg);
}
});
this.send(initialResp);
for (let msg of irc.generateAuthenticateMessages(initialResp)) {
this.send(msg);
}
return promise;
}

View file

@ -1,3 +1,5 @@
import * as base64 from "./base64.js";
// RFC 1459
export const RPL_WELCOME = "001";
export const RPL_YOURHOST = "002";
@ -942,3 +944,19 @@ export class CapRegistry {
return { command: "CAP", params: ["REQ", l.join(" ")] };
}
}
const maxSASLLength = 400;
export function generateAuthenticateMessages(payload) {
let encoded = base64.encode(payload);
// <= instead of < because we need to send a final empty response if the
// last chunk is exactly 400 bytes long
let msgs = [];
for (let i = 0; i <= encoded.length; i += maxSASLLength) {
let chunk = encoded.substring(i, i + 400);
msgs.push({ command: "AUTHENTICATE", params: [chunk || "+"] });
}
return msgs;
}