From 6c324d44a1c8cdabb13ed47a2cf1a30d4647fbd7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 30 Jun 2024 23:44:14 +0200 Subject: [PATCH] lib/client: add support for AUTHENTICATE chunking SASL responses need to be split into 400 byte chunks before being sent to the server. --- lib/client.js | 15 +++++++-------- lib/irc.js | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/client.js b/lib/client.js index f94f0e6..af5a33d 100644 --- a/lib/client.js +++ b/lib/client.js @@ -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; } diff --git a/lib/irc.js b/lib/irc.js index 4209848..fe85f0c 100644 --- a/lib/irc.js +++ b/lib/irc.js @@ -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; +}