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/account.js
CHANGED
|
@@ -1,51 +1,53 @@
|
|
|
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
|
import { LOCALPART_RE } from "../core/ids.js";
|
|
5
6
|
import { toMsisdn } from "../core/phone.js";
|
|
6
7
|
import { canonicalAddress, isMedium } from "../core/threepid.js";
|
|
7
|
-
import {
|
|
8
|
+
import { boolean, looseObject, string } from "../vendor/zod/v4/classic/schemas.js";
|
|
8
9
|
import { body, fail, lenient } from "../http/input.js";
|
|
10
|
+
import { procedure } from "../http/procedure.js";
|
|
11
|
+
import { requireUia } from "./uia.js";
|
|
9
12
|
import { createHmac, randomInt, timingSafeEqual } from "node:crypto";
|
|
10
|
-
import { z } from "zod";
|
|
11
13
|
//#region src/api/account.ts
|
|
12
14
|
const deviceFields = {
|
|
13
|
-
device_id: lenient(
|
|
14
|
-
initial_device_display_name: lenient(
|
|
15
|
-
refresh_token: lenient(
|
|
16
|
-
inhibit_login: lenient(
|
|
15
|
+
device_id: lenient(string()),
|
|
16
|
+
initial_device_display_name: lenient(string()),
|
|
17
|
+
refresh_token: lenient(boolean()),
|
|
18
|
+
inhibit_login: lenient(boolean())
|
|
17
19
|
};
|
|
18
20
|
const loginBody = body({
|
|
19
|
-
type: lenient(
|
|
20
|
-
token: lenient(
|
|
21
|
-
password: lenient(
|
|
21
|
+
type: lenient(string()),
|
|
22
|
+
token: lenient(string()),
|
|
23
|
+
password: lenient(string()),
|
|
22
24
|
...deviceFields
|
|
23
25
|
});
|
|
24
26
|
const registerBody = body({
|
|
25
|
-
type: lenient(
|
|
26
|
-
auth: lenient(
|
|
27
|
-
username:
|
|
28
|
-
password:
|
|
27
|
+
type: lenient(string()),
|
|
28
|
+
auth: lenient(looseObject({ type: lenient(string()) })),
|
|
29
|
+
username: string(fail("M_INVALID_PARAM", "username must be a string")).optional(),
|
|
30
|
+
password: string(fail("M_INVALID_PARAM", "password must be a string")).optional(),
|
|
29
31
|
...deviceFields
|
|
30
32
|
});
|
|
31
33
|
const passwordBody = body({
|
|
32
|
-
new_password: lenient(
|
|
33
|
-
logout_devices: lenient(
|
|
34
|
+
new_password: lenient(string()),
|
|
35
|
+
logout_devices: lenient(boolean())
|
|
34
36
|
});
|
|
35
37
|
const sharedSecretRegisterBody = body({
|
|
36
|
-
nonce: lenient(
|
|
37
|
-
username:
|
|
38
|
-
password:
|
|
39
|
-
admin: lenient(
|
|
40
|
-
mac: lenient(
|
|
41
|
-
displayname: lenient(
|
|
38
|
+
nonce: lenient(string()),
|
|
39
|
+
username: string(fail("M_INVALID_PARAM", "Invalid username/password")),
|
|
40
|
+
password: string(fail("M_INVALID_PARAM", "Invalid username/password")),
|
|
41
|
+
admin: lenient(boolean()),
|
|
42
|
+
mac: lenient(string()),
|
|
43
|
+
displayname: lenient(string())
|
|
42
44
|
});
|
|
43
45
|
function loginFlows(hs) {
|
|
44
46
|
if (hs.oauth) return [];
|
|
45
47
|
const flows = [];
|
|
46
|
-
if (hs.userAuth.passwordLoginEnabled) flows.push({ type:
|
|
48
|
+
if (hs.userAuth.passwordLoginEnabled) flows.push({ type: M.LOGIN.PASSWORD });
|
|
47
49
|
if (hs.sso) flows.push({
|
|
48
|
-
type:
|
|
50
|
+
type: M.LOGIN.SSO,
|
|
49
51
|
identity_providers: hs.sso.providers.map((p) => ({
|
|
50
52
|
id: p.id,
|
|
51
53
|
name: p.name,
|
|
@@ -54,7 +56,7 @@ function loginFlows(hs) {
|
|
|
54
56
|
}))
|
|
55
57
|
});
|
|
56
58
|
flows.push({
|
|
57
|
-
type:
|
|
59
|
+
type: M.LOGIN.TOKEN,
|
|
58
60
|
...hs.config.loginViaExistingSession ? { get_login_token: true } : {}
|
|
59
61
|
});
|
|
60
62
|
return flows;
|
|
@@ -105,11 +107,11 @@ async function resolveLoginUser(hs, body) {
|
|
|
105
107
|
let user;
|
|
106
108
|
const id = isObject(body.identifier) ? body.identifier : null;
|
|
107
109
|
let threepid = null;
|
|
108
|
-
if (id?.type ===
|
|
110
|
+
if (id?.type === M.ID.THIRDPARTY && typeof id.medium === "string" && typeof id.address === "string") threepid = {
|
|
109
111
|
medium: id.medium,
|
|
110
112
|
address: isMedium(id.medium) ? canonicalAddress(id.medium, id.address) : null
|
|
111
113
|
};
|
|
112
|
-
else if (id?.type ===
|
|
114
|
+
else if (id?.type === M.ID.PHONE && typeof id.country === "string" && typeof id.phone === "string") threepid = {
|
|
113
115
|
medium: "msisdn",
|
|
114
116
|
address: toMsisdn(id.country, id.phone)
|
|
115
117
|
};
|
|
@@ -123,7 +125,7 @@ async function resolveLoginUser(hs, body) {
|
|
|
123
125
|
return owner;
|
|
124
126
|
}
|
|
125
127
|
if (id) {
|
|
126
|
-
if (id.type !==
|
|
128
|
+
if (id.type !== M.ID.USER) throw errors.unknown("Unsupported identifier type");
|
|
127
129
|
user = id.user;
|
|
128
130
|
} else user = body.user;
|
|
129
131
|
if (typeof user !== "string" || !user) throw errors.missingParam("Missing user identifier");
|
|
@@ -135,77 +137,135 @@ async function resolveLoginUser(hs, body) {
|
|
|
135
137
|
return `@${user.toLowerCase()}:${hs.serverName}`;
|
|
136
138
|
}
|
|
137
139
|
function registerAccountRoutes(r) {
|
|
138
|
-
r.client("GET", "/login", (ctx) => ({ flows: loginFlows(ctx.hs) }));
|
|
139
|
-
r.client("GET", "/auth_metadata", async (ctx) => {
|
|
140
|
+
r.client("GET", "/login", procedure({ handler: (ctx) => ({ flows: loginFlows(ctx.hs) }) }));
|
|
141
|
+
r.client("GET", "/auth_metadata", procedure({ handler: async (ctx) => {
|
|
140
142
|
if (!ctx.hs.oauth) throw errors.unrecognized("OAuth is not enabled");
|
|
141
143
|
return ctx.hs.oauth.serverMetadata();
|
|
142
|
-
}, ["v1"]);
|
|
143
|
-
r.client("GET", "/auth_issuer", (ctx) => {
|
|
144
|
+
} }), ["v1"]);
|
|
145
|
+
r.client("GET", "/auth_issuer", procedure({ handler: (ctx) => {
|
|
144
146
|
if (!ctx.hs.oauth) throw errors.unrecognized("OAuth is not enabled");
|
|
145
147
|
return { issuer: ctx.hs.oauth.options.issuer };
|
|
146
|
-
}, ["v1"]);
|
|
148
|
+
} }), ["v1"]);
|
|
147
149
|
const ssoRedirect = async (ctx) => {
|
|
148
150
|
if (!ctx.hs.sso) throw errors.unrecognized("SSO is not enabled");
|
|
149
151
|
const redirectUrl = ctx.query.get("redirectUrl");
|
|
150
152
|
if (!redirectUrl || !/^https?:\/\/|^[a-z][a-z0-9+.-]*:/i.test(redirectUrl)) throw errors.missingParam("Missing or invalid redirectUrl");
|
|
151
153
|
return Response.redirect(await ctx.hs.sso.startLogin(ctx.params.idpId ?? null, redirectUrl), 302);
|
|
152
154
|
};
|
|
153
|
-
r.client("POST", "/login/get_token",
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
155
|
+
r.client("POST", "/login/get_token", procedure({
|
|
156
|
+
auth: "user",
|
|
157
|
+
handler: async (ctx) => {
|
|
158
|
+
const s = ctx.session;
|
|
159
|
+
if (!ctx.hs.config.loginViaExistingSession || ctx.hs.oauth || s.isGuest) throw errors.unrecognized();
|
|
160
|
+
await ctx.rateLimit("login");
|
|
161
|
+
await requireUia(ctx.hs, await ctx.json(), [[M.LOGIN.PASSWORD]], s.userId);
|
|
162
|
+
const ttl = 12e4;
|
|
163
|
+
return {
|
|
164
|
+
login_token: await ctx.hs.auth.createLoginToken(s.userId, ttl),
|
|
165
|
+
expires_in_ms: ttl
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}), ["v1"]);
|
|
169
|
+
r.client("GET", "/login/sso/redirect", procedure({ handler: ssoRedirect }));
|
|
170
|
+
r.client("GET", "/login/sso/redirect/:idpId", procedure({ handler: ssoRedirect }));
|
|
171
|
+
r.client("GET", "/login/sso/callback/:idpId", procedure({ handler: async (ctx) => {
|
|
167
172
|
if (!ctx.hs.sso) throw errors.unrecognized("SSO is not enabled");
|
|
168
173
|
return Response.redirect(await ctx.hs.sso.finishLogin(ctx.params.idpId, ctx.query), 302);
|
|
169
|
-
});
|
|
170
|
-
r.client("POST", "/login",
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (
|
|
174
|
+
} }));
|
|
175
|
+
r.client("POST", "/login", procedure({
|
|
176
|
+
rateLimit: "login",
|
|
177
|
+
input: loginBody,
|
|
178
|
+
handler: async (ctx) => {
|
|
179
|
+
const body = ctx.input;
|
|
180
|
+
const hs = ctx.hs;
|
|
181
|
+
if (hs.oauth) throw errors.unrecognized("Log in through the OAuth provider");
|
|
182
|
+
if (body.type === M.LOGIN.APPLICATION_SERVICE) {
|
|
183
|
+
const reg = ctx.accessToken() ? hs.appservices.byToken(ctx.accessToken()) : null;
|
|
184
|
+
if (!reg) throw errors.unknownToken("An application service token is required");
|
|
185
|
+
const userId = await resolveLoginUser(hs, body);
|
|
186
|
+
if (!hs.appservices.isInterestedUser(reg, userId)) throw errors.forbidden("User is not in the application service's namespace");
|
|
187
|
+
if (!await hs.db.findOne("users", [{
|
|
188
|
+
field: "user_id",
|
|
189
|
+
value: userId
|
|
190
|
+
}])) throw errors.forbidden("Unknown user");
|
|
191
|
+
return {
|
|
192
|
+
...await hs.auth.createSession(userId, {
|
|
193
|
+
deviceId: body.device_id,
|
|
194
|
+
displayName: body.initial_device_display_name
|
|
195
|
+
}),
|
|
196
|
+
home_server: hs.serverName
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
if (body.type === M.LOGIN.TOKEN) {
|
|
200
|
+
if (body.token === void 0) throw errors.missingParam("Missing token");
|
|
201
|
+
const userId = await hs.auth.consumeLoginToken(body.token).catch(async (err) => {
|
|
202
|
+
await hs.audit.record({
|
|
203
|
+
action: "login_failed",
|
|
204
|
+
actor: null,
|
|
205
|
+
ip: ctx.ip,
|
|
206
|
+
outcome: "failure",
|
|
207
|
+
details: { type: M.LOGIN.TOKEN }
|
|
208
|
+
});
|
|
209
|
+
throw err;
|
|
210
|
+
});
|
|
211
|
+
const user = await hs.db.findOne("users", [{
|
|
212
|
+
field: "user_id",
|
|
213
|
+
value: userId
|
|
214
|
+
}]);
|
|
215
|
+
if (!user || user.deactivated) throw errors.userDeactivated();
|
|
216
|
+
const session = await hs.auth.createSession(userId, {
|
|
217
|
+
deviceId: body.device_id,
|
|
218
|
+
displayName: body.initial_device_display_name,
|
|
219
|
+
refreshToken: body.refresh_token === true
|
|
220
|
+
});
|
|
221
|
+
await hs.audit.record({
|
|
222
|
+
action: "login",
|
|
223
|
+
actor: userId,
|
|
224
|
+
target: session.device_id,
|
|
225
|
+
ip: ctx.ip,
|
|
226
|
+
userAgent: ctx.request.headers.get("user-agent"),
|
|
227
|
+
details: { type: M.LOGIN.TOKEN }
|
|
228
|
+
});
|
|
229
|
+
return {
|
|
230
|
+
...session,
|
|
231
|
+
home_server: hs.serverName,
|
|
232
|
+
well_known: wellKnown(hs) ?? void 0
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (body.type !== M.LOGIN.PASSWORD || !hs.userAuth.passwordLoginEnabled) throw errors.unknown("Unsupported login type");
|
|
178
236
|
const userId = await resolveLoginUser(hs, body);
|
|
179
|
-
if (
|
|
180
|
-
|
|
237
|
+
if (body.password === void 0) throw errors.missingParam("Missing password");
|
|
238
|
+
let user = await hs.db.findOne("users", [{
|
|
181
239
|
field: "user_id",
|
|
182
240
|
value: userId
|
|
183
|
-
}])
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (body.type === "m.login.token") {
|
|
193
|
-
if (body.token === void 0) throw errors.missingParam("Missing token");
|
|
194
|
-
const userId = await hs.auth.consumeLoginToken(body.token).catch(async (err) => {
|
|
241
|
+
}]);
|
|
242
|
+
const ok = await hs.userAuth.checkPassword({
|
|
243
|
+
userId,
|
|
244
|
+
localpart: userId.slice(1).split(":")[0],
|
|
245
|
+
password: body.password,
|
|
246
|
+
storedHash: user?.password_hash ?? null
|
|
247
|
+
});
|
|
248
|
+
const ua = ctx.request.headers.get("user-agent");
|
|
249
|
+
if (!ok) {
|
|
195
250
|
await hs.audit.record({
|
|
196
251
|
action: "login_failed",
|
|
197
|
-
actor:
|
|
252
|
+
actor: userId,
|
|
198
253
|
ip: ctx.ip,
|
|
254
|
+
userAgent: ua,
|
|
199
255
|
outcome: "failure",
|
|
200
|
-
details: { type:
|
|
256
|
+
details: { type: M.LOGIN.PASSWORD }
|
|
201
257
|
});
|
|
202
|
-
throw
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
258
|
+
throw errors.forbidden("Invalid username or password");
|
|
259
|
+
}
|
|
260
|
+
if (!user) {
|
|
261
|
+
if (!await hs.userAuth.autoProvision(userId)) throw errors.forbidden("Invalid username or password");
|
|
262
|
+
await createUser(hs, userId.slice(1).split(":")[0], {});
|
|
263
|
+
user = await hs.db.findOne("users", [{
|
|
264
|
+
field: "user_id",
|
|
265
|
+
value: userId
|
|
266
|
+
}]);
|
|
267
|
+
}
|
|
268
|
+
if (user.deactivated) throw errors.userDeactivated();
|
|
209
269
|
const session = await hs.auth.createSession(userId, {
|
|
210
270
|
deviceId: body.device_id,
|
|
211
271
|
displayName: body.initial_device_display_name,
|
|
@@ -216,8 +276,8 @@ function registerAccountRoutes(r) {
|
|
|
216
276
|
actor: userId,
|
|
217
277
|
target: session.device_id,
|
|
218
278
|
ip: ctx.ip,
|
|
219
|
-
userAgent:
|
|
220
|
-
details: { type:
|
|
279
|
+
userAgent: ua,
|
|
280
|
+
details: { type: M.LOGIN.PASSWORD }
|
|
221
281
|
});
|
|
222
282
|
return {
|
|
223
283
|
...session,
|
|
@@ -225,141 +285,143 @@ function registerAccountRoutes(r) {
|
|
|
225
285
|
well_known: wellKnown(hs) ?? void 0
|
|
226
286
|
};
|
|
227
287
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
288
|
+
}));
|
|
289
|
+
r.client("POST", "/refresh", procedure({
|
|
290
|
+
input: body({ refresh_token: lenient(string()) }),
|
|
291
|
+
handler: async (ctx) => {
|
|
292
|
+
const body = ctx.input;
|
|
293
|
+
if (body.refresh_token === void 0) throw errors.missingParam("Missing refresh_token");
|
|
294
|
+
const { access_token, refresh_token, expires_in_ms } = await ctx.hs.auth.refresh(body.refresh_token);
|
|
295
|
+
return {
|
|
296
|
+
access_token,
|
|
297
|
+
refresh_token,
|
|
298
|
+
expires_in_ms
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}));
|
|
302
|
+
r.client("POST", "/logout", procedure({
|
|
303
|
+
auth: "user",
|
|
304
|
+
handler: async (ctx) => {
|
|
305
|
+
const s = ctx.session;
|
|
306
|
+
await ctx.hs.auth.deleteDevice(s.userId, s.deviceId);
|
|
307
|
+
await ctx.hs.audit.record({
|
|
308
|
+
action: "logout",
|
|
309
|
+
actor: s.userId,
|
|
310
|
+
target: s.deviceId,
|
|
311
|
+
ip: ctx.ip
|
|
312
|
+
});
|
|
313
|
+
return {};
|
|
314
|
+
}
|
|
315
|
+
}));
|
|
316
|
+
r.client("POST", "/logout/all", procedure({
|
|
317
|
+
auth: "user",
|
|
318
|
+
handler: async (ctx) => {
|
|
319
|
+
const s = ctx.session;
|
|
320
|
+
await ctx.hs.auth.deleteAllDevices(s.userId);
|
|
321
|
+
await ctx.hs.audit.record({
|
|
322
|
+
action: "logout",
|
|
323
|
+
actor: s.userId,
|
|
246
324
|
ip: ctx.ip,
|
|
247
|
-
|
|
248
|
-
outcome: "failure",
|
|
249
|
-
details: { type: "m.login.password" }
|
|
325
|
+
details: { all: true }
|
|
250
326
|
});
|
|
251
|
-
|
|
327
|
+
return {};
|
|
252
328
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
329
|
+
}));
|
|
330
|
+
r.client("GET", "/account/whoami", procedure({
|
|
331
|
+
auth: "user",
|
|
332
|
+
handler: async (ctx) => {
|
|
333
|
+
const s = ctx.session;
|
|
334
|
+
return {
|
|
335
|
+
user_id: s.userId,
|
|
336
|
+
...s.deviceId ? { device_id: s.deviceId } : {},
|
|
337
|
+
is_guest: s.isGuest
|
|
338
|
+
};
|
|
260
339
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
deviceId: body.device_id,
|
|
264
|
-
displayName: body.initial_device_display_name,
|
|
265
|
-
refreshToken: body.refresh_token === true
|
|
266
|
-
});
|
|
267
|
-
await hs.audit.record({
|
|
268
|
-
action: "login",
|
|
269
|
-
actor: userId,
|
|
270
|
-
target: session.device_id,
|
|
271
|
-
ip: ctx.ip,
|
|
272
|
-
userAgent: ua,
|
|
273
|
-
details: { type: "m.login.password" }
|
|
274
|
-
});
|
|
275
|
-
return {
|
|
276
|
-
...session,
|
|
277
|
-
home_server: hs.serverName,
|
|
278
|
-
well_known: wellKnown(hs) ?? void 0
|
|
279
|
-
};
|
|
280
|
-
});
|
|
281
|
-
r.client("POST", "/refresh", { input: body({ refresh_token: lenient(z.string()) }) }, async (ctx) => {
|
|
282
|
-
const body = ctx.input;
|
|
283
|
-
if (body.refresh_token === void 0) throw errors.missingParam("Missing refresh_token");
|
|
284
|
-
const { access_token, refresh_token, expires_in_ms } = await ctx.hs.auth.refresh(body.refresh_token);
|
|
285
|
-
return {
|
|
286
|
-
access_token,
|
|
287
|
-
refresh_token,
|
|
288
|
-
expires_in_ms
|
|
289
|
-
};
|
|
290
|
-
});
|
|
291
|
-
r.client("POST", "/logout", async (ctx) => {
|
|
292
|
-
const s = await ctx.auth();
|
|
293
|
-
await ctx.hs.auth.deleteDevice(s.userId, s.deviceId);
|
|
294
|
-
await ctx.hs.audit.record({
|
|
295
|
-
action: "logout",
|
|
296
|
-
actor: s.userId,
|
|
297
|
-
target: s.deviceId,
|
|
298
|
-
ip: ctx.ip
|
|
299
|
-
});
|
|
300
|
-
return {};
|
|
301
|
-
});
|
|
302
|
-
r.client("POST", "/logout/all", async (ctx) => {
|
|
303
|
-
const s = await ctx.auth();
|
|
304
|
-
await ctx.hs.auth.deleteAllDevices(s.userId);
|
|
305
|
-
await ctx.hs.audit.record({
|
|
306
|
-
action: "logout",
|
|
307
|
-
actor: s.userId,
|
|
308
|
-
ip: ctx.ip,
|
|
309
|
-
details: { all: true }
|
|
310
|
-
});
|
|
311
|
-
return {};
|
|
312
|
-
});
|
|
313
|
-
r.client("GET", "/account/whoami", async (ctx) => {
|
|
314
|
-
const s = await ctx.auth();
|
|
315
|
-
return {
|
|
316
|
-
user_id: s.userId,
|
|
317
|
-
...s.deviceId ? { device_id: s.deviceId } : {},
|
|
318
|
-
is_guest: s.isGuest
|
|
319
|
-
};
|
|
320
|
-
});
|
|
321
|
-
r.client("GET", "/register/available", async (ctx) => {
|
|
340
|
+
}));
|
|
341
|
+
r.client("GET", "/register/available", procedure({ handler: async (ctx) => {
|
|
322
342
|
const username = ctx.query.get("username");
|
|
323
343
|
if (!username) throw errors.missingParam("Missing username");
|
|
324
344
|
await validateNewUsername(ctx.hs, username);
|
|
325
345
|
return { available: true };
|
|
326
|
-
});
|
|
327
|
-
r.client("GET", "/register", async (ctx) => ({
|
|
346
|
+
} }));
|
|
347
|
+
r.client("GET", "/register", procedure({ handler: async (ctx) => ({
|
|
328
348
|
flows: (await registrationFlows(ctx.hs)).map((stages) => ({ stages })),
|
|
329
349
|
params: {}
|
|
330
|
-
}));
|
|
331
|
-
r.client("GET", "/register/m.login.registration_token/validity",
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
r.client("POST", "/register", { input: registerBody }, async (ctx) => {
|
|
338
|
-
const hs = ctx.hs;
|
|
339
|
-
const body = ctx.input;
|
|
340
|
-
const kind = ctx.query.get("kind") ?? "user";
|
|
341
|
-
if (kind === "guest") {
|
|
342
|
-
if (!hs.userAuth.guestAccessEnabled) throw errors.forbidden("Guest access is disabled");
|
|
343
|
-
const userId = await createUser(hs, await generateLocalpart(hs), { guest: true });
|
|
344
|
-
return {
|
|
345
|
-
...await hs.auth.createSession(userId, { displayName: body.initial_device_display_name }),
|
|
346
|
-
home_server: hs.serverName
|
|
347
|
-
};
|
|
350
|
+
}) }));
|
|
351
|
+
r.client("GET", "/register/m.login.registration_token/validity", procedure({
|
|
352
|
+
rateLimit: "register",
|
|
353
|
+
handler: async (ctx) => {
|
|
354
|
+
const token = ctx.query.get("token");
|
|
355
|
+
if (!token) throw errors.missingParam("Missing token");
|
|
356
|
+
return { valid: await registrationTokenValid(ctx.hs, token) };
|
|
348
357
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
if (
|
|
357
|
-
|
|
358
|
+
}), ["v1"]);
|
|
359
|
+
r.client("POST", "/register", procedure({
|
|
360
|
+
input: registerBody,
|
|
361
|
+
handler: async (ctx) => {
|
|
362
|
+
const hs = ctx.hs;
|
|
363
|
+
const body = ctx.input;
|
|
364
|
+
const kind = ctx.query.get("kind") ?? "user";
|
|
365
|
+
if (kind === "guest") {
|
|
366
|
+
if (!hs.userAuth.guestAccessEnabled) throw errors.forbidden("Guest access is disabled");
|
|
367
|
+
const userId = await createUser(hs, await generateLocalpart(hs), { guest: true });
|
|
368
|
+
return {
|
|
369
|
+
...await hs.auth.createSession(userId, { displayName: body.initial_device_display_name }),
|
|
370
|
+
home_server: hs.serverName
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
if (kind !== "user") throw errors.invalidParam("Unknown kind");
|
|
374
|
+
if (body.type === M.LOGIN.APPLICATION_SERVICE || body.auth?.type === M.LOGIN.APPLICATION_SERVICE) {
|
|
375
|
+
const s = await ctx.auth();
|
|
376
|
+
if (!s.appservice) throw errors.forbidden("An application service token is required");
|
|
377
|
+
if (body.username === void 0) throw errors.missingParam("Missing username");
|
|
378
|
+
const localpart = body.username.toLowerCase();
|
|
379
|
+
const userId = `@${localpart}:${hs.serverName}`;
|
|
380
|
+
if (!hs.appservices.isInterestedUser(s.appservice, userId)) throw errors.exclusive("User is not in the application service's namespace");
|
|
381
|
+
if (!LOCALPART_RE.test(localpart)) throw errors.invalidUsername();
|
|
382
|
+
if (await hs.db.findOne("users", [{
|
|
383
|
+
field: "user_id",
|
|
384
|
+
value: userId
|
|
385
|
+
}])) throw errors.userInUse();
|
|
386
|
+
await createUser(hs, localpart, {});
|
|
387
|
+
if (body.inhibit_login === true) return {
|
|
388
|
+
user_id: userId,
|
|
389
|
+
home_server: hs.serverName
|
|
390
|
+
};
|
|
391
|
+
return {
|
|
392
|
+
...await hs.auth.createSession(userId, {
|
|
393
|
+
deviceId: body.device_id,
|
|
394
|
+
displayName: body.initial_device_display_name
|
|
395
|
+
}),
|
|
396
|
+
home_server: hs.serverName
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
if (ctx.accessToken() && hs.appservices.byToken(ctx.accessToken())) throw errors.missingParam("Application services must use type m.login.application_service");
|
|
400
|
+
rejectIfDelegated(hs);
|
|
401
|
+
if (!await hs.registrationAllowed()) throw errors.forbidden("Registration is disabled");
|
|
402
|
+
await ctx.rateLimit("register");
|
|
403
|
+
const localpart = body.username !== void 0 ? await validateNewUsername(hs, body.username.toLowerCase()) : await generateLocalpart(hs);
|
|
404
|
+
if (hs.appservices.exclusiveUserOwner(`@${localpart}:${hs.serverName}`)) throw errors.exclusive("This user ID is reserved by an application service");
|
|
405
|
+
if (body.password !== void 0) {
|
|
406
|
+
const problem = await hs.userAuth.validatePassword(body.password);
|
|
407
|
+
if (problem) throw new MatrixError(400, "M_WEAK_PASSWORD", problem);
|
|
408
|
+
}
|
|
409
|
+
const flows = await registrationFlows(hs);
|
|
410
|
+
const { threepids } = await requireUia(hs, body, flows, null);
|
|
411
|
+
await consumeRegistrationToken(hs, body);
|
|
358
412
|
if (await hs.db.findOne("users", [{
|
|
359
|
-
field: "
|
|
360
|
-
value:
|
|
413
|
+
field: "localpart",
|
|
414
|
+
value: localpart
|
|
361
415
|
}])) throw errors.userInUse();
|
|
362
|
-
await createUser(hs, localpart, {});
|
|
416
|
+
const userId = await createUser(hs, localpart, { password: body.password });
|
|
417
|
+
for (const t of threepids) await hs.threepids.add(userId, t).catch((err) => hs.logger.warn("Could not attach", t.medium, "to", userId, err));
|
|
418
|
+
await hs.audit.record({
|
|
419
|
+
action: "register",
|
|
420
|
+
actor: userId,
|
|
421
|
+
ip: ctx.ip,
|
|
422
|
+
userAgent: ctx.request.headers.get("user-agent"),
|
|
423
|
+
details: { token: body.auth?.type === M.LOGIN.REGISTRATION_TOKEN }
|
|
424
|
+
});
|
|
363
425
|
if (body.inhibit_login === true) return {
|
|
364
426
|
user_id: userId,
|
|
365
427
|
home_server: hs.serverName
|
|
@@ -367,166 +429,138 @@ function registerAccountRoutes(r) {
|
|
|
367
429
|
return {
|
|
368
430
|
...await hs.auth.createSession(userId, {
|
|
369
431
|
deviceId: body.device_id,
|
|
370
|
-
displayName: body.initial_device_display_name
|
|
432
|
+
displayName: body.initial_device_display_name,
|
|
433
|
+
refreshToken: body.refresh_token === true
|
|
371
434
|
}),
|
|
372
435
|
home_server: hs.serverName
|
|
373
436
|
};
|
|
374
437
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
438
|
+
}));
|
|
439
|
+
r.client("POST", "/account/password", procedure({
|
|
440
|
+
input: passwordBody,
|
|
441
|
+
handler: async (ctx) => {
|
|
442
|
+
const hs = ctx.hs;
|
|
443
|
+
rejectIfDelegated(hs);
|
|
444
|
+
const body = ctx.input;
|
|
445
|
+
let s;
|
|
446
|
+
if (ctx.accessToken()) {
|
|
447
|
+
const session = await ctx.auth();
|
|
448
|
+
await requireUia(hs, body, [[M.LOGIN.PASSWORD]], session.userId);
|
|
449
|
+
s = {
|
|
450
|
+
userId: session.userId,
|
|
451
|
+
deviceId: session.deviceId
|
|
452
|
+
};
|
|
453
|
+
} else {
|
|
454
|
+
const { threepids } = await requireUia(hs, body, [[M.LOGIN.EMAIL.IDENTITY], [M.LOGIN.MSISDN]], null);
|
|
455
|
+
const owner = threepids.length ? await hs.threepids.userFor(threepids[0].medium, threepids[0].address) : null;
|
|
456
|
+
if (!owner) throw new MatrixError(400, "M_THREEPID_NOT_FOUND", "No account uses this address");
|
|
457
|
+
s = {
|
|
458
|
+
userId: owner,
|
|
459
|
+
deviceId: null
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
if (!body.new_password) throw errors.missingParam("Missing new_password");
|
|
463
|
+
const problem = await hs.userAuth.validatePassword(body.new_password);
|
|
383
464
|
if (problem) throw new MatrixError(400, "M_WEAK_PASSWORD", problem);
|
|
465
|
+
await hs.db.update("users", [{
|
|
466
|
+
field: "user_id",
|
|
467
|
+
value: s.userId
|
|
468
|
+
}], { password_hash: await hs.userAuth.hashPassword(body.new_password) });
|
|
469
|
+
await hs.audit.record({
|
|
470
|
+
action: "password_change",
|
|
471
|
+
actor: s.userId,
|
|
472
|
+
ip: ctx.ip,
|
|
473
|
+
details: { logout_devices: body.logout_devices !== false }
|
|
474
|
+
});
|
|
475
|
+
if (body.logout_devices !== false) await hs.auth.deleteAllDevices(s.userId, s.deviceId ?? void 0);
|
|
476
|
+
return {};
|
|
384
477
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
s = {
|
|
423
|
-
userId: session.userId,
|
|
424
|
-
deviceId: session.deviceId
|
|
425
|
-
};
|
|
426
|
-
} else {
|
|
427
|
-
const { threepids } = await requireUia(hs, body, [["m.login.email.identity"], ["m.login.msisdn"]], null);
|
|
428
|
-
const owner = threepids.length ? await hs.threepids.userFor(threepids[0].medium, threepids[0].address) : null;
|
|
429
|
-
if (!owner) throw new MatrixError(400, "M_THREEPID_NOT_FOUND", "No account uses this address");
|
|
430
|
-
s = {
|
|
431
|
-
userId: owner,
|
|
432
|
-
deviceId: null
|
|
433
|
-
};
|
|
478
|
+
}));
|
|
479
|
+
r.client("POST", "/account/deactivate", procedure({
|
|
480
|
+
input: body({}),
|
|
481
|
+
handler: async (ctx) => {
|
|
482
|
+
const hs = ctx.hs;
|
|
483
|
+
rejectIfDelegated(hs);
|
|
484
|
+
const s = await ctx.auth();
|
|
485
|
+
await requireUia(hs, ctx.input, [[M.LOGIN.PASSWORD]], s.userId);
|
|
486
|
+
for (const membership of [
|
|
487
|
+
"join",
|
|
488
|
+
"invite",
|
|
489
|
+
"knock"
|
|
490
|
+
]) for (const roomId of await hs.rooms.roomsForUser(s.userId, membership)) try {
|
|
491
|
+
await hs.rooms.setMembership(roomId, s.userId, s.userId, "leave");
|
|
492
|
+
} catch {}
|
|
493
|
+
await hs.auth.deleteAllDevices(s.userId);
|
|
494
|
+
await hs.db.update("users", [{
|
|
495
|
+
field: "user_id",
|
|
496
|
+
value: s.userId
|
|
497
|
+
}], {
|
|
498
|
+
deactivated: true,
|
|
499
|
+
password_hash: null,
|
|
500
|
+
displayname: null,
|
|
501
|
+
avatar_url: null
|
|
502
|
+
});
|
|
503
|
+
await hs.db.delete("profile_fields", [{
|
|
504
|
+
field: "user_id",
|
|
505
|
+
value: s.userId
|
|
506
|
+
}]);
|
|
507
|
+
const unbind = await hs.threepids.removeAll(s.userId);
|
|
508
|
+
await hs.audit.record({
|
|
509
|
+
action: "deactivate",
|
|
510
|
+
actor: s.userId,
|
|
511
|
+
target: s.userId,
|
|
512
|
+
ip: ctx.ip
|
|
513
|
+
});
|
|
514
|
+
return { id_server_unbind_result: unbind };
|
|
434
515
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if (problem) throw new MatrixError(400, "M_WEAK_PASSWORD", problem);
|
|
438
|
-
await hs.db.update("users", [{
|
|
439
|
-
field: "user_id",
|
|
440
|
-
value: s.userId
|
|
441
|
-
}], { password_hash: await hs.userAuth.hashPassword(body.new_password) });
|
|
442
|
-
await hs.audit.record({
|
|
443
|
-
action: "password_change",
|
|
444
|
-
actor: s.userId,
|
|
445
|
-
ip: ctx.ip,
|
|
446
|
-
details: { logout_devices: body.logout_devices !== false }
|
|
447
|
-
});
|
|
448
|
-
if (body.logout_devices !== false) await hs.auth.deleteAllDevices(s.userId, s.deviceId ?? void 0);
|
|
449
|
-
return {};
|
|
450
|
-
});
|
|
451
|
-
r.client("POST", "/account/deactivate", { input: body({}) }, async (ctx) => {
|
|
452
|
-
const hs = ctx.hs;
|
|
453
|
-
rejectIfDelegated(hs);
|
|
454
|
-
const s = await ctx.auth();
|
|
455
|
-
await requireUia(hs, ctx.input, [["m.login.password"]], s.userId);
|
|
456
|
-
for (const membership of [
|
|
457
|
-
"join",
|
|
458
|
-
"invite",
|
|
459
|
-
"knock"
|
|
460
|
-
]) for (const roomId of await hs.rooms.roomsForUser(s.userId, membership)) try {
|
|
461
|
-
await hs.rooms.setMembership(roomId, s.userId, s.userId, "leave");
|
|
462
|
-
} catch {}
|
|
463
|
-
await hs.auth.deleteAllDevices(s.userId);
|
|
464
|
-
await hs.db.update("users", [{
|
|
465
|
-
field: "user_id",
|
|
466
|
-
value: s.userId
|
|
467
|
-
}], {
|
|
468
|
-
deactivated: true,
|
|
469
|
-
password_hash: null,
|
|
470
|
-
displayname: null,
|
|
471
|
-
avatar_url: null
|
|
472
|
-
});
|
|
473
|
-
await hs.db.delete("profile_fields", [{
|
|
474
|
-
field: "user_id",
|
|
475
|
-
value: s.userId
|
|
476
|
-
}]);
|
|
477
|
-
const unbind = await hs.threepids.removeAll(s.userId);
|
|
478
|
-
await hs.audit.record({
|
|
479
|
-
action: "deactivate",
|
|
480
|
-
actor: s.userId,
|
|
481
|
-
target: s.userId,
|
|
482
|
-
ip: ctx.ip
|
|
483
|
-
});
|
|
484
|
-
return { id_server_unbind_result: unbind };
|
|
485
|
-
});
|
|
486
|
-
r.get("/_synapse/admin/v1/register", async (ctx) => {
|
|
516
|
+
}));
|
|
517
|
+
r.get("/_synapse/admin/v1/register", procedure({ handler: async (ctx) => {
|
|
487
518
|
if (!ctx.hs.config.registrationSharedSecret) throw errors.unrecognized();
|
|
488
519
|
const nonce = randomString(32);
|
|
489
520
|
await ctx.hs.cluster.set(`regnonce:${nonce}`, true, 6e4);
|
|
490
521
|
return { nonce };
|
|
491
|
-
});
|
|
492
|
-
r.post("/_synapse/admin/v1/register",
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
522
|
+
} }));
|
|
523
|
+
r.post("/_synapse/admin/v1/register", procedure({
|
|
524
|
+
input: sharedSecretRegisterBody,
|
|
525
|
+
handler: async (ctx) => {
|
|
526
|
+
const hs = ctx.hs;
|
|
527
|
+
const secret = hs.config.registrationSharedSecret;
|
|
528
|
+
if (!secret) throw errors.unrecognized();
|
|
529
|
+
const body = ctx.input;
|
|
530
|
+
if (body.nonce === void 0 || !await hs.cluster.get(`regnonce:${body.nonce}`)) throw errors.unknown("Unrecognised nonce");
|
|
531
|
+
await hs.cluster.delete(`regnonce:${body.nonce}`);
|
|
532
|
+
const admin = body.admin === true;
|
|
533
|
+
const expected = createHmac("sha1", secret).update(`${body.nonce}\u0000${body.username}\u0000${body.password}\u0000${admin ? "admin" : "notadmin"}`).digest("hex");
|
|
534
|
+
const given = Buffer.from(body.mac ?? "", "utf8");
|
|
535
|
+
if (given.length !== expected.length || !timingSafeEqual(given, Buffer.from(expected, "utf8"))) throw errors.forbidden("HMAC incorrect");
|
|
536
|
+
const userId = await createUser(hs, await validateNewUsername(hs, body.username.toLowerCase()), {
|
|
537
|
+
password: body.password,
|
|
538
|
+
admin,
|
|
539
|
+
displayname: body.displayname
|
|
540
|
+
});
|
|
541
|
+
const session = await hs.auth.createSession(userId);
|
|
542
|
+
await hs.audit.record({
|
|
543
|
+
action: "register",
|
|
544
|
+
actor: "server",
|
|
545
|
+
target: userId,
|
|
546
|
+
ip: ctx.ip,
|
|
547
|
+
details: {
|
|
548
|
+
shared_secret: true,
|
|
549
|
+
admin
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
return {
|
|
553
|
+
...session,
|
|
554
|
+
home_server: hs.serverName
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
}));
|
|
524
558
|
}
|
|
525
559
|
async function registrationFlows(hs) {
|
|
526
|
-
if (hs.adminRoom?.options.firstUserCanRegister && await hs.adminRoom.noUsersYet()) return [[
|
|
527
|
-
const base = await hs.registrationMode() === "token" ? [
|
|
560
|
+
if (hs.adminRoom?.options.firstUserCanRegister && await hs.adminRoom.noUsersYet()) return [[M.LOGIN.DUMMY]];
|
|
561
|
+
const base = await hs.registrationMode() === "token" ? [M.LOGIN.REGISTRATION_TOKEN] : [M.LOGIN.DUMMY];
|
|
528
562
|
if (!hs.threepids.canValidate("email")) return [base];
|
|
529
|
-
const withEmail = [...base.filter((s) => s !==
|
|
563
|
+
const withEmail = [...base.filter((s) => s !== M.LOGIN.DUMMY), M.LOGIN.EMAIL.IDENTITY];
|
|
530
564
|
return hs.config.threepid.requireEmailForRegistration ? [withEmail] : [base, withEmail];
|
|
531
565
|
}
|
|
532
566
|
async function registrationTokenValid(hs, token) {
|
|
@@ -539,7 +573,7 @@ async function registrationTokenValid(hs, token) {
|
|
|
539
573
|
return row.uses_allowed === null || row.completed < row.uses_allowed;
|
|
540
574
|
}
|
|
541
575
|
async function consumeRegistrationToken(hs, body) {
|
|
542
|
-
if (body.auth?.type !==
|
|
576
|
+
if (body.auth?.type !== M.LOGIN.REGISTRATION_TOKEN) return;
|
|
543
577
|
const token = body.auth?.token;
|
|
544
578
|
const row = await hs.db.findOne("registration_tokens", [{
|
|
545
579
|
field: "token",
|
|
@@ -552,8 +586,8 @@ async function consumeRegistrationToken(hs, body) {
|
|
|
552
586
|
}
|
|
553
587
|
function wellKnown(hs) {
|
|
554
588
|
if (!hs.config.publicBaseUrl) return null;
|
|
555
|
-
const wk = {
|
|
556
|
-
if (hs.config.rtc.transports?.length) wk[
|
|
589
|
+
const wk = { [M.HOMESERVER]: { base_url: hs.config.publicBaseUrl } };
|
|
590
|
+
if (hs.config.rtc.transports?.length) wk[UNSTABLE.ORG.MATRIX.MSC4143.RTC_FOCI] = hs.config.rtc.transports;
|
|
557
591
|
return wk;
|
|
558
592
|
}
|
|
559
593
|
//#endregion
|