Add support for SASL EXTERNAL

Can be useful when the server is using e.g. a cookie for
authentication purposes.
This commit is contained in:
Simon Ser 2021-10-12 17:29:56 +02:00
parent a890665775
commit 21a4a71542
4 changed files with 22 additions and 7 deletions

View file

@ -87,8 +87,8 @@ gamja default settings can be set using a `config.json` file at the root:
"autojoin": "#gamja",
// Controls how the password UI is presented to the user. Set to
// "mandatory" to require a password, "optional" to accept one but not
// require it, and "disabled" to never ask for a password. Defaults to
// "optional".
// require it, "disabled" to never ask for a password, or "external" to
// use SASL EXTERNAL. Defaults to "optional".
"auth": "optional",
// Default nickname (string).
"nick": "asdf",

View file

@ -124,6 +124,7 @@ export default class App extends Component {
realname: null,
nick: null,
saslPlain: null,
saslExternal: false,
autoconnect: false,
autojoin: [],
},
@ -207,6 +208,9 @@ export default class App extends Component {
if (typeof config.server.autoconnect === "boolean") {
connectParams.autoconnect = config.server.autoconnect;
}
if (config.server.auth === "external") {
connectParams.saslExternal = true;
}
}
let autoconnect = store.autoconnect.load();

View file

@ -61,6 +61,8 @@ export default class ConnectForm extends Component {
username: params.username || params.nick,
password: this.state.password,
};
} else if (this.props.auth === "external") {
params.saslExternal = true;
}
this.state.autojoin.split(",").forEach(function(ch) {
@ -112,7 +114,7 @@ export default class ConnectForm extends Component {
}
let auth = null;
if (this.props.auth !== "disabled") {
if (this.props.auth !== "disabled" && this.props.auth !== "external") {
auth = html`
<label>
Password:<br/>

View file

@ -69,6 +69,7 @@ export default class Client extends EventTarget {
nick: null,
pass: null,
saslPlain: null,
saslExternal: false,
bouncerNetwork: null,
};
batches = new Map();
@ -498,7 +499,7 @@ export default class Client extends EventTarget {
let reqCaps = [];
let capEnd = true;
if (this.params.saslPlain && this.supportsSASL("PLAIN")) {
if ((this.params.saslPlain && this.supportsSASL("PLAIN")) || (this.params.saslExternal && this.supportsSASL("EXTERNAL"))) {
// CAP END is deferred after authentication finishes
reqCaps.push("sasl");
capEnd = false;
@ -537,6 +538,9 @@ export default class Client extends EventTarget {
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"] });
}
});
break;
@ -552,15 +556,20 @@ export default class Client extends EventTarget {
handleAuthenticate(msg) {
let challengeStr = msg.params[0];
// For now only PLAIN is supported
if (challengeStr != "+") {
this.dispatchEvent(new CustomEvent("error", { detail: "Expected an empty challenge, got: " + challengeStr }));
this.send({ command: "AUTHENTICATE", params: ["*"] });
return;
}
let respStr = btoa("\0" + this.params.saslPlain.username + "\0" + this.params.saslPlain.password);
this.send({ command: "AUTHENTICATE", params: [respStr] });
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) {