matrix-nodejs-homeserver 0.3.4 → 0.3.6
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/AUDITBOT.md +67 -0
- package/README.md +55 -9
- package/dist/api/account.js +109 -64
- package/dist/api/admin.js +14 -0
- package/dist/api/federation.js +1 -1
- package/dist/api/index.js +2 -0
- package/dist/api/misc.js +3 -3
- package/dist/api/oauthserver.js +620 -0
- package/dist/api/sync.js +1 -1
- package/dist/api/threepid.js +1 -1
- package/dist/api/uia.js +20 -8
- package/dist/api/user.js +6 -3
- package/dist/auditbot/bot.d.ts +37 -0
- package/dist/auditbot/bot.js +208 -0
- package/dist/auditbot/client.d.ts +41 -0
- package/dist/auditbot/client.js +156 -0
- package/dist/auditbot/index.d.ts +4 -0
- package/dist/auditbot/index.js +4 -0
- package/dist/auditbot/sinks.d.ts +37 -0
- package/dist/auditbot/sinks.js +33 -0
- package/dist/core/admin.d.ts +6 -0
- package/dist/core/admin.js +25 -0
- package/dist/core/auth.d.ts +33 -2
- package/dist/core/auth.js +84 -21
- package/dist/core/oauth.js +2 -1
- package/dist/core/oauthserver.d.ts +141 -0
- package/dist/core/oauthserver.js +603 -0
- package/dist/core/ratelimit.d.ts +1 -1
- package/dist/core/ratelimit.js +4 -0
- package/dist/core/sso.d.ts +51 -15
- package/dist/core/sso.js +86 -26
- package/dist/homeserver.d.ts +20 -9
- package/dist/homeserver.js +17 -2
- package/dist/index.d.ts +15 -14
- package/dist/index.js +2 -1
- package/dist/storage/schema.js +20 -2
- package/dist/vendor/zod/v4/classic/errors.js +1 -1
- package/dist/vendor/zod/v4/classic/parse.js +1 -1
- package/dist/vendor/zod/v4/classic/schemas.js +1 -1
- package/dist/vendor/zod/v4/core/api.js +1 -1
- package/dist/vendor/zod/v4/core/checks.js +1 -1
- package/dist/vendor/zod/v4/core/core.js +1 -1
- package/dist/vendor/zod/v4/core/doc.js +1 -1
- package/dist/vendor/zod/v4/core/errors.js +1 -1
- package/dist/vendor/zod/v4/core/json-schema-processors.js +1 -1
- package/dist/vendor/zod/v4/core/memoizer.js +1 -1
- package/dist/vendor/zod/v4/core/parse.js +1 -1
- package/dist/vendor/zod/v4/core/regexes.js +1 -1
- package/dist/vendor/zod/v4/core/registries.js +1 -1
- package/dist/vendor/zod/v4/core/schemas.js +1 -1
- package/dist/vendor/zod/v4/core/to-json-schema.js +1 -1
- package/dist/vendor/zod/v4/core/util.js +1 -1
- package/dist/vendor/zod/v4/core/versions.js +1 -1
- package/dist/vendor/zod/v4/locales/en.js +1 -1
- package/package.json +28 -17
package/dist/core/sso.d.ts
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import { Homeserver } from "../homeserver.js";
|
|
2
2
|
//#region src/core/sso.d.ts
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* An upstream identity provider: OpenID Connect (found through `issuer`), or plain OAuth 2.0 with
|
|
5
|
+
* a user info endpoint (`endpoints`, e.g. GitHub; see `githubProvider`).
|
|
6
|
+
*/
|
|
4
7
|
export interface SsoProvider {
|
|
5
8
|
/** Stable identifier, used in URLs and stored with linked accounts. */
|
|
6
9
|
id: string;
|
|
7
10
|
name: string;
|
|
8
|
-
issuer
|
|
11
|
+
/** OpenID Connect issuer; its discovery document gives the endpoints. Required without `endpoints`. */
|
|
12
|
+
issuer?: string;
|
|
13
|
+
/** Endpoints of a provider without OpenID Connect discovery. */
|
|
14
|
+
endpoints?: {
|
|
15
|
+
authorization: string;
|
|
16
|
+
token: string;
|
|
17
|
+
userinfo?: string;
|
|
18
|
+
};
|
|
9
19
|
clientId: string;
|
|
10
20
|
clientSecret?: string;
|
|
11
|
-
/** Default: ["openid", "profile", "email"]
|
|
21
|
+
/** Default: ["openid", "profile", "email"] with `issuer`, none with `endpoints`. */
|
|
12
22
|
scopes?: string[];
|
|
23
|
+
/** Claim that identifies the user at the provider. Default: "sub". */
|
|
24
|
+
subjectClaim?: string;
|
|
13
25
|
icon?: string;
|
|
14
26
|
brand?: string;
|
|
15
27
|
/**
|
|
@@ -35,26 +47,50 @@ export interface SsoOptions {
|
|
|
35
47
|
*/
|
|
36
48
|
clientAllowlist?: string[];
|
|
37
49
|
}
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
/**
|
|
51
|
+
* What a login through a provider is for: a client of the legacy API (`m.login.sso`, which gets a
|
|
52
|
+
* login token at `redirectUrl`), or the server's own web pages (OAuth 2.0 API), which continue
|
|
53
|
+
* at `next` in the browser session `sid`.
|
|
54
|
+
*/
|
|
55
|
+
export type SsoTarget = {
|
|
56
|
+
kind: "client";
|
|
57
|
+
redirectUrl: string;
|
|
58
|
+
} | {
|
|
59
|
+
kind: "web";
|
|
60
|
+
next: string;
|
|
61
|
+
sid: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* GitHub as an identity provider (an OAuth app or GitHub app; its callback URL is
|
|
65
|
+
* `{publicBaseUrl}/_matrix/client/v3/login/sso/callback/github`). Users are matched by their
|
|
66
|
+
* numeric GitHub ID, so renaming the GitHub account keeps the Matrix account; new accounts take
|
|
67
|
+
* the GitHub login as their localpart.
|
|
68
|
+
*/
|
|
69
|
+
export declare function githubProvider(opts: {
|
|
70
|
+
clientId: string;
|
|
71
|
+
clientSecret: string;
|
|
72
|
+
id?: string;
|
|
73
|
+
name?: string;
|
|
74
|
+
allowRegistration?: boolean;
|
|
75
|
+
mapUser?: SsoProvider["mapUser"];
|
|
76
|
+
}): SsoProvider;
|
|
77
|
+
/** Upstream login (authorization code + PKCE) for the legacy `m.login.sso` and the OAuth 2.0 API's web pages. */
|
|
46
78
|
export declare class SsoService {
|
|
47
79
|
private hs;
|
|
48
80
|
readonly providers: SsoProvider[];
|
|
49
|
-
|
|
81
|
+
readonly clientAllowlist: string[];
|
|
50
82
|
private discovery;
|
|
51
83
|
constructor(hs: Homeserver, options: SsoOptions);
|
|
52
84
|
private callbackUrl;
|
|
53
85
|
private discover;
|
|
86
|
+
private scopes;
|
|
54
87
|
/** URL to send the user's browser to. */
|
|
55
|
-
startLogin(providerId: string | null,
|
|
56
|
-
/** Handle the provider's redirect
|
|
57
|
-
finishLogin(providerId: string, params: URLSearchParams): Promise<
|
|
88
|
+
startLogin(providerId: string | null, target: SsoTarget): Promise<string>;
|
|
89
|
+
/** Handle the provider's redirect: the user who logged in, and what the login was for. */
|
|
90
|
+
finishLogin(providerId: string, params: URLSearchParams): Promise<{
|
|
91
|
+
userId: string;
|
|
92
|
+
target: SsoTarget;
|
|
93
|
+
}>;
|
|
58
94
|
private resolveUser;
|
|
59
95
|
}
|
|
60
96
|
//#endregion
|
package/dist/core/sso.js
CHANGED
|
@@ -39,7 +39,35 @@ function decodeJwtPayload(jwt) {
|
|
|
39
39
|
if (!part) throw new Error("Malformed ID token");
|
|
40
40
|
return JSON.parse(Buffer.from(part, "base64url").toString("utf8"));
|
|
41
41
|
}
|
|
42
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* GitHub as an identity provider (an OAuth app or GitHub app; its callback URL is
|
|
44
|
+
* `{publicBaseUrl}/_matrix/client/v3/login/sso/callback/github`). Users are matched by their
|
|
45
|
+
* numeric GitHub ID, so renaming the GitHub account keeps the Matrix account; new accounts take
|
|
46
|
+
* the GitHub login as their localpart.
|
|
47
|
+
*/
|
|
48
|
+
function githubProvider(opts) {
|
|
49
|
+
return {
|
|
50
|
+
id: opts.id ?? "github",
|
|
51
|
+
name: opts.name ?? "GitHub",
|
|
52
|
+
brand: "github",
|
|
53
|
+
clientId: opts.clientId,
|
|
54
|
+
clientSecret: opts.clientSecret,
|
|
55
|
+
endpoints: {
|
|
56
|
+
authorization: "https://github.com/login/oauth/authorize",
|
|
57
|
+
token: "https://github.com/login/oauth/access_token",
|
|
58
|
+
userinfo: "https://api.github.com/user"
|
|
59
|
+
},
|
|
60
|
+
scopes: ["read:user"],
|
|
61
|
+
subjectClaim: "id",
|
|
62
|
+
mapUser: opts.mapUser ?? ((c) => ({
|
|
63
|
+
localpart: sanitizeLocalpart(String(c.login ?? c.id)),
|
|
64
|
+
displayname: c.name ?? c.login
|
|
65
|
+
})),
|
|
66
|
+
...opts.allowRegistration !== void 0 ? { allowRegistration: opts.allowRegistration } : {}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const USER_AGENT = "matrix-nodejs-homeserver";
|
|
70
|
+
/** Upstream login (authorization code + PKCE) for the legacy `m.login.sso` and the OAuth 2.0 API's web pages. */
|
|
43
71
|
var SsoService = class {
|
|
44
72
|
hs;
|
|
45
73
|
providers;
|
|
@@ -56,8 +84,18 @@ var SsoService = class {
|
|
|
56
84
|
return `${base.replace(/\/$/, "")}/_matrix/client/v3/login/sso/callback/${encodeURIComponent(provider.id)}`;
|
|
57
85
|
}
|
|
58
86
|
async discover(provider) {
|
|
87
|
+
if (provider.endpoints) {
|
|
88
|
+
const e = provider.endpoints;
|
|
89
|
+
return {
|
|
90
|
+
issuer: provider.issuer,
|
|
91
|
+
authorization_endpoint: e.authorization,
|
|
92
|
+
token_endpoint: e.token,
|
|
93
|
+
userinfo_endpoint: e.userinfo
|
|
94
|
+
};
|
|
95
|
+
}
|
|
59
96
|
const cached = this.discovery.get(provider.id);
|
|
60
97
|
if (cached) return cached;
|
|
98
|
+
if (!provider.issuer) throw new Error(`Identity provider ${provider.id} needs an issuer or endpoints`);
|
|
61
99
|
const res = await this.hs.fetch(`${provider.issuer.replace(/\/$/, "")}/.well-known/openid-configuration`, { signal: AbortSignal.timeout(1e4) });
|
|
62
100
|
if (!res.ok) throw new Error(`OIDC discovery for ${provider.id} failed with ${res.status}`);
|
|
63
101
|
const meta = await res.json();
|
|
@@ -65,8 +103,15 @@ var SsoService = class {
|
|
|
65
103
|
this.discovery.set(provider.id, meta);
|
|
66
104
|
return meta;
|
|
67
105
|
}
|
|
106
|
+
scopes(provider) {
|
|
107
|
+
return provider.scopes ?? (provider.endpoints ? [] : [
|
|
108
|
+
"openid",
|
|
109
|
+
"profile",
|
|
110
|
+
"email"
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
68
113
|
/** URL to send the user's browser to. */
|
|
69
|
-
async startLogin(providerId,
|
|
114
|
+
async startLogin(providerId, target) {
|
|
70
115
|
const provider = providerId ? this.providers.find((p) => p.id === providerId) : this.providers[0];
|
|
71
116
|
if (!provider) throw errors.notFound("Unknown identity provider");
|
|
72
117
|
const meta = await this.discover(provider);
|
|
@@ -75,7 +120,7 @@ var SsoService = class {
|
|
|
75
120
|
const nonce = randomToken(16);
|
|
76
121
|
await this.hs.cluster.set(`sso:${state}`, {
|
|
77
122
|
providerId: provider.id,
|
|
78
|
-
|
|
123
|
+
target,
|
|
79
124
|
verifier,
|
|
80
125
|
nonce,
|
|
81
126
|
expires: Date.now() + 6e5
|
|
@@ -84,18 +129,15 @@ var SsoService = class {
|
|
|
84
129
|
url.searchParams.set("response_type", "code");
|
|
85
130
|
url.searchParams.set("client_id", provider.clientId);
|
|
86
131
|
url.searchParams.set("redirect_uri", this.callbackUrl(provider));
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
"profile",
|
|
90
|
-
"email"
|
|
91
|
-
]).join(" "));
|
|
132
|
+
const scopes = this.scopes(provider);
|
|
133
|
+
if (scopes.length) url.searchParams.set("scope", scopes.join(" "));
|
|
92
134
|
url.searchParams.set("state", state);
|
|
93
135
|
url.searchParams.set("nonce", nonce);
|
|
94
136
|
url.searchParams.set("code_challenge", urlSafeBase64(createHash("sha256").update(verifier).digest()));
|
|
95
137
|
url.searchParams.set("code_challenge_method", "S256");
|
|
96
138
|
return url.toString();
|
|
97
139
|
}
|
|
98
|
-
/** Handle the provider's redirect
|
|
140
|
+
/** Handle the provider's redirect: the user who logged in, and what the login was for. */
|
|
99
141
|
async finishLogin(providerId, params) {
|
|
100
142
|
const state = params.get("state") ?? "";
|
|
101
143
|
const pending = await this.hs.cluster.get(`sso:${state}`);
|
|
@@ -115,7 +157,8 @@ var SsoService = class {
|
|
|
115
157
|
});
|
|
116
158
|
const headers = {
|
|
117
159
|
"content-type": "application/x-www-form-urlencoded",
|
|
118
|
-
accept: "application/json"
|
|
160
|
+
accept: "application/json",
|
|
161
|
+
"user-agent": USER_AGENT
|
|
119
162
|
};
|
|
120
163
|
if (provider.clientSecret) headers.authorization = `Basic ${Buffer.from(`${encodeURIComponent(provider.clientId)}:${encodeURIComponent(provider.clientSecret)}`).toString("base64")}`;
|
|
121
164
|
const res = await this.hs.fetch(meta.token_endpoint, {
|
|
@@ -126,8 +169,9 @@ var SsoService = class {
|
|
|
126
169
|
});
|
|
127
170
|
if (!res.ok) throw errors.forbidden("Code exchange failed");
|
|
128
171
|
const tokens = await res.json();
|
|
172
|
+
if (typeof tokens.error === "string") throw errors.forbidden("Code exchange failed");
|
|
129
173
|
let claims = {};
|
|
130
|
-
if ((provider
|
|
174
|
+
if (this.scopes(provider).includes("openid") && typeof tokens.id_token !== "string") throw errors.forbidden("The provider returned no ID token");
|
|
131
175
|
if (typeof tokens.id_token === "string") {
|
|
132
176
|
claims = decodeJwtPayload(tokens.id_token);
|
|
133
177
|
const aud = Array.isArray(claims.aud) ? claims.aud : [claims.aud];
|
|
@@ -137,7 +181,11 @@ var SsoService = class {
|
|
|
137
181
|
}
|
|
138
182
|
if (meta.userinfo_endpoint && typeof tokens.access_token === "string") {
|
|
139
183
|
const info = await this.hs.fetch(meta.userinfo_endpoint, {
|
|
140
|
-
headers: {
|
|
184
|
+
headers: {
|
|
185
|
+
authorization: `Bearer ${tokens.access_token}`,
|
|
186
|
+
accept: "application/json",
|
|
187
|
+
"user-agent": USER_AGENT
|
|
188
|
+
},
|
|
141
189
|
signal: AbortSignal.timeout(1e4)
|
|
142
190
|
});
|
|
143
191
|
if (info.ok) {
|
|
@@ -149,37 +197,35 @@ var SsoService = class {
|
|
|
149
197
|
};
|
|
150
198
|
}
|
|
151
199
|
}
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
200
|
+
const rawSubject = claims[provider.subjectClaim ?? "sub"];
|
|
201
|
+
const subject = typeof rawSubject === "number" ? String(rawSubject) : rawSubject;
|
|
202
|
+
if (typeof subject !== "string" || !subject) throw errors.forbidden("Provider did not return a subject");
|
|
203
|
+
const userId = await this.resolveUser(provider, subject, claims);
|
|
155
204
|
await this.hs.audit.record({
|
|
156
205
|
action: "sso_login",
|
|
157
206
|
actor: userId,
|
|
158
207
|
details: {
|
|
159
208
|
idp: provider.id,
|
|
160
|
-
subject
|
|
209
|
+
subject
|
|
161
210
|
}
|
|
162
211
|
});
|
|
163
|
-
const redirect = new URL(pending.redirectUrl);
|
|
164
|
-
redirect.searchParams.set("loginToken", loginToken);
|
|
165
212
|
return {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
client: redirect.host || redirect.protocol.replace(/:$/, "")
|
|
213
|
+
userId,
|
|
214
|
+
target: pending.target
|
|
169
215
|
};
|
|
170
216
|
}
|
|
171
|
-
async resolveUser(provider, claims) {
|
|
217
|
+
async resolveUser(provider, subject, claims) {
|
|
172
218
|
const link = await this.hs.db.findOne("sso_identities", [{
|
|
173
219
|
field: "idp_id",
|
|
174
220
|
value: provider.id
|
|
175
221
|
}, {
|
|
176
222
|
field: "subject",
|
|
177
|
-
value:
|
|
223
|
+
value: subject
|
|
178
224
|
}]);
|
|
179
225
|
if (link) return link.user_id;
|
|
180
226
|
if (provider.allowRegistration === false) throw errors.forbidden("No account is linked to this identity");
|
|
181
227
|
const mapped = provider.mapUser ? await provider.mapUser(claims) : {
|
|
182
|
-
localpart: sanitizeLocalpart(claims.preferred_username ??
|
|
228
|
+
localpart: sanitizeLocalpart(claims.preferred_username ?? subject),
|
|
183
229
|
displayname: claims.name
|
|
184
230
|
};
|
|
185
231
|
if (!mapped || !LOCALPART_RE.test(mapped.localpart)) throw errors.forbidden("Could not map identity to a Matrix user");
|
|
@@ -191,11 +237,25 @@ var SsoService = class {
|
|
|
191
237
|
const userId = await this.hs.admin.createUser(localpart, { displayname: mapped.displayname });
|
|
192
238
|
await this.hs.db.create("sso_identities", {
|
|
193
239
|
idp_id: provider.id,
|
|
194
|
-
subject
|
|
240
|
+
subject,
|
|
195
241
|
user_id: userId
|
|
196
242
|
});
|
|
197
243
|
return userId;
|
|
198
244
|
}
|
|
199
245
|
};
|
|
246
|
+
/**
|
|
247
|
+
* The legacy API's last step: the client URL carrying a login token for `userId`, and whether
|
|
248
|
+
* the user confirms the client first (clients not on `sso.clientAllowlist`).
|
|
249
|
+
*/
|
|
250
|
+
async function legacyClientRedirect(hs, userId, redirectUrl) {
|
|
251
|
+
const loginToken = await hs.auth.createLoginToken(userId);
|
|
252
|
+
const redirect = new URL(redirectUrl);
|
|
253
|
+
redirect.searchParams.set("loginToken", loginToken);
|
|
254
|
+
return {
|
|
255
|
+
url: redirect.toString(),
|
|
256
|
+
confirm: !(hs.sso?.clientAllowlist ?? []).some((entry) => clientMatches(redirectUrl, entry)),
|
|
257
|
+
client: redirect.host || redirect.protocol.replace(/:$/, "")
|
|
258
|
+
};
|
|
259
|
+
}
|
|
200
260
|
//#endregion
|
|
201
|
-
export { SsoService, checkRedirectUrl };
|
|
261
|
+
export { SsoService, checkRedirectUrl, githubProvider, legacyClientRedirect, sanitizeLocalpart };
|
package/dist/homeserver.d.ts
CHANGED
|
@@ -13,19 +13,20 @@ import { Notifier } from "./core/notifier.js";
|
|
|
13
13
|
import { RoomService } from "./core/rooms.js";
|
|
14
14
|
import { TypingService } from "./core/typing.js";
|
|
15
15
|
import { PushService } from "./core/push.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { SsoOptions, SsoService } from "./core/sso.js";
|
|
19
|
-
import { FederationOptions, FederationService } from "./federation/index.js";
|
|
20
|
-
import { DelayedEventService, DelayedEventsOptions } from "./core/delayed.js";
|
|
16
|
+
import { ResolvedUserAuth, UserAuthAdapter } from "./adapters/auth.js";
|
|
17
|
+
import { MediaAdapter, Thumbnailer } from "./adapters/media.js";
|
|
21
18
|
import { RateLimitAction, RateLimitRule, RateLimiter, RateLimits } from "./core/ratelimit.js";
|
|
19
|
+
import { FederationOptions, FederationService } from "./federation/index.js";
|
|
22
20
|
import { Router } from "./http/router.js";
|
|
23
21
|
import { RtcOptions } from "./api/rtc.js";
|
|
22
|
+
import { DelayedEventService, DelayedEventsOptions } from "./core/delayed.js";
|
|
23
|
+
import { OAuthDelegation, OAuthDelegationOptions } from "./core/oauth.js";
|
|
24
|
+
import { OAuthServer, OAuthServerOptions } from "./core/oauthserver.js";
|
|
25
|
+
import { SsoOptions, SsoService } from "./core/sso.js";
|
|
24
26
|
import { AuditOptions, AuditService } from "./core/audit.js";
|
|
25
|
-
import { SettingsService } from "./core/settings.js";
|
|
26
27
|
import { AdminRoomOptions, AdminRoomService } from "./core/adminroom.js";
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
28
|
+
import { AdminService, ServerNoticesOptions } from "./core/admin.js";
|
|
29
|
+
import { SettingsService } from "./core/settings.js";
|
|
29
30
|
import { ChangeTracker } from "./core/changes.js";
|
|
30
31
|
import { AddressPolicy } from "./core/outbound.js";
|
|
31
32
|
import { EventEmitter } from "node:events";
|
|
@@ -112,7 +113,16 @@ export interface HomeserverOptions {
|
|
|
112
113
|
*/
|
|
113
114
|
/** Delegate authentication to an external OAuth 2.0 / OIDC provider (disables password login and registration). */
|
|
114
115
|
oauth?: OAuthDelegationOptions;
|
|
115
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* Serve the spec's OAuth 2.0 API: users log in on the server's own web pages, with a password
|
|
118
|
+
* and/or the upstream providers of `sso` (e.g. `githubProvider`). Requires `publicBaseUrl`.
|
|
119
|
+
* Cannot be combined with `oauth`.
|
|
120
|
+
*/
|
|
121
|
+
oauthServer?: boolean | OAuthServerOptions;
|
|
122
|
+
/**
|
|
123
|
+
* Upstream identity providers (OpenID Connect, or OAuth 2.0 such as GitHub) for `m.login.sso`
|
|
124
|
+
* and the `oauthServer` login page. Requires `publicBaseUrl`.
|
|
125
|
+
*/
|
|
116
126
|
sso?: SsoOptions;
|
|
117
127
|
/**
|
|
118
128
|
* Coordination between instances. The default (`localCluster()`) supports one process;
|
|
@@ -198,6 +208,7 @@ export declare class Homeserver extends EventEmitter {
|
|
|
198
208
|
readonly rateLimits: RateLimits;
|
|
199
209
|
readonly admin: AdminService;
|
|
200
210
|
readonly oauth: OAuthDelegation | null;
|
|
211
|
+
readonly oauthServer: OAuthServer | null;
|
|
201
212
|
readonly sso: SsoService | null;
|
|
202
213
|
readonly federation: FederationService;
|
|
203
214
|
readonly delayedEvents: DelayedEventService;
|
package/dist/homeserver.js
CHANGED
|
@@ -16,6 +16,7 @@ import { TypingService } from "./core/typing.js";
|
|
|
16
16
|
import { coalesceReads } from "./storage/batch.js";
|
|
17
17
|
import { ChangeTracker } from "./core/changes.js";
|
|
18
18
|
import { PushService } from "./core/push.js";
|
|
19
|
+
import { OAuthServer } from "./core/oauthserver.js";
|
|
19
20
|
import { SsoService } from "./core/sso.js";
|
|
20
21
|
import { AdminService } from "./core/admin.js";
|
|
21
22
|
import { OAuthDelegation } from "./core/oauth.js";
|
|
@@ -75,6 +76,7 @@ var Homeserver = class extends EventEmitter {
|
|
|
75
76
|
rateLimits;
|
|
76
77
|
admin;
|
|
77
78
|
oauth;
|
|
79
|
+
oauthServer;
|
|
78
80
|
sso;
|
|
79
81
|
federation;
|
|
80
82
|
delayedEvents;
|
|
@@ -153,6 +155,9 @@ var Homeserver = class extends EventEmitter {
|
|
|
153
155
|
this.push = new PushService(this);
|
|
154
156
|
this.admin = new AdminService(this);
|
|
155
157
|
this.oauth = options.oauth ? new OAuthDelegation(this, options.oauth) : null;
|
|
158
|
+
if (options.oauthServer && options.oauth) throw new Error("`oauthServer` and `oauth` (delegated OAuth) cannot be used together");
|
|
159
|
+
if (options.oauthServer && !options.publicBaseUrl) throw new Error("`publicBaseUrl` is required for `oauthServer`");
|
|
160
|
+
this.oauthServer = options.oauthServer ? new OAuthServer(this, options.oauthServer === true ? {} : options.oauthServer, options.publicBaseUrl) : null;
|
|
156
161
|
this.fetch = options.fetch ?? globalThis.fetch;
|
|
157
162
|
this.addressPolicy = addressPolicy(options.allowPrivateAddresses);
|
|
158
163
|
this.publicFetch = policyFetch(this.addressPolicy, options.fetch);
|
|
@@ -167,7 +172,7 @@ var Homeserver = class extends EventEmitter {
|
|
|
167
172
|
this.identityServers = new IdentityServers(this);
|
|
168
173
|
const adminRoom = options.adminRoom === true ? {} : options.adminRoom || null;
|
|
169
174
|
this.adminRoom = adminRoom && adminRoom.enabled !== false ? new AdminRoomService(this, adminRoom) : null;
|
|
170
|
-
this.sso = options.sso
|
|
175
|
+
this.sso = options.sso && (options.sso.providers.length || options.oauthServer) ? new SsoService(this, options.sso) : null;
|
|
171
176
|
if (this.sso && !options.publicBaseUrl) throw new Error("`publicBaseUrl` is required when SSO is configured");
|
|
172
177
|
this.rateLimits = options.rateLimits === false ? new RateLimits(null) : new RateLimits(options.rateLimits?.limiter ?? memoryRateLimiter(), options.rateLimits?.rules);
|
|
173
178
|
const cleanupEvery = options.cleanupIntervalMs ?? 6e5;
|
|
@@ -222,7 +227,13 @@ var Homeserver = class extends EventEmitter {
|
|
|
222
227
|
limit: 1
|
|
223
228
|
});
|
|
224
229
|
if (oldestSnap[0] && oldestSnap[0].id < 0) await this.streams.lowerBackfillTo(oldestSnap[0].id);
|
|
225
|
-
|
|
230
|
+
for (const model of [
|
|
231
|
+
"federation_outbox",
|
|
232
|
+
"audit_log",
|
|
233
|
+
"event_reports",
|
|
234
|
+
"user_reports",
|
|
235
|
+
"appservice_outbox"
|
|
236
|
+
]) await this.cluster.advance(model, await this.db.max(model, "id") ?? 0);
|
|
226
237
|
if (this.federation.enabled) this.federation.sender.start();
|
|
227
238
|
this.delayedEvents.start();
|
|
228
239
|
this.appservices.start();
|
|
@@ -235,7 +246,11 @@ var Homeserver = class extends EventEmitter {
|
|
|
235
246
|
field: "expires_ts",
|
|
236
247
|
operator: "lt",
|
|
237
248
|
value: now - 6048e5
|
|
249
|
+
}, {
|
|
250
|
+
field: "refresh_used",
|
|
251
|
+
value: true
|
|
238
252
|
}]);
|
|
253
|
+
await this.oauthServer?.prune();
|
|
239
254
|
await this.db.delete("login_tokens", [{
|
|
240
255
|
field: "expires_ts",
|
|
241
256
|
operator: "lt",
|
package/dist/index.d.ts
CHANGED
|
@@ -3,24 +3,25 @@ import { M, UNSTABLE } from "./core/m.js";
|
|
|
3
3
|
import { AppServiceRegistration, loadAppServiceRegistrations, parseAppServiceRegistration } from "./core/appservices.js";
|
|
4
4
|
import { DatabaseAdapter, FieldDef, FindManyOptions, ModelDef, Operator, Row, Schema, WhereClause } from "./storage/types.js";
|
|
5
5
|
import { ClusterAdapter, localCluster } from "./core/cluster.js";
|
|
6
|
-
import { AdminService, ServerNoticesOptions } from "./core/admin.js";
|
|
7
|
-
import { OAuthDelegationOptions } from "./core/oauth.js";
|
|
8
|
-
import { SsoOptions, SsoProvider } from "./core/sso.js";
|
|
9
|
-
import { MatrixError } from "./core/errors.js";
|
|
10
|
-
import { FederationOptions } from "./federation/index.js";
|
|
11
|
-
import { DelayedEventsOptions } from "./core/delayed.js";
|
|
12
|
-
import { RateLimitAction, RateLimitRule, RateLimiter, memoryRateLimiter } from "./core/ratelimit.js";
|
|
13
|
-
import { RtcOptions, TurnOptions } from "./api/rtc.js";
|
|
14
|
-
import { AuditAction, AuditEntry, AuditOptions } from "./core/audit.js";
|
|
15
|
-
import { AdminRoomOptions } from "./core/adminroom.js";
|
|
16
|
-
import { PasswordCheckContext, UserAuthAdapter } from "./adapters/auth.js";
|
|
17
|
-
import { MediaAdapter, MediaMetadata, ThumbnailRequest, Thumbnailer, filesystemMedia, memoryMedia, sharpThumbnailer } from "./adapters/media.js";
|
|
18
|
-
import { Homeserver, HomeserverOptions, Logger, createHomeserver } from "./homeserver.js";
|
|
19
6
|
import { NodeHandler, getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, isMatrixPath } from "./http/adapters.js";
|
|
20
7
|
import { handleRequest } from "./http/handler.js";
|
|
8
|
+
import { MatrixError } from "./core/errors.js";
|
|
21
9
|
import { memoryAdapter } from "./storage/memory.js";
|
|
22
10
|
import { MysqlPoolLike, PgPoolLike, SqlAdapterOptions, SqlDialect, SqlExecutor, SqliteDatabaseLike, mysqlAdapter, postgresAdapter, sqlAdapter, sqliteAdapter } from "./storage/sql.js";
|
|
23
11
|
import { schema } from "./storage/schema.js";
|
|
12
|
+
import { PasswordCheckContext, UserAuthAdapter } from "./adapters/auth.js";
|
|
13
|
+
import { MediaAdapter, MediaMetadata, ThumbnailRequest, Thumbnailer, filesystemMedia, memoryMedia, sharpThumbnailer } from "./adapters/media.js";
|
|
24
14
|
import { S3MediaOptions, s3Media } from "./adapters/s3.js";
|
|
15
|
+
import { RateLimitAction, RateLimitRule, RateLimiter, memoryRateLimiter } from "./core/ratelimit.js";
|
|
25
16
|
import { PgClusterPoolLike, postgresCluster } from "./adapters/cluster.js";
|
|
26
|
-
|
|
17
|
+
import { FederationOptions } from "./federation/index.js";
|
|
18
|
+
import { RtcOptions, TurnOptions } from "./api/rtc.js";
|
|
19
|
+
import { DelayedEventsOptions } from "./core/delayed.js";
|
|
20
|
+
import { OAuthDelegationOptions } from "./core/oauth.js";
|
|
21
|
+
import { OAuthServerOptions } from "./core/oauthserver.js";
|
|
22
|
+
import { SsoOptions, SsoProvider, githubProvider } from "./core/sso.js";
|
|
23
|
+
import { AuditAction, AuditEntry, AuditOptions } from "./core/audit.js";
|
|
24
|
+
import { AdminRoomOptions } from "./core/adminroom.js";
|
|
25
|
+
import { AdminService, ServerNoticesOptions } from "./core/admin.js";
|
|
26
|
+
import { Homeserver, HomeserverOptions, Logger, createHomeserver } from "./homeserver.js";
|
|
27
|
+
export { type AdminRoomOptions, type AdminService, type AppServiceRegistration, type AuditAction, type AuditEntry, type AuditOptions, type ClusterAdapter, type DatabaseAdapter, type DelayedEventsOptions, type FederationOptions, type FieldDef, type FindManyOptions, Homeserver, type HomeserverOptions, type Logger, M, MatrixError, type MediaAdapter, type MediaMetadata, type ModelDef, type MysqlPoolLike, type NodeHandler, type OAuthDelegationOptions, type OAuthServerOptions, type Operator, type PasswordCheckContext, type PgClusterPoolLike, type PgPoolLike, type RateLimitAction, type RateLimitRule, type RateLimiter, type Row, type RtcOptions, type S3MediaOptions, type Schema, type ServerNoticesOptions, type SqlAdapterOptions, type SqlDialect, type SqlExecutor, type SqliteDatabaseLike, type SsoOptions, type SsoProvider, type ThreepidEmail, type ThreepidOptions, type ThreepidPurpose, type ThreepidSms, type ThumbnailRequest, type Thumbnailer, type TurnOptions, UNSTABLE, type UserAuthAdapter, type WhereClause, createHomeserver, filesystemMedia, getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, githubProvider, handleRequest, isMatrixPath, loadAppServiceRegistrations, localCluster, memoryAdapter, memoryMedia, memoryRateLimiter, mysqlAdapter, parseAppServiceRegistration, postgresAdapter, postgresCluster, s3Media, schema, sharpThumbnailer, sqlAdapter, sqliteAdapter };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { M, UNSTABLE } from "./core/m.js";
|
|
2
2
|
import { MatrixError } from "./core/errors.js";
|
|
3
3
|
import { localCluster } from "./core/cluster.js";
|
|
4
|
+
import { githubProvider } from "./core/sso.js";
|
|
4
5
|
import { loadAppServiceRegistrations, parseAppServiceRegistration } from "./core/appservices.js";
|
|
5
6
|
import { memoryRateLimiter } from "./core/ratelimit.js";
|
|
6
7
|
import { filesystemMedia, memoryMedia, sharpThumbnailer } from "./adapters/media.js";
|
|
@@ -12,4 +13,4 @@ import { getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, g
|
|
|
12
13
|
import { mysqlAdapter, postgresAdapter, sqlAdapter, sqliteAdapter } from "./storage/sql.js";
|
|
13
14
|
import { s3Media } from "./adapters/s3.js";
|
|
14
15
|
import { postgresCluster } from "./adapters/cluster.js";
|
|
15
|
-
export { Homeserver, M, MatrixError, UNSTABLE, createHomeserver, filesystemMedia, getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, handleRequest, isMatrixPath, loadAppServiceRegistrations, localCluster, memoryAdapter, memoryMedia, memoryRateLimiter, mysqlAdapter, parseAppServiceRegistration, postgresAdapter, postgresCluster, s3Media, schema, sharpThumbnailer, sqlAdapter, sqliteAdapter };
|
|
16
|
+
export { Homeserver, M, MatrixError, UNSTABLE, createHomeserver, filesystemMedia, getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, githubProvider, handleRequest, isMatrixPath, loadAppServiceRegistrations, localCluster, memoryAdapter, memoryMedia, memoryRateLimiter, mysqlAdapter, parseAppServiceRegistration, postgresAdapter, postgresCluster, s3Media, schema, sharpThumbnailer, sqlAdapter, sqliteAdapter };
|
package/dist/storage/schema.js
CHANGED
|
@@ -48,10 +48,28 @@ const schema = {
|
|
|
48
48
|
user_id: s,
|
|
49
49
|
device_id: s,
|
|
50
50
|
refresh_token: sn,
|
|
51
|
-
expires_ts: nn
|
|
51
|
+
expires_ts: nn,
|
|
52
|
+
client_id: sn,
|
|
53
|
+
scope: sn,
|
|
54
|
+
refresh_parent: sn,
|
|
55
|
+
refresh_used: bn
|
|
52
56
|
},
|
|
53
57
|
primaryKey: ["token"],
|
|
54
|
-
indexes: [
|
|
58
|
+
indexes: [
|
|
59
|
+
["user_id"],
|
|
60
|
+
["refresh_token"],
|
|
61
|
+
["client_id"],
|
|
62
|
+
["refresh_parent"]
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
oauth_clients: {
|
|
66
|
+
fields: {
|
|
67
|
+
client_id: s,
|
|
68
|
+
metadata: j,
|
|
69
|
+
created_ts: n
|
|
70
|
+
},
|
|
71
|
+
primaryKey: ["client_id"],
|
|
72
|
+
indexes: [["created_ts"]]
|
|
55
73
|
},
|
|
56
74
|
rooms: {
|
|
57
75
|
fields: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsonStringifyReplacer } from "../core/util.js";
|
|
2
2
|
import { $constructor } from "../core/core.js";
|
|
3
3
|
import { $ZodError, flattenError, formatError } from "../core/errors.js";
|
|
4
|
-
//#region node_modules/zod/v4/classic/errors.js
|
|
4
|
+
//#region ../../node_modules/zod/v4/classic/errors.js
|
|
5
5
|
const _installedErrorProtos = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);
|
|
6
6
|
function _lazyMethod(proto, key, make) {
|
|
7
7
|
Object.defineProperty(proto, key, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _decode, _decodeAsync, _encode, _encodeAsync, _parse, _parseAsync, _safeDecode, _safeDecodeAsync, _safeEncode, _safeEncodeAsync, _safeParse, _safeParseAsync } from "../core/parse.js";
|
|
2
2
|
import { ZodRealError } from "./errors.js";
|
|
3
|
-
//#region node_modules/zod/v4/classic/parse.js
|
|
3
|
+
//#region ../../node_modules/zod/v4/classic/parse.js
|
|
4
4
|
const parse = /* @__PURE__ */ _parse(ZodRealError);
|
|
5
5
|
const parseAsync = /* @__PURE__ */ _parseAsync(ZodRealError);
|
|
6
6
|
const safeParse = /* @__PURE__ */ _safeParse(ZodRealError);
|
|
@@ -9,7 +9,7 @@ import { _array, _base64, _base64url, _boolean, _cidrv4, _cidrv6, _cuid, _cuid2,
|
|
|
9
9
|
import { createStandardJSONSchemaMethod, createToJSONSchemaMethod } from "../core/to-json-schema.js";
|
|
10
10
|
import { aggregateChecks, arrayProcessor, booleanProcessor, catchProcessor, customProcessor, defaultProcessor, enumProcessor, intersectionProcessor, neverProcessor, nonoptionalProcessor, nullableProcessor, numberProcessor, objectProcessor, optionalProcessor, pipeProcessor, prefaultProcessor, readonlyProcessor, recordProcessor, stringProcessor, transformProcessor, unionProcessor, unknownProcessor } from "../core/json-schema-processors.js";
|
|
11
11
|
import { decode, decodeAsync, encode, encodeAsync, parse, parseAsync, safeDecode, safeDecodeAsync, safeEncode, safeEncodeAsync, safeParse, safeParseAsync } from "./parse.js";
|
|
12
|
-
//#region node_modules/zod/v4/classic/schemas.js
|
|
12
|
+
//#region ../../node_modules/zod/v4/classic/schemas.js
|
|
13
13
|
function _ensureDefaultLocale() {
|
|
14
14
|
if (!globalConfig.localeError) config(en_default());
|
|
15
15
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { issue, normalizeParams, slugify } from "./util.js";
|
|
2
2
|
import { $ZodCheck, $ZodCheckEndsWith, $ZodCheckGreaterThan, $ZodCheckIncludes, $ZodCheckLengthEquals, $ZodCheckLessThan, $ZodCheckLowerCase, $ZodCheckMaxLength, $ZodCheckMinLength, $ZodCheckMultipleOf, $ZodCheckOverwrite, $ZodCheckRegex, $ZodCheckStartsWith, $ZodCheckUpperCase } from "./checks.js";
|
|
3
|
-
//#region node_modules/zod/v4/core/api.js
|
|
3
|
+
//#region ../../node_modules/zod/v4/core/api.js
|
|
4
4
|
function snapshotChecks(def) {
|
|
5
5
|
if (def.checks) def.checks = [...def.checks];
|
|
6
6
|
return def;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NUMBER_FORMAT_RANGES, codePointLength, escapeRegex, floatSafeRemainder, getLengthableOrigin, nullish } from "./util.js";
|
|
2
2
|
import { $constructor } from "./core.js";
|
|
3
3
|
import { lowercase, uppercase } from "./regexes.js";
|
|
4
|
-
//#region node_modules/zod/v4/core/checks.js
|
|
4
|
+
//#region ../../node_modules/zod/v4/core/checks.js
|
|
5
5
|
const $ZodCheck = /*@__PURE__*/ $constructor("$ZodCheck", (inst, def) => {
|
|
6
6
|
var _a;
|
|
7
7
|
inst._zod ?? (inst._zod = {});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsonStringifyReplacer } from "./util.js";
|
|
2
2
|
import { $constructor } from "./core.js";
|
|
3
|
-
//#region node_modules/zod/v4/core/errors.js
|
|
3
|
+
//#region ../../node_modules/zod/v4/core/errors.js
|
|
4
4
|
function _getMessage() {
|
|
5
5
|
const internals = this._zod;
|
|
6
6
|
internals.message ?? (internals.message = JSON.stringify(internals.def, jsonStringifyReplacer, 2));
|
|
@@ -2,7 +2,7 @@ import { BIGINT_FORMAT_RANGES, NUMBER_FORMAT_RANGES, assignProp, getEnumValues }
|
|
|
2
2
|
import { base64, base64url, integer, number } from "./regexes.js";
|
|
3
3
|
import { base64Charset, base64urlCharset } from "./schemas.js";
|
|
4
4
|
import { handleUnrepresentable, process as processSchema } from "./to-json-schema.js";
|
|
5
|
-
//#region node_modules/zod/v4/core/json-schema-processors.js
|
|
5
|
+
//#region ../../node_modules/zod/v4/core/json-schema-processors.js
|
|
6
6
|
const narrowMin = (agg, key, value) => {
|
|
7
7
|
if (agg[key] === void 0 || value > agg[key]) agg[key] = value;
|
|
8
8
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { rawShape } from "./util.js";
|
|
2
|
-
//#region node_modules/zod/v4/core/memoizer.js
|
|
2
|
+
//#region ../../node_modules/zod/v4/core/memoizer.js
|
|
3
3
|
var $ZodCyclicError = class extends Error {
|
|
4
4
|
constructor() {
|
|
5
5
|
super(`Cannot parse a reference cycle that closes through a transform`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { captureStackTrace, finalizeIssue } from "./util.js";
|
|
2
2
|
import { $ZodAsyncError, config } from "./core.js";
|
|
3
|
-
//#region node_modules/zod/v4/core/parse.js
|
|
3
|
+
//#region ../../node_modules/zod/v4/core/parse.js
|
|
4
4
|
function finalizeParams(callee, params) {
|
|
5
5
|
return {
|
|
6
6
|
callee: params?.callee ?? callee,
|
|
@@ -4,7 +4,7 @@ import { anyString, boolean, cidrv4, cidrv6, cuid, cuid2, date, datetime, durati
|
|
|
4
4
|
import { $ZodCheck, $ZodCheckNumberFormat, $ZodCheckStringFormat } from "./checks.js";
|
|
5
5
|
import { Doc } from "./doc.js";
|
|
6
6
|
import { version } from "./versions.js";
|
|
7
|
-
//#region node_modules/zod/v4/core/schemas.js
|
|
7
|
+
//#region ../../node_modules/zod/v4/core/schemas.js
|
|
8
8
|
const $ZodType = /*@__PURE__*/ $constructor("$ZodType", (inst, def) => {
|
|
9
9
|
var _a;
|
|
10
10
|
inst ?? (inst = {});
|