forked from xmppjs/xmpp.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
110 lines (92 loc) · 2.67 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"use strict";
const { encode, decode } = require("@xmpp/base64");
const SASLError = require("./lib/SASLError");
const xml = require("@xmpp/xml");
const SASLFactory = require("saslmechanisms");
// https://xmpp.org/rfcs/rfc6120.html#sasl
const NS = "urn:ietf:params:xml:ns:xmpp-sasl";
function getMechanismNames(features) {
return features.getChild("mechanisms", NS).children.map((el) => el.text());
}
async function authenticate(SASL, entity, mechname, credentials) {
const mech = SASL.create([mechname]);
if (!mech) {
throw new Error("No compatible mechanism");
}
const { domain } = entity.options;
const creds = {
username: null,
password: null,
server: domain,
host: domain,
realm: domain,
serviceType: "xmpp",
serviceName: domain,
...credentials,
};
return new Promise((resolve, reject) => {
const handler = (element) => {
if (element.attrs.xmlns !== NS) {
return;
}
if (element.name === "challenge") {
mech.challenge(decode(element.text()));
const resp = mech.response(creds);
entity.send(
xml(
"response",
{ xmlns: NS, mechanism: mech.name },
typeof resp === "string" ? encode(resp) : "",
),
);
return;
}
if (element.name === "failure") {
reject(SASLError.fromElement(element));
} else if (element.name === "success") {
resolve();
}
entity.removeListener("nonza", handler);
};
entity.on("nonza", handler);
if (mech.clientFirst) {
entity.send(
xml(
"auth",
{ xmlns: NS, mechanism: mech.name },
encode(mech.response(creds)),
),
);
}
});
}
module.exports = function sasl({ streamFeatures }, credentials) {
const SASL = new SASLFactory();
streamFeatures.use("mechanisms", NS, async ({ stanza, entity }) => {
const offered = getMechanismNames(stanza);
const supported = SASL._mechs.map(({ name }) => name);
// eslint-disable-next-line unicorn/prefer-array-find
const intersection = supported.filter((mech) => {
return offered.includes(mech);
});
// eslint-disable-next-line prefer-destructuring
let mech = intersection[0];
if (typeof credentials === "function") {
await credentials(
(creds) => authenticate(SASL, entity, mech, creds, stanza),
mech,
);
} else {
if (!credentials.username && !credentials.password) {
mech = "ANONYMOUS";
}
await authenticate(SASL, entity, mech, credentials, stanza);
}
await entity.restart();
});
return {
use(...args) {
return SASL.use(...args);
},
};
};