@eaccess/auth 0.1.2 → 0.1.4
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/index.cjs +605 -522
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +262 -56
- package/dist/index.d.ts +262 -56
- package/dist/index.js +607 -523
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -64,20 +64,87 @@ __export(index_exports, {
|
|
|
64
64
|
UserInactiveError: () => UserInactiveError,
|
|
65
65
|
UserNotFoundError: () => UserNotFoundError,
|
|
66
66
|
UserNotLoggedInError: () => UserNotLoggedInError,
|
|
67
|
+
addRoleToUser: () => addRoleToUser,
|
|
68
|
+
authFunctions: () => auth_functions_exports,
|
|
67
69
|
cleanupExpiredTokens: () => cleanupExpiredTokens,
|
|
70
|
+
createAuthContext: () => createAuthContext,
|
|
68
71
|
createAuthMiddleware: () => createAuthMiddleware,
|
|
69
72
|
createAuthTables: () => createAuthTables,
|
|
70
|
-
createUser: () => createUser,
|
|
71
73
|
dropAuthTables: () => dropAuthTables,
|
|
72
74
|
getAuthTableStats: () => getAuthTableStats,
|
|
75
|
+
getUserRoles: () => getUserRoles,
|
|
73
76
|
isValidEmail: () => isValidEmail,
|
|
77
|
+
removeRoleFromUser: () => removeRoleFromUser,
|
|
78
|
+
setUserRoles: () => setUserRoles,
|
|
74
79
|
validateEmail: () => validateEmail
|
|
75
80
|
});
|
|
76
81
|
module.exports = __toCommonJS(index_exports);
|
|
77
82
|
|
|
78
|
-
// src/auth-
|
|
79
|
-
var
|
|
80
|
-
var
|
|
83
|
+
// src/auth-manager.ts
|
|
84
|
+
var import_hash4 = require("@prsm/hash");
|
|
85
|
+
var import_ms3 = __toESM(require("@prsm/ms"), 1);
|
|
86
|
+
|
|
87
|
+
// src/types.ts
|
|
88
|
+
var import_express_session = require("express-session");
|
|
89
|
+
var TwoFactorMechanism = /* @__PURE__ */ ((TwoFactorMechanism2) => {
|
|
90
|
+
TwoFactorMechanism2[TwoFactorMechanism2["TOTP"] = 1] = "TOTP";
|
|
91
|
+
TwoFactorMechanism2[TwoFactorMechanism2["EMAIL"] = 2] = "EMAIL";
|
|
92
|
+
TwoFactorMechanism2[TwoFactorMechanism2["SMS"] = 3] = "SMS";
|
|
93
|
+
return TwoFactorMechanism2;
|
|
94
|
+
})(TwoFactorMechanism || {});
|
|
95
|
+
var AuthStatus = {
|
|
96
|
+
Normal: 0,
|
|
97
|
+
Archived: 1,
|
|
98
|
+
Banned: 2,
|
|
99
|
+
Locked: 3,
|
|
100
|
+
PendingReview: 4,
|
|
101
|
+
Suspended: 5
|
|
102
|
+
};
|
|
103
|
+
var AuthRole = {
|
|
104
|
+
Admin: 1,
|
|
105
|
+
Author: 2,
|
|
106
|
+
Collaborator: 4,
|
|
107
|
+
Consultant: 8,
|
|
108
|
+
Consumer: 16,
|
|
109
|
+
Contributor: 32,
|
|
110
|
+
Coordinator: 64,
|
|
111
|
+
Creator: 128,
|
|
112
|
+
Developer: 256,
|
|
113
|
+
Director: 512,
|
|
114
|
+
Editor: 1024,
|
|
115
|
+
Employee: 2048,
|
|
116
|
+
Maintainer: 4096,
|
|
117
|
+
Manager: 8192,
|
|
118
|
+
Moderator: 16384,
|
|
119
|
+
Publisher: 32768,
|
|
120
|
+
Reviewer: 65536,
|
|
121
|
+
Subscriber: 131072,
|
|
122
|
+
SuperAdmin: 262144,
|
|
123
|
+
SuperEditor: 524288,
|
|
124
|
+
SuperModerator: 1048576,
|
|
125
|
+
Translator: 2097152
|
|
126
|
+
};
|
|
127
|
+
var AuthActivityAction = {
|
|
128
|
+
Login: "login",
|
|
129
|
+
Logout: "logout",
|
|
130
|
+
FailedLogin: "failed_login",
|
|
131
|
+
Register: "register",
|
|
132
|
+
EmailConfirmed: "email_confirmed",
|
|
133
|
+
PasswordResetRequested: "password_reset_requested",
|
|
134
|
+
PasswordResetCompleted: "password_reset_completed",
|
|
135
|
+
PasswordChanged: "password_changed",
|
|
136
|
+
EmailChanged: "email_changed",
|
|
137
|
+
RoleChanged: "role_changed",
|
|
138
|
+
StatusChanged: "status_changed",
|
|
139
|
+
ForceLogout: "force_logout",
|
|
140
|
+
OAuthConnected: "oauth_connected",
|
|
141
|
+
RememberTokenCreated: "remember_token_created",
|
|
142
|
+
TwoFactorSetup: "two_factor_setup",
|
|
143
|
+
TwoFactorVerified: "two_factor_verified",
|
|
144
|
+
TwoFactorFailed: "two_factor_failed",
|
|
145
|
+
TwoFactorDisabled: "two_factor_disabled",
|
|
146
|
+
BackupCodeUsed: "backup_code_used"
|
|
147
|
+
};
|
|
81
148
|
|
|
82
149
|
// src/queries.ts
|
|
83
150
|
var AuthQueries = class {
|
|
@@ -341,6 +408,158 @@ var AuthQueries = class {
|
|
|
341
408
|
}
|
|
342
409
|
};
|
|
343
410
|
|
|
411
|
+
// src/activity-logger.ts
|
|
412
|
+
var import_bowser = __toESM(require("bowser"), 1);
|
|
413
|
+
var ActivityLogger = class {
|
|
414
|
+
constructor(config) {
|
|
415
|
+
this.config = config;
|
|
416
|
+
this.enabled = config.activityLog?.enabled !== false;
|
|
417
|
+
this.maxEntries = config.activityLog?.maxEntries || 1e4;
|
|
418
|
+
this.allowedActions = config.activityLog?.actions || null;
|
|
419
|
+
this.tablePrefix = config.tablePrefix || "user_";
|
|
420
|
+
}
|
|
421
|
+
get activityTable() {
|
|
422
|
+
return `${this.tablePrefix}activity_log`;
|
|
423
|
+
}
|
|
424
|
+
parseUserAgent(userAgent) {
|
|
425
|
+
if (!userAgent) {
|
|
426
|
+
return { browser: null, os: null, device: null };
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
const browser = import_bowser.default.getParser(userAgent);
|
|
430
|
+
const result = browser.getResult();
|
|
431
|
+
return {
|
|
432
|
+
browser: result.browser.name || null,
|
|
433
|
+
os: result.os.name || null,
|
|
434
|
+
device: result.platform.type || "desktop"
|
|
435
|
+
};
|
|
436
|
+
} catch (error) {
|
|
437
|
+
return this.parseUserAgentSimple(userAgent);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
parseUserAgentSimple(userAgent) {
|
|
441
|
+
let browser = null;
|
|
442
|
+
if (userAgent.includes("Chrome")) browser = "Chrome";
|
|
443
|
+
else if (userAgent.includes("Firefox")) browser = "Firefox";
|
|
444
|
+
else if (userAgent.includes("Safari")) browser = "Safari";
|
|
445
|
+
else if (userAgent.includes("Edge")) browser = "Edge";
|
|
446
|
+
let os = null;
|
|
447
|
+
if (userAgent.includes("Windows")) os = "Windows";
|
|
448
|
+
else if (userAgent.includes("Mac OS")) os = "macOS";
|
|
449
|
+
else if (userAgent.includes("Linux")) os = "Linux";
|
|
450
|
+
else if (userAgent.includes("Android")) os = "Android";
|
|
451
|
+
else if (userAgent.includes("iOS")) os = "iOS";
|
|
452
|
+
let device = "desktop";
|
|
453
|
+
if (userAgent.includes("Mobile")) device = "mobile";
|
|
454
|
+
else if (userAgent.includes("Tablet")) device = "tablet";
|
|
455
|
+
return { browser, os, device };
|
|
456
|
+
}
|
|
457
|
+
getIpAddress(req) {
|
|
458
|
+
return req.ip || req.connection?.remoteAddress || req.socket?.remoteAddress || req.connection?.socket?.remoteAddress || null;
|
|
459
|
+
}
|
|
460
|
+
async logActivity(accountId, action, req, success = true, metadata = {}) {
|
|
461
|
+
if (!this.enabled) return;
|
|
462
|
+
if (this.allowedActions && !this.allowedActions.includes(action)) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const userAgent = (typeof req.get === "function" ? req.get("User-Agent") : req.headers?.["user-agent"]) || null;
|
|
466
|
+
const ip = this.getIpAddress(req);
|
|
467
|
+
const parsed = this.parseUserAgent(userAgent);
|
|
468
|
+
try {
|
|
469
|
+
await this.config.db.query(
|
|
470
|
+
`
|
|
471
|
+
INSERT INTO ${this.activityTable}
|
|
472
|
+
(account_id, action, ip_address, user_agent, browser, os, device, success, metadata)
|
|
473
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
474
|
+
`,
|
|
475
|
+
[accountId, action, ip, userAgent, parsed.browser, parsed.os, parsed.device, success, Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null]
|
|
476
|
+
);
|
|
477
|
+
if (Math.random() < 0.01) {
|
|
478
|
+
await this.cleanup();
|
|
479
|
+
}
|
|
480
|
+
} catch (error) {
|
|
481
|
+
console.error("ActivityLogger: Failed to log activity:", error);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async cleanup() {
|
|
485
|
+
if (!this.enabled) return;
|
|
486
|
+
try {
|
|
487
|
+
await this.config.db.query(
|
|
488
|
+
`
|
|
489
|
+
DELETE FROM ${this.activityTable}
|
|
490
|
+
WHERE id NOT IN (
|
|
491
|
+
SELECT id FROM ${this.activityTable}
|
|
492
|
+
ORDER BY created_at DESC
|
|
493
|
+
LIMIT $1
|
|
494
|
+
)
|
|
495
|
+
`,
|
|
496
|
+
[this.maxEntries]
|
|
497
|
+
);
|
|
498
|
+
} catch (error) {
|
|
499
|
+
console.error("ActivityLogger: Failed to cleanup old entries:", error);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
async getRecentActivity(limit = 100, accountId) {
|
|
503
|
+
if (!this.enabled) return [];
|
|
504
|
+
try {
|
|
505
|
+
let sql = `
|
|
506
|
+
SELECT
|
|
507
|
+
al.*,
|
|
508
|
+
a.email
|
|
509
|
+
FROM ${this.activityTable} al
|
|
510
|
+
LEFT JOIN ${this.tablePrefix}accounts a ON al.account_id = a.id
|
|
511
|
+
`;
|
|
512
|
+
const params = [];
|
|
513
|
+
if (accountId !== void 0) {
|
|
514
|
+
sql += " WHERE al.account_id = $1";
|
|
515
|
+
params.push(accountId);
|
|
516
|
+
}
|
|
517
|
+
sql += ` ORDER BY al.created_at DESC LIMIT $${params.length + 1}`;
|
|
518
|
+
params.push(Math.min(limit, 1e3));
|
|
519
|
+
const result = await this.config.db.query(sql, params);
|
|
520
|
+
return result.rows.map((row) => ({
|
|
521
|
+
...row,
|
|
522
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
523
|
+
}));
|
|
524
|
+
} catch (error) {
|
|
525
|
+
console.error("ActivityLogger: Failed to get recent activity:", error);
|
|
526
|
+
return [];
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async getActivityStats() {
|
|
530
|
+
if (!this.enabled) {
|
|
531
|
+
return {
|
|
532
|
+
totalEntries: 0,
|
|
533
|
+
uniqueUsers: 0,
|
|
534
|
+
recentLogins: 0,
|
|
535
|
+
failedAttempts: 0
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
const [total, unique, recent, failed] = await Promise.all([
|
|
540
|
+
this.config.db.query(`SELECT COUNT(*) as count FROM ${this.activityTable}`),
|
|
541
|
+
this.config.db.query(`SELECT COUNT(DISTINCT account_id) as count FROM ${this.activityTable} WHERE account_id IS NOT NULL`),
|
|
542
|
+
this.config.db.query(`SELECT COUNT(*) as count FROM ${this.activityTable} WHERE action = 'login' AND created_at > NOW() - INTERVAL '24 hours'`),
|
|
543
|
+
this.config.db.query(`SELECT COUNT(*) as count FROM ${this.activityTable} WHERE success = false AND created_at > NOW() - INTERVAL '24 hours'`)
|
|
544
|
+
]);
|
|
545
|
+
return {
|
|
546
|
+
totalEntries: parseInt(total.rows[0]?.count || "0"),
|
|
547
|
+
uniqueUsers: parseInt(unique.rows[0]?.count || "0"),
|
|
548
|
+
recentLogins: parseInt(recent.rows[0]?.count || "0"),
|
|
549
|
+
failedAttempts: parseInt(failed.rows[0]?.count || "0")
|
|
550
|
+
};
|
|
551
|
+
} catch (error) {
|
|
552
|
+
console.error("ActivityLogger: Failed to get activity stats:", error);
|
|
553
|
+
return {
|
|
554
|
+
totalEntries: 0,
|
|
555
|
+
uniqueUsers: 0,
|
|
556
|
+
recentLogins: 0,
|
|
557
|
+
failedAttempts: 0
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
|
|
344
563
|
// src/errors.ts
|
|
345
564
|
var AuthError = class extends Error {
|
|
346
565
|
constructor(message) {
|
|
@@ -455,9 +674,6 @@ var TwoFactorSetupIncompleteError = class extends AuthError {
|
|
|
455
674
|
}
|
|
456
675
|
};
|
|
457
676
|
|
|
458
|
-
// src/create-user.ts
|
|
459
|
-
var import_hash = require("@prsm/hash");
|
|
460
|
-
|
|
461
677
|
// src/util.ts
|
|
462
678
|
var isValidEmail = (email) => {
|
|
463
679
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
@@ -465,505 +681,16 @@ var isValidEmail = (email) => {
|
|
|
465
681
|
};
|
|
466
682
|
var validateEmail = (email) => {
|
|
467
683
|
if (typeof email !== "string") {
|
|
468
|
-
throw new InvalidEmailError();
|
|
469
|
-
}
|
|
470
|
-
if (!email.trim()) {
|
|
471
|
-
throw new InvalidEmailError();
|
|
472
|
-
}
|
|
473
|
-
if (!isValidEmail(email)) {
|
|
474
|
-
throw new InvalidEmailError();
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
var createMapFromEnum = (enumObj) => Object.fromEntries(Object.entries(enumObj).map(([key, value]) => [value, key]));
|
|
478
|
-
|
|
479
|
-
// src/types.ts
|
|
480
|
-
var import_express_session = require("express-session");
|
|
481
|
-
var TwoFactorMechanism = /* @__PURE__ */ ((TwoFactorMechanism2) => {
|
|
482
|
-
TwoFactorMechanism2[TwoFactorMechanism2["TOTP"] = 1] = "TOTP";
|
|
483
|
-
TwoFactorMechanism2[TwoFactorMechanism2["EMAIL"] = 2] = "EMAIL";
|
|
484
|
-
TwoFactorMechanism2[TwoFactorMechanism2["SMS"] = 3] = "SMS";
|
|
485
|
-
return TwoFactorMechanism2;
|
|
486
|
-
})(TwoFactorMechanism || {});
|
|
487
|
-
var AuthStatus = {
|
|
488
|
-
Normal: 0,
|
|
489
|
-
Archived: 1,
|
|
490
|
-
Banned: 2,
|
|
491
|
-
Locked: 3,
|
|
492
|
-
PendingReview: 4,
|
|
493
|
-
Suspended: 5
|
|
494
|
-
};
|
|
495
|
-
var AuthRole = {
|
|
496
|
-
Admin: 1,
|
|
497
|
-
Author: 2,
|
|
498
|
-
Collaborator: 4,
|
|
499
|
-
Consultant: 8,
|
|
500
|
-
Consumer: 16,
|
|
501
|
-
Contributor: 32,
|
|
502
|
-
Coordinator: 64,
|
|
503
|
-
Creator: 128,
|
|
504
|
-
Developer: 256,
|
|
505
|
-
Director: 512,
|
|
506
|
-
Editor: 1024,
|
|
507
|
-
Employee: 2048,
|
|
508
|
-
Maintainer: 4096,
|
|
509
|
-
Manager: 8192,
|
|
510
|
-
Moderator: 16384,
|
|
511
|
-
Publisher: 32768,
|
|
512
|
-
Reviewer: 65536,
|
|
513
|
-
Subscriber: 131072,
|
|
514
|
-
SuperAdmin: 262144,
|
|
515
|
-
SuperEditor: 524288,
|
|
516
|
-
SuperModerator: 1048576,
|
|
517
|
-
Translator: 2097152
|
|
518
|
-
};
|
|
519
|
-
var AuthActivityAction = {
|
|
520
|
-
Login: "login",
|
|
521
|
-
Logout: "logout",
|
|
522
|
-
FailedLogin: "failed_login",
|
|
523
|
-
Register: "register",
|
|
524
|
-
EmailConfirmed: "email_confirmed",
|
|
525
|
-
PasswordResetRequested: "password_reset_requested",
|
|
526
|
-
PasswordResetCompleted: "password_reset_completed",
|
|
527
|
-
PasswordChanged: "password_changed",
|
|
528
|
-
EmailChanged: "email_changed",
|
|
529
|
-
RoleChanged: "role_changed",
|
|
530
|
-
StatusChanged: "status_changed",
|
|
531
|
-
ForceLogout: "force_logout",
|
|
532
|
-
OAuthConnected: "oauth_connected",
|
|
533
|
-
RememberTokenCreated: "remember_token_created",
|
|
534
|
-
TwoFactorSetup: "two_factor_setup",
|
|
535
|
-
TwoFactorVerified: "two_factor_verified",
|
|
536
|
-
TwoFactorFailed: "two_factor_failed",
|
|
537
|
-
TwoFactorDisabled: "two_factor_disabled",
|
|
538
|
-
BackupCodeUsed: "backup_code_used"
|
|
539
|
-
};
|
|
540
|
-
|
|
541
|
-
// src/create-user.ts
|
|
542
|
-
function validatePassword(password, config) {
|
|
543
|
-
const minLength = config.minPasswordLength || 8;
|
|
544
|
-
const maxLength = config.maxPasswordLength || 64;
|
|
545
|
-
if (typeof password !== "string") {
|
|
546
|
-
throw new InvalidPasswordError();
|
|
547
|
-
}
|
|
548
|
-
if (password.length < minLength) {
|
|
549
|
-
throw new InvalidPasswordError();
|
|
550
|
-
}
|
|
551
|
-
if (password.length > maxLength) {
|
|
552
|
-
throw new InvalidPasswordError();
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
function generateAutoUserId() {
|
|
556
|
-
return crypto.randomUUID();
|
|
557
|
-
}
|
|
558
|
-
async function createConfirmationToken(queries, account, email, callback) {
|
|
559
|
-
const token = import_hash.hash.encode(email);
|
|
560
|
-
const expires = new Date(Date.now() + 1e3 * 60 * 60 * 24 * 7);
|
|
561
|
-
await queries.createConfirmation({
|
|
562
|
-
accountId: account.id,
|
|
563
|
-
token,
|
|
564
|
-
email,
|
|
565
|
-
expires
|
|
566
|
-
});
|
|
567
|
-
if (callback) {
|
|
568
|
-
callback(token);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
async function createUser(config, credentials, userId, callback) {
|
|
572
|
-
validateEmail(credentials.email);
|
|
573
|
-
validatePassword(credentials.password, config);
|
|
574
|
-
const queries = new AuthQueries(config);
|
|
575
|
-
const existing = await queries.findAccountByEmail(credentials.email);
|
|
576
|
-
if (existing) {
|
|
577
|
-
throw new EmailTakenError();
|
|
578
|
-
}
|
|
579
|
-
const finalUserId = userId || generateAutoUserId();
|
|
580
|
-
const hashedPassword = import_hash.hash.encode(credentials.password);
|
|
581
|
-
const verified = typeof callback !== "function";
|
|
582
|
-
const account = await queries.createAccount({
|
|
583
|
-
userId: finalUserId,
|
|
584
|
-
email: credentials.email,
|
|
585
|
-
password: hashedPassword,
|
|
586
|
-
verified,
|
|
587
|
-
status: AuthStatus.Normal,
|
|
588
|
-
rolemask: 0
|
|
589
|
-
});
|
|
590
|
-
if (!verified && callback) {
|
|
591
|
-
await createConfirmationToken(queries, account, credentials.email, callback);
|
|
592
|
-
}
|
|
593
|
-
return account;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// src/auth-admin-manager.ts
|
|
597
|
-
var AuthAdminManager = class {
|
|
598
|
-
constructor(req, res, config, auth) {
|
|
599
|
-
this.req = req;
|
|
600
|
-
this.res = res;
|
|
601
|
-
this.config = config;
|
|
602
|
-
this.queries = new AuthQueries(config);
|
|
603
|
-
this.auth = auth;
|
|
604
|
-
}
|
|
605
|
-
validatePassword(password) {
|
|
606
|
-
const minLength = this.config.minPasswordLength || 8;
|
|
607
|
-
const maxLength = this.config.maxPasswordLength || 64;
|
|
608
|
-
if (typeof password !== "string") {
|
|
609
|
-
throw new InvalidPasswordError();
|
|
610
|
-
}
|
|
611
|
-
if (password.length < minLength) {
|
|
612
|
-
throw new InvalidPasswordError();
|
|
613
|
-
}
|
|
614
|
-
if (password.length > maxLength) {
|
|
615
|
-
throw new InvalidPasswordError();
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
async findAccountByIdentifier(identifier) {
|
|
619
|
-
if (identifier.accountId !== void 0) {
|
|
620
|
-
return await this.queries.findAccountById(identifier.accountId);
|
|
621
|
-
} else if (identifier.email !== void 0) {
|
|
622
|
-
return await this.queries.findAccountByEmail(identifier.email);
|
|
623
|
-
} else if (identifier.userId !== void 0) {
|
|
624
|
-
return await this.queries.findAccountByUserId(identifier.userId);
|
|
625
|
-
}
|
|
626
|
-
return null;
|
|
627
|
-
}
|
|
628
|
-
async createConfirmationToken(account, email, callback) {
|
|
629
|
-
const token = import_hash2.hash.encode(email);
|
|
630
|
-
const expires = new Date(Date.now() + 1e3 * 60 * 60 * 24 * 7);
|
|
631
|
-
await this.queries.createConfirmation({
|
|
632
|
-
accountId: account.id,
|
|
633
|
-
token,
|
|
634
|
-
email,
|
|
635
|
-
expires
|
|
636
|
-
});
|
|
637
|
-
if (callback) {
|
|
638
|
-
callback(token);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* Create a new user account (admin function).
|
|
643
|
-
*
|
|
644
|
-
* @param credentials - Email and password for new account
|
|
645
|
-
* @param userId - Optional user ID to link this auth account to. If not provided, a UUID will be generated automatically.
|
|
646
|
-
* @param callback - If provided, account is created unverified and callback receives confirmation token. Create a URL like /confirm/{token} and call confirmEmail() in that handler. If omitted, account is immediately verified.
|
|
647
|
-
* @returns The created account record
|
|
648
|
-
* @throws {EmailTakenError} Email is already registered
|
|
649
|
-
* @throws {InvalidPasswordError} Password doesn't meet length requirements
|
|
650
|
-
*/
|
|
651
|
-
async createUser(credentials, userId, callback) {
|
|
652
|
-
return createUser(this.config, credentials, userId, callback);
|
|
653
|
-
}
|
|
654
|
-
/**
|
|
655
|
-
* Log in as another user (admin function).
|
|
656
|
-
* Creates a new session as the target user without requiring their password.
|
|
657
|
-
*
|
|
658
|
-
* @param identifier - Find user by accountId, email, or userId
|
|
659
|
-
* @throws {UserNotFoundError} No account matches the identifier
|
|
660
|
-
*/
|
|
661
|
-
async loginAsUserBy(identifier) {
|
|
662
|
-
const account = await this.findAccountByIdentifier(identifier);
|
|
663
|
-
if (!account) {
|
|
664
|
-
throw new UserNotFoundError();
|
|
665
|
-
}
|
|
666
|
-
await this.auth.onLoginSuccessful(account, false);
|
|
667
|
-
}
|
|
668
|
-
/**
|
|
669
|
-
* Delete a user account and all associated data (admin function).
|
|
670
|
-
* Removes account, confirmations, remember tokens, and reset tokens.
|
|
671
|
-
*
|
|
672
|
-
* @param identifier - Find user by accountId, email, or userId
|
|
673
|
-
* @throws {UserNotFoundError} No account matches the identifier
|
|
674
|
-
*/
|
|
675
|
-
async deleteUserBy(identifier) {
|
|
676
|
-
const account = await this.findAccountByIdentifier(identifier);
|
|
677
|
-
if (!account) {
|
|
678
|
-
throw new UserNotFoundError();
|
|
679
|
-
}
|
|
680
|
-
await this.queries.deleteAccount(account.id);
|
|
681
|
-
}
|
|
682
|
-
/**
|
|
683
|
-
* Add a role to a user's account (admin function).
|
|
684
|
-
* Uses bitwise OR to add role to existing rolemask.
|
|
685
|
-
*
|
|
686
|
-
* @param identifier - Find user by accountId, email, or userId
|
|
687
|
-
* @param role - Role bitmask to add (e.g., AuthRole.Admin)
|
|
688
|
-
* @throws {UserNotFoundError} No account matches the identifier
|
|
689
|
-
*/
|
|
690
|
-
async addRoleForUserBy(identifier, role) {
|
|
691
|
-
const account = await this.findAccountByIdentifier(identifier);
|
|
692
|
-
if (!account) {
|
|
693
|
-
throw new UserNotFoundError();
|
|
694
|
-
}
|
|
695
|
-
const rolemask = account.rolemask | role;
|
|
696
|
-
await this.queries.updateAccount(account.id, { rolemask });
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* Remove a role from a user's account (admin function).
|
|
700
|
-
* Uses bitwise operations to remove role from rolemask.
|
|
701
|
-
*
|
|
702
|
-
* @param identifier - Find user by accountId, email, or userId
|
|
703
|
-
* @param role - Role bitmask to remove (e.g., AuthRole.Admin)
|
|
704
|
-
* @throws {UserNotFoundError} No account matches the identifier
|
|
705
|
-
*/
|
|
706
|
-
async removeRoleForUserBy(identifier, role) {
|
|
707
|
-
const account = await this.findAccountByIdentifier(identifier);
|
|
708
|
-
if (!account) {
|
|
709
|
-
throw new UserNotFoundError();
|
|
710
|
-
}
|
|
711
|
-
const rolemask = account.rolemask & ~role;
|
|
712
|
-
await this.queries.updateAccount(account.id, { rolemask });
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* Check if a user has a specific role (admin function).
|
|
716
|
-
*
|
|
717
|
-
* @param identifier - Find user by accountId, email, or userId
|
|
718
|
-
* @param role - Role bitmask to check (e.g., AuthRole.Admin)
|
|
719
|
-
* @returns true if user has the role, false otherwise
|
|
720
|
-
* @throws {UserNotFoundError} No account matches the identifier
|
|
721
|
-
*/
|
|
722
|
-
async hasRoleForUserBy(identifier, role) {
|
|
723
|
-
const account = await this.findAccountByIdentifier(identifier);
|
|
724
|
-
if (!account) {
|
|
725
|
-
throw new UserNotFoundError();
|
|
726
|
-
}
|
|
727
|
-
return (account.rolemask & role) === role;
|
|
728
|
-
}
|
|
729
|
-
/**
|
|
730
|
-
* Change a user's password (admin function).
|
|
731
|
-
* Does not require knowing the current password.
|
|
732
|
-
*
|
|
733
|
-
* @param identifier - Find user by accountId, email, or userId
|
|
734
|
-
* @param password - New password (will be hashed)
|
|
735
|
-
* @throws {UserNotFoundError} No account matches the identifier
|
|
736
|
-
* @throws {InvalidPasswordError} New password doesn't meet requirements
|
|
737
|
-
*/
|
|
738
|
-
async changePasswordForUserBy(identifier, password) {
|
|
739
|
-
this.validatePassword(password);
|
|
740
|
-
const account = await this.findAccountByIdentifier(identifier);
|
|
741
|
-
if (!account) {
|
|
742
|
-
throw new UserNotFoundError();
|
|
743
|
-
}
|
|
744
|
-
await this.queries.updateAccount(account.id, {
|
|
745
|
-
password: import_hash2.hash.encode(password)
|
|
746
|
-
});
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* Change a user's account status (admin function).
|
|
750
|
-
*
|
|
751
|
-
* @param identifier - Find user by accountId, email, or userId
|
|
752
|
-
* @param status - New status (0=Normal, 1=Archived, 2=Banned, 3=Locked, etc.)
|
|
753
|
-
* @throws {UserNotFoundError} No account matches the identifier
|
|
754
|
-
*/
|
|
755
|
-
async setStatusForUserBy(identifier, status) {
|
|
756
|
-
const account = await this.findAccountByIdentifier(identifier);
|
|
757
|
-
if (!account) {
|
|
758
|
-
throw new UserNotFoundError();
|
|
759
|
-
}
|
|
760
|
-
await this.queries.updateAccount(account.id, { status });
|
|
761
|
-
}
|
|
762
|
-
/**
|
|
763
|
-
* Initiate password reset for a user (admin function).
|
|
764
|
-
* Creates a reset token without rate limiting.
|
|
765
|
-
*
|
|
766
|
-
* @param identifier - Find user by accountId, email, or userId
|
|
767
|
-
* @param expiresAfter - Token expiration (default: 6h). Accepts ms format like '1h', '30m'
|
|
768
|
-
* @param callback - Called with reset token. Create a URL like /reset/{token} and call confirmResetPassword() in that handler
|
|
769
|
-
* @throws {UserNotFoundError} No account matches the identifier
|
|
770
|
-
* @throws {EmailNotVerifiedError} Account exists but email is not verified
|
|
771
|
-
*/
|
|
772
|
-
async initiatePasswordResetForUserBy(identifier, expiresAfter = null, callback) {
|
|
773
|
-
const account = await this.findAccountByIdentifier(identifier);
|
|
774
|
-
if (!account) {
|
|
775
|
-
throw new UserNotFoundError();
|
|
776
|
-
}
|
|
777
|
-
if (!account.verified) {
|
|
778
|
-
throw new EmailNotVerifiedError();
|
|
779
|
-
}
|
|
780
|
-
const expiry = !expiresAfter ? (0, import_ms.default)("6h") : (0, import_ms.default)(expiresAfter);
|
|
781
|
-
const token = import_hash2.hash.encode(account.email);
|
|
782
|
-
const expires = new Date(Date.now() + expiry);
|
|
783
|
-
await this.queries.createResetToken({
|
|
784
|
-
accountId: account.id,
|
|
785
|
-
token,
|
|
786
|
-
expires
|
|
787
|
-
});
|
|
788
|
-
if (callback) {
|
|
789
|
-
callback(token);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
/**
|
|
793
|
-
* Force logout all sessions for a specific user (admin function).
|
|
794
|
-
* Increments force_logout counter and deletes all remember tokens.
|
|
795
|
-
* If target user is currently logged in, marks their session for logout.
|
|
796
|
-
*
|
|
797
|
-
* @param identifier - Find user by accountId, email, or userId
|
|
798
|
-
* @throws {UserNotFoundError} No account matches the identifier
|
|
799
|
-
*/
|
|
800
|
-
async forceLogoutForUserBy(identifier) {
|
|
801
|
-
const account = await this.findAccountByIdentifier(identifier);
|
|
802
|
-
if (!account) {
|
|
803
|
-
throw new UserNotFoundError();
|
|
804
|
-
}
|
|
805
|
-
await this.queries.incrementForceLogout(account.id);
|
|
806
|
-
if (this.auth.getId() === account.id) {
|
|
807
|
-
this.req.session.auth.shouldForceLogout = true;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
};
|
|
811
|
-
|
|
812
|
-
// src/auth-manager.ts
|
|
813
|
-
var import_hash5 = require("@prsm/hash");
|
|
814
|
-
var import_ms3 = __toESM(require("@prsm/ms"), 1);
|
|
815
|
-
|
|
816
|
-
// src/activity-logger.ts
|
|
817
|
-
var import_bowser = __toESM(require("bowser"), 1);
|
|
818
|
-
var ActivityLogger = class {
|
|
819
|
-
constructor(config) {
|
|
820
|
-
this.config = config;
|
|
821
|
-
this.enabled = config.activityLog?.enabled !== false;
|
|
822
|
-
this.maxEntries = config.activityLog?.maxEntries || 1e4;
|
|
823
|
-
this.allowedActions = config.activityLog?.actions || null;
|
|
824
|
-
this.tablePrefix = config.tablePrefix || "user_";
|
|
825
|
-
}
|
|
826
|
-
get activityTable() {
|
|
827
|
-
return `${this.tablePrefix}activity_log`;
|
|
828
|
-
}
|
|
829
|
-
parseUserAgent(userAgent) {
|
|
830
|
-
if (!userAgent) {
|
|
831
|
-
return { browser: null, os: null, device: null };
|
|
832
|
-
}
|
|
833
|
-
try {
|
|
834
|
-
const browser = import_bowser.default.getParser(userAgent);
|
|
835
|
-
const result = browser.getResult();
|
|
836
|
-
return {
|
|
837
|
-
browser: result.browser.name || null,
|
|
838
|
-
os: result.os.name || null,
|
|
839
|
-
device: result.platform.type || "desktop"
|
|
840
|
-
};
|
|
841
|
-
} catch (error) {
|
|
842
|
-
return this.parseUserAgentSimple(userAgent);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
parseUserAgentSimple(userAgent) {
|
|
846
|
-
let browser = null;
|
|
847
|
-
if (userAgent.includes("Chrome")) browser = "Chrome";
|
|
848
|
-
else if (userAgent.includes("Firefox")) browser = "Firefox";
|
|
849
|
-
else if (userAgent.includes("Safari")) browser = "Safari";
|
|
850
|
-
else if (userAgent.includes("Edge")) browser = "Edge";
|
|
851
|
-
let os = null;
|
|
852
|
-
if (userAgent.includes("Windows")) os = "Windows";
|
|
853
|
-
else if (userAgent.includes("Mac OS")) os = "macOS";
|
|
854
|
-
else if (userAgent.includes("Linux")) os = "Linux";
|
|
855
|
-
else if (userAgent.includes("Android")) os = "Android";
|
|
856
|
-
else if (userAgent.includes("iOS")) os = "iOS";
|
|
857
|
-
let device = "desktop";
|
|
858
|
-
if (userAgent.includes("Mobile")) device = "mobile";
|
|
859
|
-
else if (userAgent.includes("Tablet")) device = "tablet";
|
|
860
|
-
return { browser, os, device };
|
|
861
|
-
}
|
|
862
|
-
getIpAddress(req) {
|
|
863
|
-
return req.ip || req.connection?.remoteAddress || req.socket?.remoteAddress || req.connection?.socket?.remoteAddress || null;
|
|
864
|
-
}
|
|
865
|
-
async logActivity(accountId, action, req, success = true, metadata = {}) {
|
|
866
|
-
if (!this.enabled) return;
|
|
867
|
-
if (this.allowedActions && !this.allowedActions.includes(action)) {
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
870
|
-
const userAgent = (typeof req.get === "function" ? req.get("User-Agent") : req.headers?.["user-agent"]) || null;
|
|
871
|
-
const ip = this.getIpAddress(req);
|
|
872
|
-
const parsed = this.parseUserAgent(userAgent);
|
|
873
|
-
try {
|
|
874
|
-
await this.config.db.query(
|
|
875
|
-
`
|
|
876
|
-
INSERT INTO ${this.activityTable}
|
|
877
|
-
(account_id, action, ip_address, user_agent, browser, os, device, success, metadata)
|
|
878
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
879
|
-
`,
|
|
880
|
-
[accountId, action, ip, userAgent, parsed.browser, parsed.os, parsed.device, success, Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null]
|
|
881
|
-
);
|
|
882
|
-
if (Math.random() < 0.01) {
|
|
883
|
-
await this.cleanup();
|
|
884
|
-
}
|
|
885
|
-
} catch (error) {
|
|
886
|
-
console.error("ActivityLogger: Failed to log activity:", error);
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
async cleanup() {
|
|
890
|
-
if (!this.enabled) return;
|
|
891
|
-
try {
|
|
892
|
-
await this.config.db.query(
|
|
893
|
-
`
|
|
894
|
-
DELETE FROM ${this.activityTable}
|
|
895
|
-
WHERE id NOT IN (
|
|
896
|
-
SELECT id FROM ${this.activityTable}
|
|
897
|
-
ORDER BY created_at DESC
|
|
898
|
-
LIMIT $1
|
|
899
|
-
)
|
|
900
|
-
`,
|
|
901
|
-
[this.maxEntries]
|
|
902
|
-
);
|
|
903
|
-
} catch (error) {
|
|
904
|
-
console.error("ActivityLogger: Failed to cleanup old entries:", error);
|
|
905
|
-
}
|
|
684
|
+
throw new InvalidEmailError();
|
|
906
685
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
try {
|
|
910
|
-
let sql = `
|
|
911
|
-
SELECT
|
|
912
|
-
al.*,
|
|
913
|
-
a.email
|
|
914
|
-
FROM ${this.activityTable} al
|
|
915
|
-
LEFT JOIN ${this.tablePrefix}accounts a ON al.account_id = a.id
|
|
916
|
-
`;
|
|
917
|
-
const params = [];
|
|
918
|
-
if (accountId !== void 0) {
|
|
919
|
-
sql += " WHERE al.account_id = $1";
|
|
920
|
-
params.push(accountId);
|
|
921
|
-
}
|
|
922
|
-
sql += ` ORDER BY al.created_at DESC LIMIT $${params.length + 1}`;
|
|
923
|
-
params.push(Math.min(limit, 1e3));
|
|
924
|
-
const result = await this.config.db.query(sql, params);
|
|
925
|
-
return result.rows.map((row) => ({
|
|
926
|
-
...row,
|
|
927
|
-
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
928
|
-
}));
|
|
929
|
-
} catch (error) {
|
|
930
|
-
console.error("ActivityLogger: Failed to get recent activity:", error);
|
|
931
|
-
return [];
|
|
932
|
-
}
|
|
686
|
+
if (!email.trim()) {
|
|
687
|
+
throw new InvalidEmailError();
|
|
933
688
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
return {
|
|
937
|
-
totalEntries: 0,
|
|
938
|
-
uniqueUsers: 0,
|
|
939
|
-
recentLogins: 0,
|
|
940
|
-
failedAttempts: 0
|
|
941
|
-
};
|
|
942
|
-
}
|
|
943
|
-
try {
|
|
944
|
-
const [total, unique, recent, failed] = await Promise.all([
|
|
945
|
-
this.config.db.query(`SELECT COUNT(*) as count FROM ${this.activityTable}`),
|
|
946
|
-
this.config.db.query(`SELECT COUNT(DISTINCT account_id) as count FROM ${this.activityTable} WHERE account_id IS NOT NULL`),
|
|
947
|
-
this.config.db.query(`SELECT COUNT(*) as count FROM ${this.activityTable} WHERE action = 'login' AND created_at > NOW() - INTERVAL '24 hours'`),
|
|
948
|
-
this.config.db.query(`SELECT COUNT(*) as count FROM ${this.activityTable} WHERE success = false AND created_at > NOW() - INTERVAL '24 hours'`)
|
|
949
|
-
]);
|
|
950
|
-
return {
|
|
951
|
-
totalEntries: parseInt(total.rows[0]?.count || "0"),
|
|
952
|
-
uniqueUsers: parseInt(unique.rows[0]?.count || "0"),
|
|
953
|
-
recentLogins: parseInt(recent.rows[0]?.count || "0"),
|
|
954
|
-
failedAttempts: parseInt(failed.rows[0]?.count || "0")
|
|
955
|
-
};
|
|
956
|
-
} catch (error) {
|
|
957
|
-
console.error("ActivityLogger: Failed to get activity stats:", error);
|
|
958
|
-
return {
|
|
959
|
-
totalEntries: 0,
|
|
960
|
-
uniqueUsers: 0,
|
|
961
|
-
recentLogins: 0,
|
|
962
|
-
failedAttempts: 0
|
|
963
|
-
};
|
|
964
|
-
}
|
|
689
|
+
if (!isValidEmail(email)) {
|
|
690
|
+
throw new InvalidEmailError();
|
|
965
691
|
}
|
|
966
692
|
};
|
|
693
|
+
var createMapFromEnum = (enumObj) => Object.fromEntries(Object.entries(enumObj).map(([key, value]) => [value, key]));
|
|
967
694
|
|
|
968
695
|
// src/providers/base-provider.ts
|
|
969
696
|
var BaseOAuthProvider = class {
|
|
@@ -1208,7 +935,7 @@ var AzureProvider = class extends BaseOAuthProvider {
|
|
|
1208
935
|
|
|
1209
936
|
// src/two-factor/totp-provider.ts
|
|
1210
937
|
var import_totp = __toESM(require("@eaccess/totp"), 1);
|
|
1211
|
-
var
|
|
938
|
+
var import_hash = require("@prsm/hash");
|
|
1212
939
|
var TotpProvider = class {
|
|
1213
940
|
constructor(config) {
|
|
1214
941
|
this.config = config;
|
|
@@ -1237,11 +964,11 @@ var TotpProvider = class {
|
|
|
1237
964
|
return codes;
|
|
1238
965
|
}
|
|
1239
966
|
hashBackupCodes(codes) {
|
|
1240
|
-
return codes.map((code) =>
|
|
967
|
+
return codes.map((code) => import_hash.hash.encode(code));
|
|
1241
968
|
}
|
|
1242
969
|
verifyBackupCode(hashedCodes, inputCode) {
|
|
1243
970
|
for (let i = 0; i < hashedCodes.length; i++) {
|
|
1244
|
-
if (
|
|
971
|
+
if (import_hash.hash.verify(hashedCodes[i], inputCode.toUpperCase())) {
|
|
1245
972
|
return { isValid: true, index: i };
|
|
1246
973
|
}
|
|
1247
974
|
}
|
|
@@ -1257,8 +984,8 @@ var TotpProvider = class {
|
|
|
1257
984
|
};
|
|
1258
985
|
|
|
1259
986
|
// src/two-factor/otp-provider.ts
|
|
1260
|
-
var
|
|
1261
|
-
var
|
|
987
|
+
var import_ms = __toESM(require("@prsm/ms"), 1);
|
|
988
|
+
var import_hash2 = require("@prsm/hash");
|
|
1262
989
|
var OtpProvider = class {
|
|
1263
990
|
constructor(config) {
|
|
1264
991
|
this.config = config;
|
|
@@ -1283,9 +1010,9 @@ var OtpProvider = class {
|
|
|
1283
1010
|
async createAndStoreOTP(accountId, mechanism) {
|
|
1284
1011
|
const otp = this.generateOTP();
|
|
1285
1012
|
const selector = this.generateSelector();
|
|
1286
|
-
const tokenHash =
|
|
1013
|
+
const tokenHash = import_hash2.hash.encode(otp);
|
|
1287
1014
|
const expiryDuration = this.config.twoFactor?.tokenExpiry || "5m";
|
|
1288
|
-
const expiresAt = new Date(Date.now() + (0,
|
|
1015
|
+
const expiresAt = new Date(Date.now() + (0, import_ms.default)(expiryDuration));
|
|
1289
1016
|
await this.queries.deleteTwoFactorTokensByAccountAndMechanism(accountId, mechanism);
|
|
1290
1017
|
await this.queries.createTwoFactorToken({
|
|
1291
1018
|
accountId,
|
|
@@ -1305,7 +1032,7 @@ var OtpProvider = class {
|
|
|
1305
1032
|
await this.queries.deleteTwoFactorToken(token.id);
|
|
1306
1033
|
return { isValid: false };
|
|
1307
1034
|
}
|
|
1308
|
-
const isValid =
|
|
1035
|
+
const isValid = import_hash2.hash.verify(token.token_hash, inputCode);
|
|
1309
1036
|
if (isValid) {
|
|
1310
1037
|
await this.queries.deleteTwoFactorToken(token.id);
|
|
1311
1038
|
return { isValid: true, token };
|
|
@@ -1703,6 +1430,248 @@ var TwoFactorManager = class {
|
|
|
1703
1430
|
}
|
|
1704
1431
|
};
|
|
1705
1432
|
|
|
1433
|
+
// src/auth-functions.ts
|
|
1434
|
+
var auth_functions_exports = {};
|
|
1435
|
+
__export(auth_functions_exports, {
|
|
1436
|
+
addRoleForUserBy: () => addRoleForUserBy,
|
|
1437
|
+
changePasswordForUserBy: () => changePasswordForUserBy,
|
|
1438
|
+
confirmResetPassword: () => confirmResetPassword,
|
|
1439
|
+
createUser: () => createUser,
|
|
1440
|
+
deleteUserBy: () => deleteUserBy,
|
|
1441
|
+
forceLogoutForUserBy: () => forceLogoutForUserBy,
|
|
1442
|
+
hasRoleForUserBy: () => hasRoleForUserBy,
|
|
1443
|
+
initiatePasswordResetForUserBy: () => initiatePasswordResetForUserBy,
|
|
1444
|
+
register: () => register,
|
|
1445
|
+
removeRoleForUserBy: () => removeRoleForUserBy,
|
|
1446
|
+
resetPassword: () => resetPassword,
|
|
1447
|
+
setStatusForUserBy: () => setStatusForUserBy
|
|
1448
|
+
});
|
|
1449
|
+
var import_hash3 = require("@prsm/hash");
|
|
1450
|
+
var import_ms2 = __toESM(require("@prsm/ms"), 1);
|
|
1451
|
+
function validatePassword(password, config) {
|
|
1452
|
+
const minLength = config.minPasswordLength || 8;
|
|
1453
|
+
const maxLength = config.maxPasswordLength || 64;
|
|
1454
|
+
if (typeof password !== "string") {
|
|
1455
|
+
throw new InvalidPasswordError();
|
|
1456
|
+
}
|
|
1457
|
+
if (password.length < minLength) {
|
|
1458
|
+
throw new InvalidPasswordError();
|
|
1459
|
+
}
|
|
1460
|
+
if (password.length > maxLength) {
|
|
1461
|
+
throw new InvalidPasswordError();
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
function generateAutoUserId() {
|
|
1465
|
+
return crypto.randomUUID();
|
|
1466
|
+
}
|
|
1467
|
+
async function findAccountByIdentifier(queries, identifier) {
|
|
1468
|
+
if (identifier.accountId !== void 0) {
|
|
1469
|
+
return await queries.findAccountById(identifier.accountId);
|
|
1470
|
+
} else if (identifier.email !== void 0) {
|
|
1471
|
+
return await queries.findAccountByEmail(identifier.email);
|
|
1472
|
+
} else if (identifier.userId !== void 0) {
|
|
1473
|
+
return await queries.findAccountByUserId(identifier.userId);
|
|
1474
|
+
}
|
|
1475
|
+
return null;
|
|
1476
|
+
}
|
|
1477
|
+
async function createConfirmationToken(queries, account, email, callback) {
|
|
1478
|
+
const token = import_hash3.hash.encode(email);
|
|
1479
|
+
const expires = new Date(Date.now() + 1e3 * 60 * 60 * 24 * 7);
|
|
1480
|
+
await queries.createConfirmation({
|
|
1481
|
+
accountId: account.id,
|
|
1482
|
+
token,
|
|
1483
|
+
email,
|
|
1484
|
+
expires
|
|
1485
|
+
});
|
|
1486
|
+
if (callback) {
|
|
1487
|
+
callback(token);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
async function createUser(config, credentials, userId, callback) {
|
|
1491
|
+
validateEmail(credentials.email);
|
|
1492
|
+
validatePassword(credentials.password, config);
|
|
1493
|
+
const queries = new AuthQueries(config);
|
|
1494
|
+
const existing = await queries.findAccountByEmail(credentials.email);
|
|
1495
|
+
if (existing) {
|
|
1496
|
+
throw new EmailTakenError();
|
|
1497
|
+
}
|
|
1498
|
+
const finalUserId = userId || generateAutoUserId();
|
|
1499
|
+
const hashedPassword = import_hash3.hash.encode(credentials.password);
|
|
1500
|
+
const verified = typeof callback !== "function";
|
|
1501
|
+
const account = await queries.createAccount({
|
|
1502
|
+
userId: finalUserId,
|
|
1503
|
+
email: credentials.email,
|
|
1504
|
+
password: hashedPassword,
|
|
1505
|
+
verified,
|
|
1506
|
+
status: AuthStatus.Normal,
|
|
1507
|
+
rolemask: 0
|
|
1508
|
+
});
|
|
1509
|
+
if (!verified && callback) {
|
|
1510
|
+
await createConfirmationToken(queries, account, credentials.email, callback);
|
|
1511
|
+
}
|
|
1512
|
+
return account;
|
|
1513
|
+
}
|
|
1514
|
+
async function register(config, email, password, userId, callback) {
|
|
1515
|
+
validateEmail(email);
|
|
1516
|
+
validatePassword(password, config);
|
|
1517
|
+
const queries = new AuthQueries(config);
|
|
1518
|
+
const existing = await queries.findAccountByEmail(email);
|
|
1519
|
+
if (existing) {
|
|
1520
|
+
throw new EmailTakenError();
|
|
1521
|
+
}
|
|
1522
|
+
const finalUserId = userId || generateAutoUserId();
|
|
1523
|
+
const hashedPassword = import_hash3.hash.encode(password);
|
|
1524
|
+
const verified = typeof callback !== "function";
|
|
1525
|
+
const account = await queries.createAccount({
|
|
1526
|
+
userId: finalUserId,
|
|
1527
|
+
email,
|
|
1528
|
+
password: hashedPassword,
|
|
1529
|
+
verified,
|
|
1530
|
+
status: AuthStatus.Normal,
|
|
1531
|
+
rolemask: 0
|
|
1532
|
+
});
|
|
1533
|
+
if (!verified && callback) {
|
|
1534
|
+
await createConfirmationToken(queries, account, email, callback);
|
|
1535
|
+
}
|
|
1536
|
+
return account;
|
|
1537
|
+
}
|
|
1538
|
+
async function deleteUserBy(config, identifier) {
|
|
1539
|
+
const queries = new AuthQueries(config);
|
|
1540
|
+
const account = await findAccountByIdentifier(queries, identifier);
|
|
1541
|
+
if (!account) {
|
|
1542
|
+
throw new UserNotFoundError();
|
|
1543
|
+
}
|
|
1544
|
+
await queries.deleteAccount(account.id);
|
|
1545
|
+
}
|
|
1546
|
+
async function addRoleForUserBy(config, identifier, role) {
|
|
1547
|
+
const queries = new AuthQueries(config);
|
|
1548
|
+
const account = await findAccountByIdentifier(queries, identifier);
|
|
1549
|
+
if (!account) {
|
|
1550
|
+
throw new UserNotFoundError();
|
|
1551
|
+
}
|
|
1552
|
+
const rolemask = account.rolemask | role;
|
|
1553
|
+
await queries.updateAccount(account.id, { rolemask });
|
|
1554
|
+
}
|
|
1555
|
+
async function removeRoleForUserBy(config, identifier, role) {
|
|
1556
|
+
const queries = new AuthQueries(config);
|
|
1557
|
+
const account = await findAccountByIdentifier(queries, identifier);
|
|
1558
|
+
if (!account) {
|
|
1559
|
+
throw new UserNotFoundError();
|
|
1560
|
+
}
|
|
1561
|
+
const rolemask = account.rolemask & ~role;
|
|
1562
|
+
await queries.updateAccount(account.id, { rolemask });
|
|
1563
|
+
}
|
|
1564
|
+
async function hasRoleForUserBy(config, identifier, role) {
|
|
1565
|
+
const queries = new AuthQueries(config);
|
|
1566
|
+
const account = await findAccountByIdentifier(queries, identifier);
|
|
1567
|
+
if (!account) {
|
|
1568
|
+
throw new UserNotFoundError();
|
|
1569
|
+
}
|
|
1570
|
+
return (account.rolemask & role) === role;
|
|
1571
|
+
}
|
|
1572
|
+
async function changePasswordForUserBy(config, identifier, password) {
|
|
1573
|
+
validatePassword(password, config);
|
|
1574
|
+
const queries = new AuthQueries(config);
|
|
1575
|
+
const account = await findAccountByIdentifier(queries, identifier);
|
|
1576
|
+
if (!account) {
|
|
1577
|
+
throw new UserNotFoundError();
|
|
1578
|
+
}
|
|
1579
|
+
await queries.updateAccount(account.id, {
|
|
1580
|
+
password: import_hash3.hash.encode(password)
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
async function setStatusForUserBy(config, identifier, status) {
|
|
1584
|
+
const queries = new AuthQueries(config);
|
|
1585
|
+
const account = await findAccountByIdentifier(queries, identifier);
|
|
1586
|
+
if (!account) {
|
|
1587
|
+
throw new UserNotFoundError();
|
|
1588
|
+
}
|
|
1589
|
+
await queries.updateAccount(account.id, { status });
|
|
1590
|
+
}
|
|
1591
|
+
async function initiatePasswordResetForUserBy(config, identifier, expiresAfter = null, callback) {
|
|
1592
|
+
const queries = new AuthQueries(config);
|
|
1593
|
+
const account = await findAccountByIdentifier(queries, identifier);
|
|
1594
|
+
if (!account) {
|
|
1595
|
+
throw new UserNotFoundError();
|
|
1596
|
+
}
|
|
1597
|
+
if (!account.verified) {
|
|
1598
|
+
throw new EmailNotVerifiedError();
|
|
1599
|
+
}
|
|
1600
|
+
const expiry = !expiresAfter ? (0, import_ms2.default)("6h") : (0, import_ms2.default)(expiresAfter);
|
|
1601
|
+
const token = import_hash3.hash.encode(account.email);
|
|
1602
|
+
const expires = new Date(Date.now() + expiry);
|
|
1603
|
+
await queries.createResetToken({
|
|
1604
|
+
accountId: account.id,
|
|
1605
|
+
token,
|
|
1606
|
+
expires
|
|
1607
|
+
});
|
|
1608
|
+
if (callback) {
|
|
1609
|
+
callback(token);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
async function resetPassword(config, email, expiresAfter = null, maxOpenRequests = null, callback) {
|
|
1613
|
+
validateEmail(email);
|
|
1614
|
+
const expiry = !expiresAfter ? (0, import_ms2.default)("6h") : (0, import_ms2.default)(expiresAfter);
|
|
1615
|
+
const maxRequests = maxOpenRequests === null ? 2 : Math.max(1, maxOpenRequests);
|
|
1616
|
+
const queries = new AuthQueries(config);
|
|
1617
|
+
const account = await queries.findAccountByEmail(email);
|
|
1618
|
+
if (!account || !account.verified) {
|
|
1619
|
+
throw new EmailNotVerifiedError();
|
|
1620
|
+
}
|
|
1621
|
+
if (!account.resettable) {
|
|
1622
|
+
throw new ResetDisabledError();
|
|
1623
|
+
}
|
|
1624
|
+
const openRequests = await queries.countActiveResetTokensForAccount(account.id);
|
|
1625
|
+
if (openRequests >= maxRequests) {
|
|
1626
|
+
throw new TooManyResetsError();
|
|
1627
|
+
}
|
|
1628
|
+
const token = import_hash3.hash.encode(email);
|
|
1629
|
+
const expires = new Date(Date.now() + expiry);
|
|
1630
|
+
await queries.createResetToken({
|
|
1631
|
+
accountId: account.id,
|
|
1632
|
+
token,
|
|
1633
|
+
expires
|
|
1634
|
+
});
|
|
1635
|
+
if (callback) {
|
|
1636
|
+
callback(token);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
async function confirmResetPassword(config, token, password) {
|
|
1640
|
+
const queries = new AuthQueries(config);
|
|
1641
|
+
const reset = await queries.findResetToken(token);
|
|
1642
|
+
if (!reset) {
|
|
1643
|
+
throw new ResetNotFoundError();
|
|
1644
|
+
}
|
|
1645
|
+
if (new Date(reset.expires) < /* @__PURE__ */ new Date()) {
|
|
1646
|
+
throw new ResetExpiredError();
|
|
1647
|
+
}
|
|
1648
|
+
const account = await queries.findAccountById(reset.account_id);
|
|
1649
|
+
if (!account) {
|
|
1650
|
+
throw new UserNotFoundError();
|
|
1651
|
+
}
|
|
1652
|
+
if (!account.resettable) {
|
|
1653
|
+
throw new ResetDisabledError();
|
|
1654
|
+
}
|
|
1655
|
+
validatePassword(password, config);
|
|
1656
|
+
if (!import_hash3.hash.verify(token, account.email)) {
|
|
1657
|
+
throw new InvalidTokenError();
|
|
1658
|
+
}
|
|
1659
|
+
await queries.updateAccount(account.id, {
|
|
1660
|
+
password: import_hash3.hash.encode(password)
|
|
1661
|
+
});
|
|
1662
|
+
await queries.deleteResetToken(token);
|
|
1663
|
+
return { accountId: account.id, email: account.email };
|
|
1664
|
+
}
|
|
1665
|
+
async function forceLogoutForUserBy(config, identifier) {
|
|
1666
|
+
const queries = new AuthQueries(config);
|
|
1667
|
+
const account = await findAccountByIdentifier(queries, identifier);
|
|
1668
|
+
if (!account) {
|
|
1669
|
+
throw new UserNotFoundError();
|
|
1670
|
+
}
|
|
1671
|
+
await queries.incrementForceLogout(account.id);
|
|
1672
|
+
return { accountId: account.id };
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1706
1675
|
// src/auth-manager.ts
|
|
1707
1676
|
var AuthManager = class {
|
|
1708
1677
|
constructor(req, res, config) {
|
|
@@ -1894,7 +1863,7 @@ var AuthManager = class {
|
|
|
1894
1863
|
});
|
|
1895
1864
|
}
|
|
1896
1865
|
async createRememberDirective(account) {
|
|
1897
|
-
const token =
|
|
1866
|
+
const token = import_hash4.hash.encode(account.email);
|
|
1898
1867
|
const duration = this.config.rememberDuration || "30d";
|
|
1899
1868
|
const expires = new Date(Date.now() + (0, import_ms3.default)(duration));
|
|
1900
1869
|
await this.queries.createRememberToken({
|
|
@@ -1932,7 +1901,7 @@ var AuthManager = class {
|
|
|
1932
1901
|
await this.activityLogger.logActivity(null, AuthActivityAction.FailedLogin, this.req, false, { email, reason: "account_not_found" });
|
|
1933
1902
|
throw new UserNotFoundError();
|
|
1934
1903
|
}
|
|
1935
|
-
if (!account.password || !
|
|
1904
|
+
if (!account.password || !import_hash4.hash.verify(account.password, password)) {
|
|
1936
1905
|
await this.activityLogger.logActivity(account.id, AuthActivityAction.FailedLogin, this.req, false, { email, reason: "invalid_password" });
|
|
1937
1906
|
throw new InvalidPasswordError();
|
|
1938
1907
|
}
|
|
@@ -2044,7 +2013,7 @@ var AuthManager = class {
|
|
|
2044
2013
|
throw new EmailTakenError();
|
|
2045
2014
|
}
|
|
2046
2015
|
const finalUserId = userId || this.generateAutoUserId();
|
|
2047
|
-
const hashedPassword =
|
|
2016
|
+
const hashedPassword = import_hash4.hash.encode(password);
|
|
2048
2017
|
const verified = typeof callback !== "function";
|
|
2049
2018
|
const account = await this.queries.createAccount({
|
|
2050
2019
|
userId: finalUserId,
|
|
@@ -2061,7 +2030,7 @@ var AuthManager = class {
|
|
|
2061
2030
|
return account;
|
|
2062
2031
|
}
|
|
2063
2032
|
async createConfirmationToken(account, email, callback) {
|
|
2064
|
-
const token =
|
|
2033
|
+
const token = import_hash4.hash.encode(email);
|
|
2065
2034
|
const expires = new Date(Date.now() + 1e3 * 60 * 60 * 24 * 7);
|
|
2066
2035
|
await this.queries.createConfirmation({
|
|
2067
2036
|
accountId: account.id,
|
|
@@ -2195,7 +2164,7 @@ var AuthManager = class {
|
|
|
2195
2164
|
if (new Date(confirmation.expires) < /* @__PURE__ */ new Date()) {
|
|
2196
2165
|
throw new ConfirmationExpiredError();
|
|
2197
2166
|
}
|
|
2198
|
-
if (!
|
|
2167
|
+
if (!import_hash4.hash.verify(token, confirmation.email)) {
|
|
2199
2168
|
throw new InvalidTokenError();
|
|
2200
2169
|
}
|
|
2201
2170
|
await this.queries.updateAccount(confirmation.account_id, {
|
|
@@ -2292,7 +2261,7 @@ var AuthManager = class {
|
|
|
2292
2261
|
if (openRequests >= maxRequests) {
|
|
2293
2262
|
throw new TooManyResetsError();
|
|
2294
2263
|
}
|
|
2295
|
-
const token =
|
|
2264
|
+
const token = import_hash4.hash.encode(email);
|
|
2296
2265
|
const expires = new Date(Date.now() + expiry);
|
|
2297
2266
|
await this.queries.createResetToken({
|
|
2298
2267
|
accountId: account.id,
|
|
@@ -2334,11 +2303,11 @@ var AuthManager = class {
|
|
|
2334
2303
|
throw new ResetDisabledError();
|
|
2335
2304
|
}
|
|
2336
2305
|
this.validatePassword(password);
|
|
2337
|
-
if (!
|
|
2306
|
+
if (!import_hash4.hash.verify(token, account.email)) {
|
|
2338
2307
|
throw new InvalidTokenError();
|
|
2339
2308
|
}
|
|
2340
2309
|
await this.queries.updateAccount(account.id, {
|
|
2341
|
-
password:
|
|
2310
|
+
password: import_hash4.hash.encode(password)
|
|
2342
2311
|
});
|
|
2343
2312
|
if (logout) {
|
|
2344
2313
|
await this.forceLogoutForAccountById(account.id);
|
|
@@ -2366,7 +2335,7 @@ var AuthManager = class {
|
|
|
2366
2335
|
if (!account.password) {
|
|
2367
2336
|
return false;
|
|
2368
2337
|
}
|
|
2369
|
-
return
|
|
2338
|
+
return import_hash4.hash.verify(account.password, password);
|
|
2370
2339
|
}
|
|
2371
2340
|
async forceLogoutForAccountById(accountId) {
|
|
2372
2341
|
await this.queries.deleteRememberTokensForAccount(accountId);
|
|
@@ -2404,6 +2373,61 @@ var AuthManager = class {
|
|
|
2404
2373
|
await this.logoutEverywhereElse();
|
|
2405
2374
|
await this.logout();
|
|
2406
2375
|
}
|
|
2376
|
+
async findAccountByIdentifier(identifier) {
|
|
2377
|
+
if (identifier.accountId !== void 0) {
|
|
2378
|
+
return await this.queries.findAccountById(identifier.accountId);
|
|
2379
|
+
} else if (identifier.email !== void 0) {
|
|
2380
|
+
return await this.queries.findAccountByEmail(identifier.email);
|
|
2381
|
+
} else if (identifier.userId !== void 0) {
|
|
2382
|
+
return await this.queries.findAccountByUserId(identifier.userId);
|
|
2383
|
+
}
|
|
2384
|
+
return null;
|
|
2385
|
+
}
|
|
2386
|
+
// Admin/standalone functions (delegated to auth-functions.js)
|
|
2387
|
+
async createUser(credentials, userId, callback) {
|
|
2388
|
+
return createUser(this.config, credentials, userId, callback);
|
|
2389
|
+
}
|
|
2390
|
+
async deleteUserBy(identifier) {
|
|
2391
|
+
return deleteUserBy(this.config, identifier);
|
|
2392
|
+
}
|
|
2393
|
+
async addRoleForUserBy(identifier, role) {
|
|
2394
|
+
return addRoleForUserBy(this.config, identifier, role);
|
|
2395
|
+
}
|
|
2396
|
+
async removeRoleForUserBy(identifier, role) {
|
|
2397
|
+
return removeRoleForUserBy(this.config, identifier, role);
|
|
2398
|
+
}
|
|
2399
|
+
async hasRoleForUserBy(identifier, role) {
|
|
2400
|
+
return hasRoleForUserBy(this.config, identifier, role);
|
|
2401
|
+
}
|
|
2402
|
+
async changePasswordForUserBy(identifier, password) {
|
|
2403
|
+
return changePasswordForUserBy(this.config, identifier, password);
|
|
2404
|
+
}
|
|
2405
|
+
async setStatusForUserBy(identifier, status) {
|
|
2406
|
+
return setStatusForUserBy(this.config, identifier, status);
|
|
2407
|
+
}
|
|
2408
|
+
async initiatePasswordResetForUserBy(identifier, expiresAfter, callback) {
|
|
2409
|
+
return initiatePasswordResetForUserBy(this.config, identifier, expiresAfter, callback);
|
|
2410
|
+
}
|
|
2411
|
+
async forceLogoutForUserBy(identifier) {
|
|
2412
|
+
const result = await forceLogoutForUserBy(this.config, identifier);
|
|
2413
|
+
if (this.getId() === result.accountId) {
|
|
2414
|
+
this.req.session.auth.shouldForceLogout = true;
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
/**
|
|
2418
|
+
* Log in as another user (admin function).
|
|
2419
|
+
* Creates a new session as the target user without requiring their password.
|
|
2420
|
+
*
|
|
2421
|
+
* @param identifier - Find user by accountId, email, or userId
|
|
2422
|
+
* @throws {UserNotFoundError} No account matches the identifier
|
|
2423
|
+
*/
|
|
2424
|
+
async loginAsUserBy(identifier) {
|
|
2425
|
+
const account = await this.findAccountByIdentifier(identifier);
|
|
2426
|
+
if (!account) {
|
|
2427
|
+
throw new UserNotFoundError();
|
|
2428
|
+
}
|
|
2429
|
+
await this.onLoginSuccessful(account, false);
|
|
2430
|
+
}
|
|
2407
2431
|
};
|
|
2408
2432
|
|
|
2409
2433
|
// src/middleware.ts
|
|
@@ -2411,9 +2435,7 @@ function createAuthMiddleware(config) {
|
|
|
2411
2435
|
return async (req, res, next) => {
|
|
2412
2436
|
try {
|
|
2413
2437
|
const authManager = new AuthManager(req, res, config);
|
|
2414
|
-
const authAdminManager = new AuthAdminManager(req, res, config, authManager);
|
|
2415
2438
|
req.auth = authManager;
|
|
2416
|
-
req.authAdmin = authAdminManager;
|
|
2417
2439
|
await authManager.resyncSession();
|
|
2418
2440
|
await authManager.processRememberDirective();
|
|
2419
2441
|
next();
|
|
@@ -2621,6 +2643,62 @@ async function getAuthTableStats(config) {
|
|
|
2621
2643
|
expiredTwoFactorTokens: parseInt(expiredTwoFactorTokensResult.rows[0]?.count || "0")
|
|
2622
2644
|
};
|
|
2623
2645
|
}
|
|
2646
|
+
|
|
2647
|
+
// src/auth-context.ts
|
|
2648
|
+
function createAuthContext(config) {
|
|
2649
|
+
return {
|
|
2650
|
+
createUser: (credentials, userId, callback) => createUser(config, credentials, userId, callback),
|
|
2651
|
+
register: (email, password, userId, callback) => register(config, email, password, userId, callback),
|
|
2652
|
+
deleteUserBy: (identifier) => deleteUserBy(config, identifier),
|
|
2653
|
+
addRoleForUserBy: (identifier, role) => addRoleForUserBy(config, identifier, role),
|
|
2654
|
+
removeRoleForUserBy: (identifier, role) => removeRoleForUserBy(config, identifier, role),
|
|
2655
|
+
hasRoleForUserBy: (identifier, role) => hasRoleForUserBy(config, identifier, role),
|
|
2656
|
+
changePasswordForUserBy: (identifier, password) => changePasswordForUserBy(config, identifier, password),
|
|
2657
|
+
setStatusForUserBy: (identifier, status) => setStatusForUserBy(config, identifier, status),
|
|
2658
|
+
initiatePasswordResetForUserBy: (identifier, expiresAfter, callback) => initiatePasswordResetForUserBy(config, identifier, expiresAfter, callback),
|
|
2659
|
+
resetPassword: (email, expiresAfter, maxOpenRequests, callback) => resetPassword(config, email, expiresAfter, maxOpenRequests, callback),
|
|
2660
|
+
confirmResetPassword: (token, password) => confirmResetPassword(config, token, password),
|
|
2661
|
+
forceLogoutForUserBy: (identifier) => forceLogoutForUserBy(config, identifier)
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
// src/user-roles.ts
|
|
2666
|
+
async function findAccountByIdentifier2(queries, identifier) {
|
|
2667
|
+
let account = null;
|
|
2668
|
+
if (identifier.accountId !== void 0) {
|
|
2669
|
+
account = await queries.findAccountById(identifier.accountId);
|
|
2670
|
+
} else if (identifier.email !== void 0) {
|
|
2671
|
+
account = await queries.findAccountByEmail(identifier.email);
|
|
2672
|
+
} else if (identifier.userId !== void 0) {
|
|
2673
|
+
account = await queries.findAccountByUserId(identifier.userId);
|
|
2674
|
+
}
|
|
2675
|
+
if (!account) {
|
|
2676
|
+
throw new UserNotFoundError();
|
|
2677
|
+
}
|
|
2678
|
+
return account;
|
|
2679
|
+
}
|
|
2680
|
+
async function addRoleToUser(config, identifier, role) {
|
|
2681
|
+
const queries = new AuthQueries(config);
|
|
2682
|
+
const account = await findAccountByIdentifier2(queries, identifier);
|
|
2683
|
+
const rolemask = account.rolemask | role;
|
|
2684
|
+
await queries.updateAccount(account.id, { rolemask });
|
|
2685
|
+
}
|
|
2686
|
+
async function removeRoleFromUser(config, identifier, role) {
|
|
2687
|
+
const queries = new AuthQueries(config);
|
|
2688
|
+
const account = await findAccountByIdentifier2(queries, identifier);
|
|
2689
|
+
const rolemask = account.rolemask & ~role;
|
|
2690
|
+
await queries.updateAccount(account.id, { rolemask });
|
|
2691
|
+
}
|
|
2692
|
+
async function setUserRoles(config, identifier, rolemask) {
|
|
2693
|
+
const queries = new AuthQueries(config);
|
|
2694
|
+
const account = await findAccountByIdentifier2(queries, identifier);
|
|
2695
|
+
await queries.updateAccount(account.id, { rolemask });
|
|
2696
|
+
}
|
|
2697
|
+
async function getUserRoles(config, identifier) {
|
|
2698
|
+
const queries = new AuthQueries(config);
|
|
2699
|
+
const account = await findAccountByIdentifier2(queries, identifier);
|
|
2700
|
+
return account.rolemask;
|
|
2701
|
+
}
|
|
2624
2702
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2625
2703
|
0 && (module.exports = {
|
|
2626
2704
|
ActivityLogger,
|
|
@@ -2657,13 +2735,18 @@ async function getAuthTableStats(config) {
|
|
|
2657
2735
|
UserInactiveError,
|
|
2658
2736
|
UserNotFoundError,
|
|
2659
2737
|
UserNotLoggedInError,
|
|
2738
|
+
addRoleToUser,
|
|
2739
|
+
authFunctions,
|
|
2660
2740
|
cleanupExpiredTokens,
|
|
2741
|
+
createAuthContext,
|
|
2661
2742
|
createAuthMiddleware,
|
|
2662
2743
|
createAuthTables,
|
|
2663
|
-
createUser,
|
|
2664
2744
|
dropAuthTables,
|
|
2665
2745
|
getAuthTableStats,
|
|
2746
|
+
getUserRoles,
|
|
2666
2747
|
isValidEmail,
|
|
2748
|
+
removeRoleFromUser,
|
|
2749
|
+
setUserRoles,
|
|
2667
2750
|
validateEmail
|
|
2668
2751
|
});
|
|
2669
2752
|
//# sourceMappingURL=index.cjs.map
|