matrix-nodejs-homeserver 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -7
- package/dist/api/account.js +385 -351
- package/dist/api/admin.js +76 -60
- package/dist/api/appservice.js +37 -25
- package/dist/api/backup.js +73 -62
- package/dist/api/federation.js +172 -138
- package/dist/api/filters.js +4 -3
- package/dist/api/legacy.js +91 -78
- package/dist/api/media.js +70 -54
- package/dist/api/misc.js +46 -37
- package/dist/api/preview.js +4 -3
- package/dist/api/profile.js +108 -97
- package/dist/api/push.js +114 -99
- package/dist/api/relationships.js +64 -58
- package/dist/api/rooms.js +703 -580
- package/dist/api/rtc.d.ts +0 -1
- package/dist/api/rtc.js +48 -34
- package/dist/api/search.js +133 -127
- package/dist/api/slidingsync.js +41 -31
- package/dist/api/spaces.js +82 -77
- package/dist/api/sync.js +59 -50
- package/dist/api/threadsubs.js +72 -61
- package/dist/api/threepid.js +99 -66
- package/dist/api/uia.js +17 -16
- package/dist/api/user.js +221 -178
- package/dist/core/admin.d.ts +0 -1
- package/dist/core/admin.js +6 -5
- package/dist/core/adminroom.js +9 -8
- package/dist/core/appservices.d.ts +2 -1
- package/dist/core/appservices.js +5 -4
- package/dist/core/audit.d.ts +0 -1
- package/dist/core/authrules.js +26 -25
- package/dist/core/delayed.js +2 -1
- package/dist/core/events.js +11 -10
- package/dist/core/invitefilter.js +2 -1
- package/dist/core/keys.js +2 -1
- package/dist/core/m.d.ts +209 -0
- package/dist/core/m.js +182 -0
- package/dist/core/partialstate.js +2 -1
- package/dist/core/policy.js +4 -3
- package/dist/core/push.d.ts +0 -1
- package/dist/core/push.js +12 -11
- package/dist/core/pushrules.js +36 -35
- package/dist/core/rooms.js +76 -74
- package/dist/core/stateres.js +7 -6
- package/dist/federation/client.js +2 -1
- package/dist/federation/index.d.ts +1 -1
- package/dist/federation/index.js +30 -29
- package/dist/homeserver.d.ts +2 -1
- package/dist/homeserver.js +1 -0
- package/dist/http/handler.js +2 -23
- package/dist/http/input.js +2 -2
- package/dist/http/procedure.js +25 -0
- package/dist/http/router.d.ts +9 -26
- package/dist/http/router.js +10 -30
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/vendor/zod/LICENSE +21 -0
- package/dist/vendor/zod/v4/classic/errors.js +54 -0
- package/dist/vendor/zod/v4/classic/parse.js +17 -0
- package/dist/vendor/zod/v4/classic/schemas.js +865 -0
- package/dist/vendor/zod/v4/core/api.js +512 -0
- package/dist/vendor/zod/v4/core/checks.js +300 -0
- package/dist/vendor/zod/v4/core/core.js +109 -0
- package/dist/vendor/zod/v4/core/doc.js +35 -0
- package/dist/vendor/zod/v4/core/errors.js +118 -0
- package/dist/vendor/zod/v4/core/json-schema-processors.js +428 -0
- package/dist/vendor/zod/v4/core/memoizer.js +255 -0
- package/dist/vendor/zod/v4/core/parse.js +206 -0
- package/dist/vendor/zod/v4/core/regexes.js +73 -0
- package/dist/vendor/zod/v4/core/registries.js +48 -0
- package/dist/vendor/zod/v4/core/schemas.js +1378 -0
- package/dist/vendor/zod/v4/core/to-json-schema.js +448 -0
- package/dist/vendor/zod/v4/core/util.js +537 -0
- package/dist/vendor/zod/v4/core/versions.js +8 -0
- package/dist/vendor/zod/v4/locales/en.js +110 -0
- package/package.json +3 -5
- package/dist/core/pushrules.d.ts +0 -1
package/dist/api/threepid.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import { M } from "../core/m.js";
|
|
1
2
|
import { MatrixError, errors } from "../core/errors.js";
|
|
2
3
|
import { canonicalAddress, isMedium } from "../core/threepid.js";
|
|
3
|
-
import {
|
|
4
|
+
import { enum as _enum, string } from "../vendor/zod/v4/classic/schemas.js";
|
|
4
5
|
import { body, fail, lenient } from "../http/input.js";
|
|
5
|
-
import {
|
|
6
|
+
import { procedure } from "../http/procedure.js";
|
|
7
|
+
import { requireUia } from "./uia.js";
|
|
6
8
|
//#region src/api/threepid.ts
|
|
7
|
-
const submitField =
|
|
9
|
+
const submitField = string(fail("M_MISSING_PARAM", "sid, client_secret and token are required"));
|
|
8
10
|
const submitBody = body({
|
|
9
11
|
sid: submitField,
|
|
10
12
|
client_secret: submitField,
|
|
11
13
|
token: submitField
|
|
12
14
|
});
|
|
13
|
-
const bindField = (name) =>
|
|
15
|
+
const bindField = (name) => string(fail("M_MISSING_PARAM", `Missing ${name}`));
|
|
14
16
|
const bindBody = body({
|
|
15
17
|
client_secret: bindField("client_secret"),
|
|
16
18
|
id_access_token: bindField("id_access_token"),
|
|
@@ -18,9 +20,9 @@ const bindBody = body({
|
|
|
18
20
|
sid: bindField("sid")
|
|
19
21
|
});
|
|
20
22
|
const threepidBody = body({
|
|
21
|
-
medium:
|
|
22
|
-
address:
|
|
23
|
-
id_server:
|
|
23
|
+
medium: _enum(["email", "msisdn"], fail("M_INVALID_PARAM", "Unsupported medium")),
|
|
24
|
+
address: string(fail("M_INVALID_PARAM", "Invalid address")),
|
|
25
|
+
id_server: string(fail("M_INVALID_PARAM", "id_server must be a string")).optional()
|
|
24
26
|
});
|
|
25
27
|
function notDelegated(ctx) {
|
|
26
28
|
if (ctx.hs.oauth) throw errors.unrecognized("Managed by the OAuth provider");
|
|
@@ -32,12 +34,21 @@ function registerThreepidRoutes(r) {
|
|
|
32
34
|
return ctx.hs.threepids.requestToken(purpose, medium, ctx.input);
|
|
33
35
|
};
|
|
34
36
|
for (const medium of ["email", "msisdn"]) {
|
|
35
|
-
r.client("POST", `/register/${medium}/requestToken`,
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
r.client("POST", `/register/${medium}/requestToken`, procedure({
|
|
38
|
+
input: body({}),
|
|
39
|
+
handler: requestToken("register", medium)
|
|
40
|
+
}));
|
|
41
|
+
r.client("POST", `/account/password/${medium}/requestToken`, procedure({
|
|
42
|
+
input: body({}),
|
|
43
|
+
handler: requestToken("password_reset", medium)
|
|
44
|
+
}));
|
|
45
|
+
r.client("POST", `/account/3pid/${medium}/requestToken`, procedure({
|
|
46
|
+
input: body({}),
|
|
47
|
+
handler: requestToken("add_threepid", medium)
|
|
48
|
+
}));
|
|
38
49
|
}
|
|
39
50
|
const submitPath = "/_matrix/client/unstable/threepid/submit_token";
|
|
40
|
-
r.get(submitPath, async (ctx) => {
|
|
51
|
+
r.get(submitPath, procedure({ handler: async (ctx) => {
|
|
41
52
|
const q = ctx.query;
|
|
42
53
|
let nextLink = null;
|
|
43
54
|
let message = "Your address has been validated. You can return to your Matrix client.";
|
|
@@ -53,44 +64,57 @@ function registerThreepidRoutes(r) {
|
|
|
53
64
|
status,
|
|
54
65
|
headers: { "content-type": "text/html; charset=utf-8" }
|
|
55
66
|
});
|
|
56
|
-
});
|
|
57
|
-
r.post(submitPath,
|
|
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
|
-
|
|
67
|
+
} }));
|
|
68
|
+
r.post(submitPath, procedure({
|
|
69
|
+
input: submitBody,
|
|
70
|
+
handler: async (ctx) => {
|
|
71
|
+
const body = ctx.input;
|
|
72
|
+
await ctx.hs.threepids.submitToken(body.sid, body.client_secret, body.token);
|
|
73
|
+
return { success: true };
|
|
74
|
+
}
|
|
75
|
+
}));
|
|
76
|
+
r.client("GET", "/account/3pid", procedure({
|
|
77
|
+
auth: "user",
|
|
78
|
+
handler: async (ctx) => {
|
|
79
|
+
const s = ctx.session;
|
|
80
|
+
return { threepids: await ctx.hs.threepids.list(s.userId) };
|
|
81
|
+
}
|
|
82
|
+
}));
|
|
83
|
+
r.client("POST", "/account/3pid/add", procedure({
|
|
84
|
+
input: body({
|
|
85
|
+
sid: lenient(string()),
|
|
86
|
+
client_secret: lenient(string())
|
|
87
|
+
}),
|
|
88
|
+
handler: async (ctx) => {
|
|
89
|
+
notDelegated(ctx);
|
|
90
|
+
const s = await ctx.auth();
|
|
91
|
+
const body = ctx.input;
|
|
92
|
+
await requireUia(ctx.hs, body, [[M.LOGIN.PASSWORD]], s.userId);
|
|
93
|
+
const validated = await ctx.hs.threepids.validated({
|
|
94
|
+
sid: body.sid,
|
|
95
|
+
client_secret: body.client_secret
|
|
96
|
+
});
|
|
97
|
+
if (!validated) throw new MatrixError(400, "M_THREEPID_AUTH_FAILED", "The address has not been validated");
|
|
98
|
+
await ctx.hs.threepids.add(s.userId, validated);
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
}));
|
|
102
|
+
r.client("POST", "/account/3pid/bind", procedure({
|
|
103
|
+
auth: "user",
|
|
104
|
+
input: bindBody,
|
|
105
|
+
handler: async (ctx) => {
|
|
106
|
+
const s = ctx.session;
|
|
107
|
+
const body = ctx.input;
|
|
108
|
+
const res = await ctx.hs.identityServers.bind(body.id_server, body.id_access_token, body.sid, body.client_secret, s.userId);
|
|
109
|
+
if (res && isMedium(res.medium) && typeof res.address === "string") await ctx.hs.db.upsert("threepid_bindings", {
|
|
110
|
+
user_id: s.userId,
|
|
111
|
+
medium: res.medium,
|
|
112
|
+
address: res.address,
|
|
113
|
+
id_server: body.id_server
|
|
114
|
+
});
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
}));
|
|
94
118
|
const parseThreepid = (body) => {
|
|
95
119
|
const address = canonicalAddress(body.medium, body.address);
|
|
96
120
|
if (!address) throw errors.invalidParam("Invalid address");
|
|
@@ -100,23 +124,32 @@ function registerThreepidRoutes(r) {
|
|
|
100
124
|
idServer: body.id_server
|
|
101
125
|
};
|
|
102
126
|
};
|
|
103
|
-
r.client("POST", "/account/3pid/delete",
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
r.client("POST", "/account/3pid/delete", procedure({
|
|
128
|
+
input: threepidBody,
|
|
129
|
+
handler: async (ctx) => {
|
|
130
|
+
notDelegated(ctx);
|
|
131
|
+
const s = await ctx.auth();
|
|
132
|
+
const { medium, address, idServer } = parseThreepid(ctx.input);
|
|
133
|
+
const result = await ctx.hs.threepids.unbindAll(s.userId, medium, address, idServer);
|
|
134
|
+
await ctx.hs.threepids.remove(s.userId, medium, address);
|
|
135
|
+
return { id_server_unbind_result: result };
|
|
136
|
+
}
|
|
137
|
+
}));
|
|
138
|
+
r.client("POST", "/account/3pid/unbind", procedure({
|
|
139
|
+
auth: "user",
|
|
140
|
+
input: threepidBody,
|
|
141
|
+
handler: async (ctx) => {
|
|
142
|
+
const s = ctx.session;
|
|
143
|
+
const { medium, address, idServer } = parseThreepid(ctx.input);
|
|
144
|
+
return { id_server_unbind_result: await ctx.hs.threepids.unbindAll(s.userId, medium, address, idServer) };
|
|
145
|
+
}
|
|
146
|
+
}));
|
|
147
|
+
r.client("POST", "/account/3pid", procedure({
|
|
148
|
+
auth: "user",
|
|
149
|
+
handler: async () => {
|
|
150
|
+
throw errors.threepidDenied();
|
|
151
|
+
}
|
|
152
|
+
}));
|
|
120
153
|
}
|
|
121
154
|
//#endregion
|
|
122
155
|
export { registerThreepidRoutes };
|
package/dist/api/uia.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isObject } from "../core/json.js";
|
|
2
2
|
import { randomString } from "../core/crypto.js";
|
|
3
|
+
import { M, UNSTABLE } from "../core/m.js";
|
|
3
4
|
import { MatrixError, errors } from "../core/errors.js";
|
|
4
5
|
//#region src/api/uia.ts
|
|
5
6
|
const UIA_TTL_MS = 6e5;
|
|
@@ -9,7 +10,7 @@ const UIA_TTL_MS = 6e5;
|
|
|
9
10
|
* `userId` binds password stages to an already-authenticated user.
|
|
10
11
|
*/
|
|
11
12
|
async function requireUia(hs, body, flows, userId) {
|
|
12
|
-
if (hs.oauth && flows.some((f) => f.includes(
|
|
13
|
+
if (hs.oauth && flows.some((f) => f.includes(M.LOGIN.PASSWORD))) flows = [[M.OAUTH], [UNSTABLE.ORG.MATRIX.CROSS_SIGNING_RESET]];
|
|
13
14
|
const auth = isObject(body.auth) ? body.auth : null;
|
|
14
15
|
let sessionId = typeof auth?.session === "string" ? auth.session : void 0;
|
|
15
16
|
const stored = sessionId ? await hs.cluster.get(`uia:${sessionId}`) : null;
|
|
@@ -26,8 +27,8 @@ async function requireUia(hs, body, flows, userId) {
|
|
|
26
27
|
}, UIA_TTL_MS);
|
|
27
28
|
const oauthUrl = hs.oauth?.options.accountManagementUrl ?? hs.oauth?.options.issuer;
|
|
28
29
|
const params = hs.oauth ? {
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
[M.OAUTH]: { url: oauthUrl },
|
|
31
|
+
[UNSTABLE.ORG.MATRIX.CROSS_SIGNING_RESET]: { url: oauthUrl }
|
|
31
32
|
} : {};
|
|
32
33
|
const challenge = (extra = {}) => new MatrixError(401, extra.errcode ? String(extra.errcode) : "M_FORBIDDEN", String(extra.error ?? "Authentication required"), {
|
|
33
34
|
flows: flows.map((stages) => ({ stages })),
|
|
@@ -36,36 +37,36 @@ async function requireUia(hs, body, flows, userId) {
|
|
|
36
37
|
completed: [...session.completed],
|
|
37
38
|
...extra
|
|
38
39
|
});
|
|
39
|
-
if (auth?.type ===
|
|
40
|
+
if (auth?.type === M.LOGIN.DUMMY && !auth.session && typeof body.username === "string" && !userId) {
|
|
40
41
|
if (await hs.cluster.get(`uia-register:${body.username.toLowerCase()}`)) throw challenge({
|
|
41
42
|
errcode: "M_FORBIDDEN",
|
|
42
43
|
error: "Missing session"
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
46
|
if (auth?.type) switch (auth.type) {
|
|
46
|
-
case
|
|
47
|
-
session.completed.add(
|
|
47
|
+
case M.LOGIN.DUMMY:
|
|
48
|
+
session.completed.add(M.LOGIN.DUMMY);
|
|
48
49
|
break;
|
|
49
|
-
case
|
|
50
|
-
case
|
|
50
|
+
case M.OAUTH:
|
|
51
|
+
case UNSTABLE.ORG.MATRIX.CROSS_SIGNING_RESET:
|
|
51
52
|
if (!hs.oauth) throw challenge({
|
|
52
53
|
errcode: "M_UNRECOGNIZED",
|
|
53
54
|
error: "OAuth is not enabled"
|
|
54
55
|
});
|
|
55
56
|
session.completed.add(auth.type);
|
|
56
57
|
break;
|
|
57
|
-
case
|
|
58
|
+
case M.LOGIN.REGISTRATION_TOKEN: {
|
|
58
59
|
const { registrationTokenValid } = await import("./account.js");
|
|
59
60
|
if (typeof auth.token !== "string" || !await registrationTokenValid(hs, auth.token)) throw challenge({
|
|
60
61
|
errcode: "M_FORBIDDEN",
|
|
61
62
|
error: "Invalid registration token"
|
|
62
63
|
});
|
|
63
|
-
session.completed.add(
|
|
64
|
+
session.completed.add(M.LOGIN.REGISTRATION_TOKEN);
|
|
64
65
|
break;
|
|
65
66
|
}
|
|
66
|
-
case
|
|
67
|
+
case M.LOGIN.PASSWORD: {
|
|
67
68
|
const identifier = isObject(auth.identifier) ? auth.identifier : {
|
|
68
|
-
type:
|
|
69
|
+
type: M.ID.USER,
|
|
69
70
|
user: auth.user
|
|
70
71
|
};
|
|
71
72
|
const user = typeof identifier.user === "string" ? identifier.user : "";
|
|
@@ -84,13 +85,13 @@ async function requireUia(hs, body, flows, userId) {
|
|
|
84
85
|
errcode: "M_FORBIDDEN",
|
|
85
86
|
error: "Invalid password"
|
|
86
87
|
});
|
|
87
|
-
session.completed.add(
|
|
88
|
+
session.completed.add(M.LOGIN.PASSWORD);
|
|
88
89
|
break;
|
|
89
90
|
}
|
|
90
|
-
case
|
|
91
|
-
case
|
|
91
|
+
case M.LOGIN.EMAIL.IDENTITY:
|
|
92
|
+
case M.LOGIN.MSISDN: {
|
|
92
93
|
const validated = await hs.threepids.validated(auth.threepid_creds ?? auth.threepidCreds);
|
|
93
|
-
const medium = auth.type ===
|
|
94
|
+
const medium = auth.type === M.LOGIN.EMAIL.IDENTITY ? "email" : "msisdn";
|
|
94
95
|
if (!validated || validated.medium !== medium) throw challenge({
|
|
95
96
|
errcode: "M_UNAUTHORIZED",
|
|
96
97
|
error: "The address has not been validated"
|