@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 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-admin-manager.ts
79
- var import_hash2 = require("@prsm/hash");
80
- var import_ms = __toESM(require("@prsm/ms"), 1);
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
- async getRecentActivity(limit = 100, accountId) {
908
- if (!this.enabled) return [];
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
- async getActivityStats() {
935
- if (!this.enabled) {
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 import_hash3 = require("@prsm/hash");
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) => import_hash3.hash.encode(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 (import_hash3.hash.verify(hashedCodes[i], inputCode.toUpperCase())) {
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 import_ms2 = __toESM(require("@prsm/ms"), 1);
1261
- var import_hash4 = require("@prsm/hash");
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 = import_hash4.hash.encode(otp);
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, import_ms2.default)(expiryDuration));
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 = import_hash4.hash.verify(token.token_hash, inputCode);
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 = import_hash5.hash.encode(account.email);
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 || !import_hash5.hash.verify(account.password, 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 = import_hash5.hash.encode(password);
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 = import_hash5.hash.encode(email);
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 (!import_hash5.hash.verify(token, confirmation.email)) {
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 = import_hash5.hash.encode(email);
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 (!import_hash5.hash.verify(token, account.email)) {
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: import_hash5.hash.encode(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 import_hash5.hash.verify(account.password, password);
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