@eaccess/auth 0.1.20 → 1.0.0

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
@@ -67,6 +67,7 @@ __export(index_exports, {
67
67
  addRoleForUserBy: () => addRoleForUserBy,
68
68
  addRoleToUser: () => addRoleToUser,
69
69
  authFunctions: () => auth_functions_exports,
70
+ authenticateRequest: () => authenticateRequest,
70
71
  changePasswordForUserBy: () => changePasswordForUserBy,
71
72
  cleanupExpiredTokens: () => cleanupExpiredTokens,
72
73
  confirmResetPassword: () => confirmResetPassword,
@@ -74,6 +75,7 @@ __export(index_exports, {
74
75
  createAuthMiddleware: () => createAuthMiddleware,
75
76
  createAuthTables: () => createAuthTables,
76
77
  createUser: () => createUser,
78
+ defineRoles: () => defineRoles,
77
79
  deleteUserBy: () => deleteUserBy,
78
80
  dropAuthTables: () => dropAuthTables,
79
81
  forceLogoutForUserBy: () => forceLogoutForUserBy,
@@ -94,7 +96,7 @@ __export(index_exports, {
94
96
  module.exports = __toCommonJS(index_exports);
95
97
 
96
98
  // src/auth-manager.ts
97
- var import_hash4 = require("@prsm/hash");
99
+ var import_hash4 = __toESM(require("@prsm/hash"), 1);
98
100
  var import_ms3 = __toESM(require("@prsm/ms"), 1);
99
101
 
100
102
  // src/types.ts
@@ -810,7 +812,7 @@ var GitHubProvider = class extends BaseOAuthProvider {
810
812
  client_id: this.config.clientId,
811
813
  redirect_uri: this.config.redirectUri,
812
814
  scope: scopes?.join(" ") || "user:email",
813
- state: state || Math.random().toString(36).substring(2),
815
+ state: state || crypto.randomUUID(),
814
816
  response_type: "code"
815
817
  });
816
818
  return `https://github.com/login/oauth/authorize?${params}`;
@@ -859,7 +861,7 @@ var GoogleProvider = class extends BaseOAuthProvider {
859
861
  client_id: this.config.clientId,
860
862
  redirect_uri: this.config.redirectUri,
861
863
  scope: scopes?.join(" ") || "openid profile email",
862
- state: state || Math.random().toString(36).substring(2),
864
+ state: state || crypto.randomUUID(),
863
865
  response_type: "code",
864
866
  access_type: "offline",
865
867
  prompt: "consent"
@@ -901,7 +903,7 @@ var AzureProvider = class extends BaseOAuthProvider {
901
903
  client_id: azureConfig.clientId,
902
904
  redirect_uri: azureConfig.redirectUri,
903
905
  scope: scopes?.join(" ") || "openid profile email User.Read",
904
- state: state || Math.random().toString(36).substring(2),
906
+ state: state || crypto.randomUUID(),
905
907
  response_type: "code",
906
908
  response_mode: "query"
907
909
  });
@@ -960,7 +962,7 @@ var AzureProvider = class extends BaseOAuthProvider {
960
962
 
961
963
  // src/two-factor/totp-provider.ts
962
964
  var import_totp = __toESM(require("@eaccess/totp"), 1);
963
- var import_hash = require("@prsm/hash");
965
+ var import_hash = __toESM(require("@prsm/hash"), 1);
964
966
  var TotpProvider = class {
965
967
  constructor(config) {
966
968
  this.config = config;
@@ -977,23 +979,20 @@ var TotpProvider = class {
977
979
  return import_totp.default.verifyTotp(secret, code, window);
978
980
  }
979
981
  generateBackupCodes(count = 10) {
982
+ const chars = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
980
983
  const codes = [];
981
984
  for (let i = 0; i < count; i++) {
982
- const chars = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
983
- let code = "";
984
- for (let j = 0; j < 8; j++) {
985
- code += chars.charAt(Math.floor(Math.random() * chars.length));
986
- }
987
- codes.push(code);
985
+ const bytes = crypto.getRandomValues(new Uint8Array(8));
986
+ codes.push(Array.from(bytes, (b) => chars[b % chars.length]).join(""));
988
987
  }
989
988
  return codes;
990
989
  }
991
- hashBackupCodes(codes) {
992
- return codes.map((code) => import_hash.hash.encode(code));
990
+ async hashBackupCodes(codes) {
991
+ return await Promise.all(codes.map((code) => import_hash.default.encode(code)));
993
992
  }
994
- verifyBackupCode(hashedCodes, inputCode) {
993
+ async verifyBackupCode(hashedCodes, inputCode) {
995
994
  for (let i = 0; i < hashedCodes.length; i++) {
996
- if (import_hash.hash.verify(hashedCodes[i], inputCode.toUpperCase())) {
995
+ if (await import_hash.default.verify(hashedCodes[i], inputCode.toUpperCase())) {
997
996
  return { isValid: true, index: i };
998
997
  }
999
998
  }
@@ -1010,7 +1009,7 @@ var TotpProvider = class {
1010
1009
 
1011
1010
  // src/two-factor/otp-provider.ts
1012
1011
  var import_ms = __toESM(require("@prsm/ms"), 1);
1013
- var import_hash2 = require("@prsm/hash");
1012
+ var import_hash2 = __toESM(require("@prsm/hash"), 1);
1014
1013
  var OtpProvider = class {
1015
1014
  constructor(config) {
1016
1015
  this.config = config;
@@ -1018,24 +1017,16 @@ var OtpProvider = class {
1018
1017
  }
1019
1018
  generateOTP() {
1020
1019
  const length = this.config.twoFactor?.codeLength || 6;
1021
- let otp = "";
1022
- for (let i = 0; i < length; i++) {
1023
- otp += Math.floor(Math.random() * 10).toString();
1024
- }
1025
- return otp;
1020
+ const bytes = crypto.getRandomValues(new Uint8Array(length));
1021
+ return Array.from(bytes, (b) => (b % 10).toString()).join("");
1026
1022
  }
1027
1023
  generateSelector() {
1028
- const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1029
- let selector = "";
1030
- for (let i = 0; i < 32; i++) {
1031
- selector += chars.charAt(Math.floor(Math.random() * chars.length));
1032
- }
1033
- return selector;
1024
+ return crypto.randomUUID().replace(/-/g, "");
1034
1025
  }
1035
1026
  async createAndStoreOTP(accountId, mechanism) {
1036
1027
  const otp = this.generateOTP();
1037
1028
  const selector = this.generateSelector();
1038
- const tokenHash = import_hash2.hash.encode(otp);
1029
+ const tokenHash = await import_hash2.default.encode(otp);
1039
1030
  const expiryDuration = this.config.twoFactor?.tokenExpiry || "5m";
1040
1031
  const expiresAt = new Date(Date.now() + (0, import_ms.default)(expiryDuration));
1041
1032
  await this.queries.deleteTwoFactorTokensByAccountAndMechanism(accountId, mechanism);
@@ -1057,7 +1048,7 @@ var OtpProvider = class {
1057
1048
  await this.queries.deleteTwoFactorToken(token.id);
1058
1049
  return { isValid: false };
1059
1050
  }
1060
- const isValid = import_hash2.hash.verify(token.token_hash, inputCode);
1051
+ const isValid = await import_hash2.default.verify(token.token_hash, inputCode);
1061
1052
  if (isValid) {
1062
1053
  await this.queries.deleteTwoFactorToken(token.id);
1063
1054
  return { isValid: true, token };
@@ -1104,7 +1095,7 @@ var TwoFactorManager = class {
1104
1095
  const backupCodesCount = this.config.twoFactor?.backupCodesCount || 10;
1105
1096
  backupCodes = this.totpProvider.generateBackupCodes(backupCodesCount);
1106
1097
  }
1107
- const hashedBackupCodes = backupCodes ? this.totpProvider.hashBackupCodes(backupCodes) : void 0;
1098
+ const hashedBackupCodes = backupCodes ? await this.totpProvider.hashBackupCodes(backupCodes) : void 0;
1108
1099
  const verified = !requireVerification;
1109
1100
  if (existingMethod) {
1110
1101
  await this.queries.updateTwoFactorMethod(existingMethod.id, {
@@ -1202,7 +1193,7 @@ var TwoFactorManager = class {
1202
1193
  }
1203
1194
  const backupCodesCount = this.config.twoFactor?.backupCodesCount || 10;
1204
1195
  const backupCodes = this.totpProvider.generateBackupCodes(backupCodesCount);
1205
- const hashedBackupCodes = this.totpProvider.hashBackupCodes(backupCodes);
1196
+ const hashedBackupCodes = await this.totpProvider.hashBackupCodes(backupCodes);
1206
1197
  await this.queries.updateTwoFactorMethod(method.id, {
1207
1198
  verified: true,
1208
1199
  backup_codes: hashedBackupCodes,
@@ -1254,7 +1245,7 @@ var TwoFactorManager = class {
1254
1245
  if (!method || !method.verified || !method.backup_codes) {
1255
1246
  throw new TwoFactorNotSetupError();
1256
1247
  }
1257
- const { isValid, index } = this.totpProvider.verifyBackupCode(method.backup_codes, code);
1248
+ const { isValid, index } = await this.totpProvider.verifyBackupCode(method.backup_codes, code);
1258
1249
  if (!isValid) {
1259
1250
  await this.activityLogger.logActivity(twoFactorState.accountId, AuthActivityAction.TwoFactorFailed, this.req, false, { mechanism: "backup_code", reason: "invalid_code" });
1260
1251
  throw new InvalidBackupCodeError();
@@ -1403,7 +1394,7 @@ var TwoFactorManager = class {
1403
1394
  }
1404
1395
  const backupCodesCount = this.config.twoFactor?.backupCodesCount || 10;
1405
1396
  const backupCodes = this.totpProvider.generateBackupCodes(backupCodesCount);
1406
- const hashedBackupCodes = this.totpProvider.hashBackupCodes(backupCodes);
1397
+ const hashedBackupCodes = await this.totpProvider.hashBackupCodes(backupCodes);
1407
1398
  await this.queries.updateTwoFactorMethod(method.id, {
1408
1399
  backup_codes: hashedBackupCodes
1409
1400
  });
@@ -1471,6 +1462,7 @@ var TwoFactorManager = class {
1471
1462
  var auth_functions_exports = {};
1472
1463
  __export(auth_functions_exports, {
1473
1464
  addRoleForUserBy: () => addRoleForUserBy,
1465
+ authenticateRequest: () => authenticateRequest,
1474
1466
  changePasswordForUserBy: () => changePasswordForUserBy,
1475
1467
  confirmResetPassword: () => confirmResetPassword,
1476
1468
  createUser: () => createUser,
@@ -1484,8 +1476,50 @@ __export(auth_functions_exports, {
1484
1476
  setStatusForUserBy: () => setStatusForUserBy,
1485
1477
  userExistsByEmail: () => userExistsByEmail
1486
1478
  });
1487
- var import_hash3 = require("@prsm/hash");
1479
+ var import_hash3 = __toESM(require("@prsm/hash"), 1);
1488
1480
  var import_ms2 = __toESM(require("@prsm/ms"), 1);
1481
+ function parseCookies(cookieHeader) {
1482
+ const cookies = {};
1483
+ if (!cookieHeader) return cookies;
1484
+ for (const pair of cookieHeader.split(";")) {
1485
+ const idx = pair.indexOf("=");
1486
+ if (idx === -1) continue;
1487
+ const key = pair.slice(0, idx).trim();
1488
+ const value = pair.slice(idx + 1).trim();
1489
+ if (key) cookies[key] = decodeURIComponent(value);
1490
+ }
1491
+ return cookies;
1492
+ }
1493
+ async function authenticateRequest(config, req, sessionMiddleware) {
1494
+ const queries = new AuthQueries(config);
1495
+ if (sessionMiddleware) {
1496
+ await new Promise((resolve) => {
1497
+ sessionMiddleware(req, {}, resolve);
1498
+ });
1499
+ }
1500
+ const session = req.session;
1501
+ if (session?.auth?.loggedIn && session.auth.accountId) {
1502
+ const account2 = await queries.findAccountById(session.auth.accountId);
1503
+ if (account2 && account2.status === AuthStatus.Normal) {
1504
+ return { account: account2, source: "session" };
1505
+ }
1506
+ }
1507
+ const cookies = parseCookies(req.headers.cookie || "");
1508
+ const cookieName = config.rememberCookieName || "remember_token";
1509
+ const token = cookies[cookieName];
1510
+ if (!token) {
1511
+ return { account: null, source: null };
1512
+ }
1513
+ const remember = await queries.findRememberToken(token);
1514
+ if (!remember || /* @__PURE__ */ new Date() > remember.expires) {
1515
+ return { account: null, source: null };
1516
+ }
1517
+ const account = await queries.findAccountById(remember.account_id);
1518
+ if (!account || account.status !== AuthStatus.Normal) {
1519
+ return { account: null, source: null };
1520
+ }
1521
+ return { account, source: "remember" };
1522
+ }
1489
1523
  function validatePassword(password, config) {
1490
1524
  const minLength = config.minPasswordLength || 8;
1491
1525
  const maxLength = config.maxPasswordLength || 64;
@@ -1513,7 +1547,7 @@ async function findAccountByIdentifier(queries, identifier) {
1513
1547
  return null;
1514
1548
  }
1515
1549
  async function createConfirmationToken(queries, account, email, callback) {
1516
- const token = import_hash3.hash.encode(email);
1550
+ const token = await import_hash3.default.encode(email);
1517
1551
  const expires = new Date(Date.now() + 1e3 * 60 * 60 * 24 * 7);
1518
1552
  await queries.createConfirmation({
1519
1553
  accountId: account.id,
@@ -1534,7 +1568,7 @@ async function createUser(config, credentials, userId, callback) {
1534
1568
  throw new EmailTakenError();
1535
1569
  }
1536
1570
  const finalUserId = userId || generateAutoUserId();
1537
- const hashedPassword = import_hash3.hash.encode(credentials.password);
1571
+ const hashedPassword = await import_hash3.default.encode(credentials.password);
1538
1572
  const verified = typeof callback !== "function";
1539
1573
  const account = await queries.createAccount({
1540
1574
  userId: finalUserId,
@@ -1558,7 +1592,7 @@ async function register(config, email, password, userId, callback) {
1558
1592
  throw new EmailTakenError();
1559
1593
  }
1560
1594
  const finalUserId = userId || generateAutoUserId();
1561
- const hashedPassword = import_hash3.hash.encode(password);
1595
+ const hashedPassword = await import_hash3.default.encode(password);
1562
1596
  const verified = typeof callback !== "function";
1563
1597
  const account = await queries.createAccount({
1564
1598
  userId: finalUserId,
@@ -1615,7 +1649,7 @@ async function changePasswordForUserBy(config, identifier, password) {
1615
1649
  throw new UserNotFoundError();
1616
1650
  }
1617
1651
  await queries.updateAccount(account.id, {
1618
- password: import_hash3.hash.encode(password)
1652
+ password: await import_hash3.default.encode(password)
1619
1653
  });
1620
1654
  }
1621
1655
  async function setStatusForUserBy(config, identifier, status) {
@@ -1636,7 +1670,7 @@ async function initiatePasswordResetForUserBy(config, identifier, expiresAfter =
1636
1670
  throw new EmailNotVerifiedError();
1637
1671
  }
1638
1672
  const expiry = !expiresAfter ? (0, import_ms2.default)("6h") : (0, import_ms2.default)(expiresAfter);
1639
- const token = import_hash3.hash.encode(account.email);
1673
+ const token = await import_hash3.default.encode(account.email);
1640
1674
  const expires = new Date(Date.now() + expiry);
1641
1675
  await queries.createResetToken({
1642
1676
  accountId: account.id,
@@ -1663,7 +1697,7 @@ async function resetPassword(config, email, expiresAfter = null, maxOpenRequests
1663
1697
  if (openRequests >= maxRequests) {
1664
1698
  throw new TooManyResetsError();
1665
1699
  }
1666
- const token = import_hash3.hash.encode(email);
1700
+ const token = await import_hash3.default.encode(email);
1667
1701
  const expires = new Date(Date.now() + expiry);
1668
1702
  await queries.createResetToken({
1669
1703
  accountId: account.id,
@@ -1691,11 +1725,11 @@ async function confirmResetPassword(config, token, password) {
1691
1725
  throw new ResetDisabledError();
1692
1726
  }
1693
1727
  validatePassword(password, config);
1694
- if (!import_hash3.hash.verify(token, account.email)) {
1728
+ if (!await import_hash3.default.verify(token, account.email)) {
1695
1729
  throw new InvalidTokenError();
1696
1730
  }
1697
1731
  await queries.updateAccount(account.id, {
1698
- password: import_hash3.hash.encode(password)
1732
+ password: await import_hash3.default.encode(password)
1699
1733
  });
1700
1734
  await queries.deleteResetToken(token);
1701
1735
  return { accountId: account.id, email: account.email };
@@ -1765,7 +1799,7 @@ var AuthManager = class {
1765
1799
  }
1766
1800
  }
1767
1801
  getRoleMap() {
1768
- return createMapFromEnum(AuthRole);
1802
+ return createMapFromEnum(this.config.roles || AuthRole);
1769
1803
  }
1770
1804
  getStatusMap() {
1771
1805
  return createMapFromEnum(AuthStatus);
@@ -1916,7 +1950,7 @@ var AuthManager = class {
1916
1950
  });
1917
1951
  }
1918
1952
  async createRememberDirective(account) {
1919
- const token = import_hash4.hash.encode(account.email);
1953
+ const token = await import_hash4.default.encode(account.email);
1920
1954
  const duration = this.config.rememberDuration || "30d";
1921
1955
  const expires = new Date(Date.now() + (0, import_ms3.default)(duration));
1922
1956
  await this.queries.createRememberToken({
@@ -1954,7 +1988,7 @@ var AuthManager = class {
1954
1988
  await this.activityLogger.logActivity(null, AuthActivityAction.FailedLogin, this.req, false, { email, reason: "account_not_found" });
1955
1989
  throw new UserNotFoundError();
1956
1990
  }
1957
- if (!account.password || !import_hash4.hash.verify(account.password, password)) {
1991
+ if (!account.password || !await import_hash4.default.verify(account.password, password)) {
1958
1992
  await this.activityLogger.logActivity(account.id, AuthActivityAction.FailedLogin, this.req, false, { email, reason: "invalid_password" });
1959
1993
  throw new InvalidPasswordError();
1960
1994
  }
@@ -2067,7 +2101,7 @@ var AuthManager = class {
2067
2101
  throw new EmailTakenError();
2068
2102
  }
2069
2103
  const finalUserId = userId || this.generateAutoUserId();
2070
- const hashedPassword = import_hash4.hash.encode(password);
2104
+ const hashedPassword = await import_hash4.default.encode(password);
2071
2105
  const verified = typeof callback !== "function";
2072
2106
  const account = await this.queries.createAccount({
2073
2107
  userId: finalUserId,
@@ -2084,7 +2118,7 @@ var AuthManager = class {
2084
2118
  return account;
2085
2119
  }
2086
2120
  async createConfirmationToken(account, email, callback) {
2087
- const token = import_hash4.hash.encode(email);
2121
+ const token = await import_hash4.default.encode(email);
2088
2122
  const expires = new Date(Date.now() + 1e3 * 60 * 60 * 24 * 7);
2089
2123
  await this.queries.createConfirmation({
2090
2124
  accountId: account.id,
@@ -2226,7 +2260,7 @@ var AuthManager = class {
2226
2260
  if (new Date(confirmation.expires) < /* @__PURE__ */ new Date()) {
2227
2261
  throw new ConfirmationExpiredError();
2228
2262
  }
2229
- if (!import_hash4.hash.verify(token, confirmation.email)) {
2263
+ if (!await import_hash4.default.verify(token, confirmation.email)) {
2230
2264
  throw new InvalidTokenError();
2231
2265
  }
2232
2266
  await this.queries.updateAccount(confirmation.account_id, {
@@ -2324,7 +2358,7 @@ var AuthManager = class {
2324
2358
  if (openRequests >= maxRequests) {
2325
2359
  throw new TooManyResetsError();
2326
2360
  }
2327
- const token = import_hash4.hash.encode(email);
2361
+ const token = await import_hash4.default.encode(email);
2328
2362
  const expires = new Date(Date.now() + expiry);
2329
2363
  await this.queries.createResetToken({
2330
2364
  accountId: account.id,
@@ -2366,11 +2400,11 @@ var AuthManager = class {
2366
2400
  throw new ResetDisabledError();
2367
2401
  }
2368
2402
  this.validatePassword(password);
2369
- if (!import_hash4.hash.verify(token, account.email)) {
2403
+ if (!await import_hash4.default.verify(token, account.email)) {
2370
2404
  throw new InvalidTokenError();
2371
2405
  }
2372
2406
  await this.queries.updateAccount(account.id, {
2373
- password: import_hash4.hash.encode(password)
2407
+ password: await import_hash4.default.encode(password)
2374
2408
  });
2375
2409
  if (logout) {
2376
2410
  await this.forceLogoutForAccountById(account.id);
@@ -2398,7 +2432,7 @@ var AuthManager = class {
2398
2432
  if (!account.password) {
2399
2433
  return false;
2400
2434
  }
2401
- return import_hash4.hash.verify(account.password, password);
2435
+ return await import_hash4.default.verify(account.password, password);
2402
2436
  }
2403
2437
  async forceLogoutForAccountById(accountId) {
2404
2438
  await this.queries.deleteRememberTokensForAccount(accountId);
@@ -2730,6 +2764,26 @@ function createAuthContext(config) {
2730
2764
  }
2731
2765
 
2732
2766
  // src/user-roles.ts
2767
+ var MAX_ROLES = 31;
2768
+ function defineRoles(...names) {
2769
+ if (names.length > MAX_ROLES) {
2770
+ throw new Error(`Cannot define more than ${MAX_ROLES} roles (postgres INTEGER is 32-bit signed)`);
2771
+ }
2772
+ if (names.length === 0) {
2773
+ throw new Error("At least one role name is required");
2774
+ }
2775
+ const seen = /* @__PURE__ */ new Set();
2776
+ const roles = {};
2777
+ for (let i = 0; i < names.length; i++) {
2778
+ const name = names[i];
2779
+ if (seen.has(name)) {
2780
+ throw new Error(`Duplicate role name: ${name}`);
2781
+ }
2782
+ seen.add(name);
2783
+ roles[name] = 1 << i;
2784
+ }
2785
+ return Object.freeze(roles);
2786
+ }
2733
2787
  async function findAccountByIdentifier2(queries, identifier) {
2734
2788
  let account = null;
2735
2789
  if (identifier.accountId !== void 0) {
@@ -2805,6 +2859,7 @@ async function getUserRoles(config, identifier) {
2805
2859
  addRoleForUserBy,
2806
2860
  addRoleToUser,
2807
2861
  authFunctions,
2862
+ authenticateRequest,
2808
2863
  changePasswordForUserBy,
2809
2864
  cleanupExpiredTokens,
2810
2865
  confirmResetPassword,
@@ -2812,6 +2867,7 @@ async function getUserRoles(config, identifier) {
2812
2867
  createAuthMiddleware,
2813
2868
  createAuthTables,
2814
2869
  createUser,
2870
+ defineRoles,
2815
2871
  deleteUserBy,
2816
2872
  dropAuthTables,
2817
2873
  forceLogoutForUserBy,