@madauth/server 0.2.0-beta.2 → 0.3.0-beta.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/dist/bin.js +98 -31
- package/dist/{chunk-CNL562LZ.js → chunk-56MOVFO4.js} +24 -2
- package/dist/{chunk-XNBWZZHT.js → chunk-CD7Y4T66.js} +11 -9
- package/dist/{chunk-7CTAB3QV.js → chunk-DCDTO5LG.js} +2 -2
- package/dist/{chunk-6EJDVZCD.js → chunk-NPNM26J3.js} +9 -2
- package/dist/{chunk-5CKEMC6M.js → chunk-ODITZLMM.js} +218 -86
- package/dist/{chunk-YALS7OMV.js → chunk-ZSBKNV5W.js} +217 -122
- package/dist/{config-BkxADgtp.d.ts → config-DEBYi8hb.d.ts} +7 -6
- package/dist/dynamodb.js +1 -1
- package/dist/entry/azure.d.ts +3 -3
- package/dist/entry/azure.js +4 -4
- package/dist/entry/lambda.d.ts +3 -3
- package/dist/entry/lambda.js +4 -4
- package/dist/entry/node.d.ts +3 -3
- package/dist/entry/node.js +4 -4
- package/dist/index.d.ts +30 -7
- package/dist/index.js +15 -7
- package/dist/{schema-BSgYVRpd.d.ts → schema-3yhCc2jH.d.ts} +17 -9
- package/dist/sqlite.js +2 -2
- package/dist/standalone/azure.mjs +799 -564
- package/dist/standalone/lambda.mjs +799 -564
- package/dist/standalone/main.mjs +563 -328
- package/dist/store/dynamodb.d.ts +1 -1
- package/dist/store/sqlite.d.ts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +2 -0
- package/dist/user-BMWEWREH.d.ts +12 -0
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +1 -1
- package/dist/{webhook-ltwulgcB.d.ts → webhook-DCDP6SaE.d.ts} +1 -1
- package/dist/webhook.d.ts +1 -1
- package/dist/webhook.js +1 -1
- package/package.json +1 -1
- package/dist/user-C1A3BfOo.d.ts +0 -12
|
@@ -8,7 +8,132 @@ import {
|
|
|
8
8
|
WEBHOOK_TYPES,
|
|
9
9
|
checkWebhookSecret,
|
|
10
10
|
generateWebhookSecret
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NPNM26J3.js";
|
|
12
|
+
|
|
13
|
+
// src/claims.ts
|
|
14
|
+
var ADMIN_ROLE = "admin";
|
|
15
|
+
var MAX_ROLES = 20;
|
|
16
|
+
var MAX_CLAIMS_LENGTH = 2048;
|
|
17
|
+
var ROLE_NAME = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
18
|
+
var CLAIM_NAME = /^[a-zA-Z][a-zA-Z0-9_]{0,63}$/;
|
|
19
|
+
function parseRoles(input) {
|
|
20
|
+
if (!Array.isArray(input)) return null;
|
|
21
|
+
if (!input.every((role) => typeof role === "string" && ROLE_NAME.test(role))) return null;
|
|
22
|
+
const roles = [...new Set(input)].sort();
|
|
23
|
+
return roles.length <= MAX_ROLES ? roles : null;
|
|
24
|
+
}
|
|
25
|
+
function parseClaims(input) {
|
|
26
|
+
if (!isJsonObject(input)) return null;
|
|
27
|
+
const claims = {};
|
|
28
|
+
for (const key of Object.keys(input).sort()) {
|
|
29
|
+
const value = input[key];
|
|
30
|
+
if (!CLAIM_NAME.test(key) || !isJsonValue(value)) return null;
|
|
31
|
+
if (key === "roles") {
|
|
32
|
+
const roles = parseRoles(value);
|
|
33
|
+
if (!roles) return null;
|
|
34
|
+
claims[key] = roles;
|
|
35
|
+
} else {
|
|
36
|
+
claims[key] = value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return JSON.stringify(claims).length <= MAX_CLAIMS_LENGTH ? claims : null;
|
|
40
|
+
}
|
|
41
|
+
function isJsonObject(value) {
|
|
42
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
43
|
+
}
|
|
44
|
+
function isJsonValue(value) {
|
|
45
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
46
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
47
|
+
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
48
|
+
return isJsonObject(value) && Object.values(value).every(isJsonValue);
|
|
49
|
+
}
|
|
50
|
+
function sameClaims(a, b) {
|
|
51
|
+
return JSON.stringify(a ?? {}) === JSON.stringify(b ?? {});
|
|
52
|
+
}
|
|
53
|
+
function claimsFromJson(json) {
|
|
54
|
+
if (!json) return {};
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(json);
|
|
57
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
58
|
+
} catch {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function rolesOf(user) {
|
|
63
|
+
const roles = user.claims?.roles;
|
|
64
|
+
return Array.isArray(roles) ? roles.filter((role) => typeof role === "string") : [];
|
|
65
|
+
}
|
|
66
|
+
function isAdmin(user) {
|
|
67
|
+
return rolesOf(user).includes(ADMIN_ROLE);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/settings.ts
|
|
71
|
+
var SIGN_IN_METHODS = ["google", "password"];
|
|
72
|
+
var METHODS_SETTING = "methods";
|
|
73
|
+
function parseMethodSettings(input) {
|
|
74
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return null;
|
|
75
|
+
const settings = {};
|
|
76
|
+
for (const [key, value] of Object.entries(input)) {
|
|
77
|
+
if (!SIGN_IN_METHODS.includes(key) || typeof value !== "boolean") return null;
|
|
78
|
+
settings[key] = value;
|
|
79
|
+
}
|
|
80
|
+
return settings;
|
|
81
|
+
}
|
|
82
|
+
var MAX_TRIES = 3;
|
|
83
|
+
var Settings = class {
|
|
84
|
+
constructor(store) {
|
|
85
|
+
this.store = store;
|
|
86
|
+
}
|
|
87
|
+
/** The setting's value, or undefined if it was never set. */
|
|
88
|
+
async get(id) {
|
|
89
|
+
return (await this.read(id)).value;
|
|
90
|
+
}
|
|
91
|
+
/** The stored JSON and what it holds; both undefined if the setting was never set. */
|
|
92
|
+
async read(id) {
|
|
93
|
+
const record = await this.store.findOne("setting", { id });
|
|
94
|
+
if (typeof record?.value !== "string") return {};
|
|
95
|
+
try {
|
|
96
|
+
return { json: record.value, value: JSON.parse(record.value) };
|
|
97
|
+
} catch {
|
|
98
|
+
return { json: record.value };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Changes the setting: `apply` gets the current value and returns the new one, or null to refuse the
|
|
103
|
+
* change. The write only lands if nobody changed the setting since `apply` read it; otherwise `apply` runs
|
|
104
|
+
* again with the newer value. Resolves to the new value, or null if `apply` refused.
|
|
105
|
+
* `updatedBy` is the address of the admin, or null for the command line.
|
|
106
|
+
*/
|
|
107
|
+
async change(id, apply, updatedBy) {
|
|
108
|
+
for (let attempt = 0; attempt < MAX_TRIES; attempt++) {
|
|
109
|
+
const { json, value } = await this.read(id);
|
|
110
|
+
const next = apply(value);
|
|
111
|
+
if (next === null) return null;
|
|
112
|
+
const patch = { value: JSON.stringify(next), updatedAt: Date.now(), updatedBy };
|
|
113
|
+
const written = json === void 0 ? await this.store.create("setting", { id, ...patch }) : await this.store.update("setting", { id, value: json }, patch) === 1;
|
|
114
|
+
if (written) return next;
|
|
115
|
+
}
|
|
116
|
+
throw new Error(`Setting "${id}" kept changing; try again.`);
|
|
117
|
+
}
|
|
118
|
+
/** Replaces the setting. */
|
|
119
|
+
async set(id, value, updatedBy) {
|
|
120
|
+
await this.change(id, () => value, updatedBy);
|
|
121
|
+
}
|
|
122
|
+
/** Which sign-in methods are on; a method that was never switched off is on. */
|
|
123
|
+
async methods() {
|
|
124
|
+
return parseMethodSettings(await this.get(METHODS_SETTING)) ?? {};
|
|
125
|
+
}
|
|
126
|
+
/** Changes which methods are on, see {@link change}; `apply` gets the methods as they are now. */
|
|
127
|
+
async changeMethods(apply, updatedBy) {
|
|
128
|
+
return this.change(METHODS_SETTING, (current) => apply(parseMethodSettings(current) ?? {}), updatedBy);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
function isOn(method, available, methods) {
|
|
132
|
+
return available(method) && methods[method] !== false;
|
|
133
|
+
}
|
|
134
|
+
function anyOn(available, methods) {
|
|
135
|
+
return SIGN_IN_METHODS.some((method) => isOn(method, available, methods));
|
|
136
|
+
}
|
|
12
137
|
|
|
13
138
|
// src/config.ts
|
|
14
139
|
var envVars = {
|
|
@@ -17,7 +142,7 @@ var envVars = {
|
|
|
17
142
|
ALLOWED_ORIGINS: true,
|
|
18
143
|
GOOGLE_CLIENT_ID: false,
|
|
19
144
|
GOOGLE_CLIENT_SECRET: false,
|
|
20
|
-
DATABASE_URL:
|
|
145
|
+
DATABASE_URL: true,
|
|
21
146
|
PASSWORD_MIN_LENGTH: false,
|
|
22
147
|
WEBHOOK_URL: false,
|
|
23
148
|
WEBHOOK_SECRET: false,
|
|
@@ -86,19 +211,18 @@ MADAUTH_SIGNING_KEY='${JSON.stringify(key)}'
|
|
|
86
211
|
if (clientId && !clientId.endsWith(".apps.googleusercontent.com")) {
|
|
87
212
|
throw new ConfigError(`GOOGLE_CLIENT_ID must end with ".apps.googleusercontent.com" but is "${clientId}".`);
|
|
88
213
|
}
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
214
|
+
const webhook = webhookFromEnv(read("WEBHOOK_URL"), read("WEBHOOK_SECRET"), read("WEBHOOK_EVENTS"));
|
|
215
|
+
const sendsEmails = !!webhook && REQUIRED_EMAIL_TYPES.every((type) => webhook.events.has(type));
|
|
216
|
+
const password = sendsEmails ? { minLength: passwordMinLength(read("PASSWORD_MIN_LENGTH")) } : void 0;
|
|
217
|
+
if (!clientId && !password) {
|
|
92
218
|
throw new ConfigError(
|
|
93
|
-
`
|
|
219
|
+
`No sign-in method is configured. Set GOOGLE_CLIENT_ID for Google sign-in, and/or a webhook that sends the e-mails for e-mail & password sign-in: for development, run the receiver at ${DEV_WEBHOOK_RECEIVER_URL} and set WEBHOOK_URL=http://localhost:8790/webhook and WEBHOOK_EVENTS=${REQUIRED_EMAIL_TYPES.join(",")},email.already_registered. See docs/server.md.`
|
|
94
220
|
);
|
|
95
221
|
}
|
|
96
222
|
const store = overrides.store ?? await storeFromDatabaseUrl(read("DATABASE_URL"));
|
|
97
|
-
|
|
98
|
-
if (store) password = { minLength: passwordMinLength(read("PASSWORD_MIN_LENGTH")), store };
|
|
99
|
-
if (!clientId && !password) {
|
|
223
|
+
if (!store) {
|
|
100
224
|
throw new ConfigError(
|
|
101
|
-
"
|
|
225
|
+
"DATABASE_URL is not set. madAuth stores every user, whichever way they sign in: set e.g. DATABASE_URL=sqlite:./madauth.db or DATABASE_URL=dynamodb:<table>. See docs/server.md."
|
|
102
226
|
);
|
|
103
227
|
}
|
|
104
228
|
const ttlValue = read("SESSION_TTL");
|
|
@@ -121,6 +245,7 @@ MADAUTH_SIGNING_KEY='${JSON.stringify(key)}'
|
|
|
121
245
|
renewalTtlSeconds,
|
|
122
246
|
cookieDomain: read("COOKIE_DOMAIN"),
|
|
123
247
|
google: clientId ? { clientId, clientSecret: read("GOOGLE_CLIENT_SECRET") } : void 0,
|
|
248
|
+
store,
|
|
124
249
|
password,
|
|
125
250
|
webhook
|
|
126
251
|
};
|
|
@@ -174,8 +299,9 @@ async function dynamoDbStore(tableName) {
|
|
|
174
299
|
const { createDynamoDbAdapter } = await import("./dynamodb.js");
|
|
175
300
|
return createDynamoDbAdapter({ tableName });
|
|
176
301
|
}
|
|
302
|
+
var RENAMED_TYPES = { "password.reset": "email.password_reset", "roles.changed": "user.claims_changed" };
|
|
177
303
|
var LOCAL_HOSTS = ["localhost", "127.0.0.1", "[::1]", "host.docker.internal"];
|
|
178
|
-
function webhookFromEnv(url, secret, events
|
|
304
|
+
function webhookFromEnv(url, secret, events) {
|
|
179
305
|
if (!url) {
|
|
180
306
|
if (secret || events) throw new ConfigError("WEBHOOK_SECRET and WEBHOOK_EVENTS need WEBHOOK_URL.");
|
|
181
307
|
return void 0;
|
|
@@ -205,24 +331,22 @@ WEBHOOK_SECRET=${generateWebhookSecret()}
|
|
|
205
331
|
const allTypes = `All types: ${WEBHOOK_TYPES.join(", ")}. See "Webhooks" in docs/server.md.`;
|
|
206
332
|
if (!selected.size) {
|
|
207
333
|
throw new ConfigError(
|
|
208
|
-
|
|
334
|
+
`WEBHOOK_EVENTS is not set. List the types your webhook receiver handles; only these are sent. E-mail & password sign-in is on when they include ${REQUIRED_EMAIL_TYPES.join(" and ")}. For a receiver that sends the e-mails:
|
|
209
335
|
|
|
210
336
|
WEBHOOK_EVENTS=email.verify,email.reset,email.already_registered
|
|
211
337
|
|
|
212
|
-
|
|
338
|
+
For one that only wants to know who signed in:
|
|
339
|
+
|
|
340
|
+
WEBHOOK_EVENTS=user.signed_in
|
|
341
|
+
|
|
342
|
+
` + allTypes
|
|
213
343
|
);
|
|
214
344
|
}
|
|
215
345
|
const unknown = [...selected].filter((e) => !WEBHOOK_TYPES.includes(e));
|
|
216
346
|
if (unknown.length) {
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
const missing = sendsEmails ? REQUIRED_EMAIL_TYPES.filter((type) => !selected.has(type)) : [];
|
|
220
|
-
if (missing.length) {
|
|
347
|
+
const renamed = unknown.filter((e) => e in RENAMED_TYPES).map((e) => `${e} is now ${RENAMED_TYPES[e]}`);
|
|
221
348
|
throw new ConfigError(
|
|
222
|
-
`WEBHOOK_EVENTS
|
|
223
|
-
|
|
224
|
-
WEBHOOK_EVENTS=${[...selected, ...missing].join(",")}
|
|
225
|
-
`
|
|
349
|
+
`WEBHOOK_EVENTS: unknown type ${unknown.join(", ")}. ${renamed.length ? `${renamed.join("; ")}. ` : ""}Known types: ${WEBHOOK_TYPES.join(", ")}.`
|
|
226
350
|
);
|
|
227
351
|
}
|
|
228
352
|
return { url: parsed.href, secret, events: selected };
|
|
@@ -303,45 +427,6 @@ function safeEqual(a, b) {
|
|
|
303
427
|
return x.length === y.length && timingSafeEqual(x, y);
|
|
304
428
|
}
|
|
305
429
|
|
|
306
|
-
// src/roles.ts
|
|
307
|
-
var ADMIN_ROLE = "admin";
|
|
308
|
-
var MAX_ROLES = 20;
|
|
309
|
-
var ROLE_NAME = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
310
|
-
function parseRoles(input) {
|
|
311
|
-
if (!Array.isArray(input)) return null;
|
|
312
|
-
if (!input.every((role) => typeof role === "string" && ROLE_NAME.test(role))) return null;
|
|
313
|
-
const roles = [...new Set(input)].sort();
|
|
314
|
-
return roles.length <= MAX_ROLES ? roles : null;
|
|
315
|
-
}
|
|
316
|
-
function sameRoles(a, b) {
|
|
317
|
-
return a.length === b.length && a.every((role, i) => role === b[i]);
|
|
318
|
-
}
|
|
319
|
-
var Roles = class {
|
|
320
|
-
constructor(store) {
|
|
321
|
-
this.store = store;
|
|
322
|
-
}
|
|
323
|
-
/** The address's roles, sorted; empty if it has none. */
|
|
324
|
-
async get(emailNormalized) {
|
|
325
|
-
const record = await this.store.findOne("role", { id: emailNormalized });
|
|
326
|
-
return typeof record?.roles === "string" && record.roles ? record.roles.split(" ").filter(Boolean) : [];
|
|
327
|
-
}
|
|
328
|
-
/** Replaces the address's roles. `updatedBy` is the address of the admin, or null for the command line. */
|
|
329
|
-
async set(emailNormalized, roles, updatedBy) {
|
|
330
|
-
if (!roles.length) {
|
|
331
|
-
await this.remove(emailNormalized);
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
const data = { roles: roles.join(" "), updatedAt: Date.now(), updatedBy };
|
|
335
|
-
if (await this.store.update("role", { id: emailNormalized }, data) === 1) return;
|
|
336
|
-
if (!await this.store.create("role", { id: emailNormalized, ...data })) {
|
|
337
|
-
await this.store.update("role", { id: emailNormalized }, data);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
async remove(emailNormalized) {
|
|
341
|
-
await this.store.delete("role", { id: emailNormalized });
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
|
|
345
430
|
// src/users.ts
|
|
346
431
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
347
432
|
var MAX_CODE_ATTEMPTS = 5;
|
|
@@ -350,6 +435,18 @@ var newId = (prefix) => `${prefix}_${randomBytes2(16).toString("base64url")}`;
|
|
|
350
435
|
function passwordAccountKey(userId) {
|
|
351
436
|
return `password:${userId}`;
|
|
352
437
|
}
|
|
438
|
+
function googleAccountKey(sub) {
|
|
439
|
+
return `google:${sub}`;
|
|
440
|
+
}
|
|
441
|
+
function toMadauthUser(user, profile = {}) {
|
|
442
|
+
const result = { id: user.id, email: user.email };
|
|
443
|
+
const name = user.name ?? profile.name;
|
|
444
|
+
if (name) result.name = name;
|
|
445
|
+
if (profile.picture) result.picture = profile.picture;
|
|
446
|
+
const claims = claimsFromJson(user.claims);
|
|
447
|
+
if (Object.keys(claims).length) result.claims = claims;
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
353
450
|
var Users = class {
|
|
354
451
|
/** `codeKey` keys the e-mail codes (see deriveCodeKey); only needed to issue and check them. */
|
|
355
452
|
constructor(store, codeKey) {
|
|
@@ -367,11 +464,16 @@ var Users = class {
|
|
|
367
464
|
return await this.store.findOne("user", { id });
|
|
368
465
|
}
|
|
369
466
|
async passwordAccount(userId) {
|
|
370
|
-
return
|
|
467
|
+
return this.findAccountByKey(passwordAccountKey(userId));
|
|
468
|
+
}
|
|
469
|
+
async findAccountByKey(key) {
|
|
470
|
+
return await this.store.findOne("account", { key });
|
|
371
471
|
}
|
|
372
|
-
/**
|
|
373
|
-
|
|
374
|
-
|
|
472
|
+
/**
|
|
473
|
+
* Creates a user with their first account. Resolves to null if the e-mail address or the account key is
|
|
474
|
+
* taken. `account.key` is `password:<userId>` for a password (its hash as `secret`), or e.g. `google:<sub>`.
|
|
475
|
+
*/
|
|
476
|
+
async createUser(data) {
|
|
375
477
|
const user = {
|
|
376
478
|
id: newId("usr"),
|
|
377
479
|
email: data.email,
|
|
@@ -381,39 +483,59 @@ var Users = class {
|
|
|
381
483
|
sessionVersion: 0,
|
|
382
484
|
lastMailAt: 0,
|
|
383
485
|
wrongCodes: 0,
|
|
384
|
-
|
|
486
|
+
claims: null,
|
|
487
|
+
createdAt: Date.now()
|
|
385
488
|
};
|
|
386
489
|
if (!await this.store.create("user", user)) return null;
|
|
387
|
-
const account =
|
|
388
|
-
|
|
389
|
-
userId: user.id,
|
|
390
|
-
key: passwordAccountKey(user.id),
|
|
391
|
-
secret: data.passwordHash,
|
|
392
|
-
failedAttempts: 0,
|
|
393
|
-
lockedUntil: 0,
|
|
394
|
-
createdAt: now
|
|
395
|
-
};
|
|
396
|
-
if (!await this.store.create("account", account)) {
|
|
490
|
+
const account = typeof data.account === "function" ? data.account(user.id) : data.account;
|
|
491
|
+
if (!await this.linkAccount(user.id, account)) {
|
|
397
492
|
await this.store.delete("user", { id: user.id });
|
|
398
493
|
return null;
|
|
399
494
|
}
|
|
400
495
|
return user;
|
|
401
496
|
}
|
|
402
497
|
/**
|
|
403
|
-
*
|
|
404
|
-
*
|
|
498
|
+
* A provider verified the user's address: an unconfirmed password sign-up with it, whose password nobody
|
|
499
|
+
* has proven, is dropped (the account and its pending links and codes), and the user counts as verified.
|
|
500
|
+
*/
|
|
501
|
+
async verifyByProvider(user) {
|
|
502
|
+
if (user.emailVerified) return;
|
|
503
|
+
await this.store.delete("account", { key: passwordAccountKey(user.id) });
|
|
504
|
+
await this.clearVerifications(user.id);
|
|
505
|
+
await this.updateUser(user.id, { emailVerified: true, wrongCodes: 0 });
|
|
506
|
+
}
|
|
507
|
+
/** Removes an account record, e.g. one left behind by a deletion that did not finish. */
|
|
508
|
+
async deleteAccount(id) {
|
|
509
|
+
await this.store.delete("account", { id });
|
|
510
|
+
}
|
|
511
|
+
/** Adds a sign-in method to a user. Resolves to null if the account key is taken. */
|
|
512
|
+
async linkAccount(userId, account) {
|
|
513
|
+
const record = {
|
|
514
|
+
id: newId("acc"),
|
|
515
|
+
userId,
|
|
516
|
+
key: account.key,
|
|
517
|
+
secret: account.secret ?? null,
|
|
518
|
+
failedAttempts: 0,
|
|
519
|
+
lockedUntil: 0,
|
|
520
|
+
email: account.email ?? null,
|
|
521
|
+
createdAt: Date.now()
|
|
522
|
+
};
|
|
523
|
+
return await this.store.create("account", record) ? record : null;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Deletes the user and everything stored for them. Resolves to false if there is no such user (any more).
|
|
405
527
|
*/
|
|
406
|
-
async
|
|
407
|
-
const user = await this.
|
|
408
|
-
if (!user) return
|
|
409
|
-
if (await this.store.delete("user", { id: user.id }) !== 1) return
|
|
528
|
+
async deleteById(id) {
|
|
529
|
+
const user = await this.findById(id);
|
|
530
|
+
if (!user) return false;
|
|
531
|
+
if (await this.store.delete("user", { id: user.id }) !== 1) return false;
|
|
410
532
|
try {
|
|
411
533
|
await this.store.delete("account", { userId: user.id });
|
|
412
534
|
await this.store.delete("verification", { userId: user.id });
|
|
413
535
|
} catch (e) {
|
|
414
536
|
console.error(`[madauth] Deleted user ${user.id}, but not all of their records: ${e.message}`);
|
|
415
537
|
}
|
|
416
|
-
return
|
|
538
|
+
return true;
|
|
417
539
|
}
|
|
418
540
|
async updateUser(id, patch) {
|
|
419
541
|
await this.store.update("user", { id }, patch);
|
|
@@ -499,6 +621,12 @@ var Users = class {
|
|
|
499
621
|
};
|
|
500
622
|
|
|
501
623
|
export {
|
|
624
|
+
ADMIN_ROLE,
|
|
625
|
+
parseRoles,
|
|
626
|
+
parseClaims,
|
|
627
|
+
sameClaims,
|
|
628
|
+
claimsFromJson,
|
|
629
|
+
isAdmin,
|
|
502
630
|
MAX_PASSWORD_LENGTH,
|
|
503
631
|
hashPassword,
|
|
504
632
|
verifyPassword,
|
|
@@ -507,11 +635,15 @@ export {
|
|
|
507
635
|
normalizeEmail,
|
|
508
636
|
isValidEmail,
|
|
509
637
|
deriveCodeKey,
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
Roles,
|
|
638
|
+
passwordAccountKey,
|
|
639
|
+
googleAccountKey,
|
|
640
|
+
toMadauthUser,
|
|
514
641
|
Users,
|
|
642
|
+
SIGN_IN_METHODS,
|
|
643
|
+
parseMethodSettings,
|
|
644
|
+
Settings,
|
|
645
|
+
isOn,
|
|
646
|
+
anyOn,
|
|
515
647
|
envVars,
|
|
516
648
|
resolveEnv,
|
|
517
649
|
ConfigError,
|