@vunexa/lixa 0.1.6-alpha.13 → 0.1.6-alpha.14

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.js CHANGED
@@ -387,10 +387,24 @@ var WeakPasswordError = class extends LixaError {
387
387
  }
388
388
  };
389
389
  var CredentialsNotConfiguredError = class extends LixaError {
390
- constructor(message = "Credentials authentication is not configured in this Lixa instance. Pass credentials: {} in LixaConfig to enable it.", details) {
390
+ constructor(message = "Credentials authentication is not configured in this Lixa instance. Pass credentials: {} or identity: ... in LixaConfig to enable it.", details) {
391
391
  super(message, "CREDENTIALS_NOT_CONFIGURED", details);
392
392
  }
393
393
  };
394
+ var IdentityProviderError = class extends LixaError {
395
+ provider;
396
+ statusCode;
397
+ constructor(message, provider, statusCode, details) {
398
+ super(message, "IDENTITY_PROVIDER_ERROR", { provider, statusCode, ...details });
399
+ this.provider = provider;
400
+ this.statusCode = statusCode;
401
+ }
402
+ };
403
+ var IdentityNotConfiguredError = class extends CredentialsNotConfiguredError {
404
+ constructor(message = "Identity provider is not configured in this Lixa instance. Pass identity: ... or credentials: {} in LixaConfig to enable it.", details) {
405
+ super(message, details);
406
+ }
407
+ };
394
408
 
395
409
  // src/credentials/credentials-manager.ts
396
410
  var CredentialsManager = class {
@@ -586,6 +600,94 @@ var CredentialsManager = class {
586
600
  }
587
601
  };
588
602
 
603
+ // src/identity/self-managed.ts
604
+ var SelfManagedIdentityProvider = class {
605
+ name = "self-managed";
606
+ manager;
607
+ constructor(configOrManager) {
608
+ if (configOrManager instanceof CredentialsManager) {
609
+ this.manager = configOrManager;
610
+ } else {
611
+ this.manager = new CredentialsManager(configOrManager || {});
612
+ }
613
+ }
614
+ mapUser(user) {
615
+ return {
616
+ id: user.id,
617
+ identifier: user.identifier,
618
+ username: user.username,
619
+ email: user.email,
620
+ emailVerified: true,
621
+ // Self-managed accounts verify locally unless configured otherwise
622
+ createdAt: user.createdAt,
623
+ updatedAt: user.updatedAt,
624
+ metadata: user.metadata,
625
+ provider: this.name
626
+ };
627
+ }
628
+ /**
629
+ * Registers a new user.
630
+ */
631
+ async signUp(params) {
632
+ const user = await this.manager.signUp(params);
633
+ return this.mapUser(user);
634
+ }
635
+ /**
636
+ * Authenticates a user with password.
637
+ */
638
+ async signIn(params) {
639
+ const user = await this.manager.verifyCredentials(params);
640
+ if (!user) {
641
+ throw new InvalidCredentialsError();
642
+ }
643
+ return {
644
+ user: this.mapUser(user)
645
+ };
646
+ }
647
+ /**
648
+ * Verifies credentials without throwing an error if invalid.
649
+ */
650
+ async verifyCredentials(params) {
651
+ const user = await this.manager.verifyCredentials(params);
652
+ return user ? this.mapUser(user) : null;
653
+ }
654
+ /**
655
+ * Changes a user's password.
656
+ */
657
+ async changePassword(params) {
658
+ return await this.manager.changePassword(params);
659
+ }
660
+ /**
661
+ * Retrieves a user by their unique ID.
662
+ */
663
+ async getUserById(id) {
664
+ const user = await this.manager.getUserById(id);
665
+ return user ? this.mapUser(user) : null;
666
+ }
667
+ /**
668
+ * Retrieves a user by their identifier (email or username).
669
+ */
670
+ async getUserByIdentifier(identifier) {
671
+ const user = await this.manager.getUserByIdentifier(identifier);
672
+ return user ? this.mapUser(user) : null;
673
+ }
674
+ /**
675
+ * Deletes a user from storage if supported by the storage adapter.
676
+ */
677
+ async deleteUser(id) {
678
+ const storage = this.manager.getStorage();
679
+ if (typeof storage.deleteUser === "function") {
680
+ await storage.deleteUser(id);
681
+ }
682
+ }
683
+ /**
684
+ * Returns the underlying CredentialsManager instance.
685
+ */
686
+ getCredentialsManager() {
687
+ return this.manager;
688
+ }
689
+ };
690
+
589
691
  // src/types.ts
590
692
  var AccountLinkingStrategy = /* @__PURE__ */ ((AccountLinkingStrategy2) => {
591
693
  AccountLinkingStrategy2["AUTO_LINK_BY_VERIFIED_EMAIL"] = "AUTO_LINK_BY_VERIFIED_EMAIL";
@@ -691,10 +793,7 @@ var LocalSessionHandler = class {
691
793
  if (!tokenData.access_token || typeof tokenData.access_token !== "string") {
692
794
  throw new Error("No valid access token found in OAuth response");
693
795
  }
694
- const session = {
695
- token: tokenData.access_token,
696
- raw: tokenData
697
- };
796
+ const session = {};
698
797
  return session;
699
798
  }
700
799
  // PascalCase alias for backward compatibility
@@ -772,6 +871,125 @@ async function extractUserInfo(tokenData, providerMetadata) {
772
871
  return { userInfo };
773
872
  }
774
873
 
874
+ // src/utils/cookies.ts
875
+ var DEFAULT_SESSION_COOKIE_NAME = "lixa_session";
876
+ var DEFAULT_STATE_COOKIE_NAME = "lixa_oauth_state";
877
+ var DEFAULT_SESSION_MAX_AGE_SECONDS = 24 * 60 * 60;
878
+ var DEFAULT_STATE_MAX_AGE_SECONDS = 5 * 60;
879
+ function isProductionEnvironment() {
880
+ return typeof process !== "undefined" && process.env?.NODE_ENV === "production";
881
+ }
882
+ function serializeCookie(name, value, options) {
883
+ const isProd = isProductionEnvironment();
884
+ const path = options?.path ?? "/";
885
+ const httpOnly = options?.httpOnly ?? true;
886
+ const secure = options?.secure ?? isProd;
887
+ const sameSite = options?.sameSite ?? "lax";
888
+ const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
889
+ if (path) {
890
+ parts.push(`Path=${path}`);
891
+ }
892
+ if (typeof options?.maxAge === "number") {
893
+ parts.push(`Max-Age=${Math.floor(options.maxAge)}`);
894
+ const expires = new Date(Date.now() + options.maxAge * 1e3).toUTCString();
895
+ parts.push(`Expires=${expires}`);
896
+ }
897
+ if (options?.domain) {
898
+ parts.push(`Domain=${options.domain}`);
899
+ }
900
+ if (httpOnly) {
901
+ parts.push("HttpOnly");
902
+ }
903
+ if (secure) {
904
+ parts.push("Secure");
905
+ }
906
+ if (sameSite) {
907
+ const capitalized = sameSite.charAt(0).toUpperCase() + sameSite.slice(1).toLowerCase();
908
+ parts.push(`SameSite=${capitalized}`);
909
+ }
910
+ return parts.join("; ");
911
+ }
912
+ function createSessionCookie(sessionId, options) {
913
+ const isProd = isProductionEnvironment();
914
+ const resolvedOptions = {
915
+ name: options?.name || DEFAULT_SESSION_COOKIE_NAME,
916
+ path: options?.path ?? "/",
917
+ maxAge: options?.maxAge ?? DEFAULT_SESSION_MAX_AGE_SECONDS,
918
+ httpOnly: options?.httpOnly ?? true,
919
+ secure: options?.secure ?? isProd,
920
+ sameSite: options?.sameSite ?? "lax",
921
+ domain: options?.domain
922
+ };
923
+ const name = resolvedOptions.name;
924
+ const header = serializeCookie(name, sessionId, resolvedOptions);
925
+ return {
926
+ name,
927
+ value: sessionId,
928
+ options: resolvedOptions,
929
+ header
930
+ };
931
+ }
932
+ function clearSessionCookie(options) {
933
+ const isProd = isProductionEnvironment();
934
+ const resolvedOptions = {
935
+ name: options?.name || DEFAULT_SESSION_COOKIE_NAME,
936
+ path: options?.path ?? "/",
937
+ maxAge: 0,
938
+ httpOnly: options?.httpOnly ?? true,
939
+ secure: options?.secure ?? isProd,
940
+ sameSite: options?.sameSite ?? "lax",
941
+ domain: options?.domain
942
+ };
943
+ const name = resolvedOptions.name;
944
+ const header = serializeCookie(name, "", resolvedOptions);
945
+ return {
946
+ name,
947
+ value: "",
948
+ options: resolvedOptions,
949
+ header
950
+ };
951
+ }
952
+ function createStateCookie(state, options) {
953
+ const isProd = isProductionEnvironment();
954
+ const resolvedOptions = {
955
+ name: options?.name || DEFAULT_STATE_COOKIE_NAME,
956
+ path: options?.path ?? "/",
957
+ maxAge: options?.maxAge ?? DEFAULT_STATE_MAX_AGE_SECONDS,
958
+ httpOnly: options?.httpOnly ?? true,
959
+ secure: options?.secure ?? isProd,
960
+ sameSite: options?.sameSite ?? "lax",
961
+ domain: options?.domain
962
+ };
963
+ const name = resolvedOptions.name;
964
+ const header = serializeCookie(name, state, resolvedOptions);
965
+ return {
966
+ name,
967
+ value: state,
968
+ options: resolvedOptions,
969
+ header
970
+ };
971
+ }
972
+ function clearStateCookie(options) {
973
+ const isProd = isProductionEnvironment();
974
+ const resolvedOptions = {
975
+ name: options?.name || DEFAULT_STATE_COOKIE_NAME,
976
+ path: options?.path ?? "/",
977
+ maxAge: 0,
978
+ httpOnly: options?.httpOnly ?? true,
979
+ secure: options?.secure ?? isProd,
980
+ sameSite: options?.sameSite ?? "lax",
981
+ domain: options?.domain
982
+ };
983
+ const name = resolvedOptions.name;
984
+ const header = serializeCookie(name, "", resolvedOptions);
985
+ return {
986
+ name,
987
+ value: "",
988
+ options: resolvedOptions,
989
+ header
990
+ };
991
+ }
992
+
775
993
  // src/lixa.ts
776
994
  var Lixa = class _Lixa {
777
995
  static DEFAULT_PROVIDERS = /* @__PURE__ */ new Map();
@@ -781,14 +999,19 @@ var Lixa = class _Lixa {
781
999
  localStateHandler;
782
1000
  localSessionHandler;
783
1001
  localResourceHandler;
1002
+ localAccountHandler;
784
1003
  userResourceStore = /* @__PURE__ */ new Map();
1004
+ userAccountStore = /* @__PURE__ */ new Map();
785
1005
  refreshMutexes = /* @__PURE__ */ new Map();
1006
+ identityProvider;
786
1007
  credentialsManager;
787
1008
  config;
788
1009
  stateHandler;
789
1010
  sessionHandler;
790
1011
  resourceHandler;
1012
+ accountHandler;
791
1013
  debug;
1014
+ sessionCookieResolver;
792
1015
  /**
793
1016
  * Creates a new Lixa instance with the provided configuration.
794
1017
  *
@@ -803,7 +1026,15 @@ var Lixa = class _Lixa {
803
1026
  * @throws Error when provider is not available and no inline implementation is provided
804
1027
  */
805
1028
  constructor(config) {
806
- this.config = config;
1029
+ this.sessionCookieResolver = config.sessionCookieName;
1030
+ const federatedOAuthProviders = config.federatedOAuthProviders || config.providers;
1031
+ this.config = {
1032
+ ...config,
1033
+ ...federatedOAuthProviders ? {
1034
+ providers: federatedOAuthProviders,
1035
+ federatedOAuthProviders
1036
+ } : {}
1037
+ };
807
1038
  this.debug = config.debug || false;
808
1039
  this.localStateHandler = new LocalStateHandler();
809
1040
  this.localSessionHandler = new LocalSessionHandler();
@@ -839,19 +1070,70 @@ var Lixa = class _Lixa {
839
1070
  }
840
1071
  }
841
1072
  };
842
- this.stateHandler = config.stateHandler || this.localStateHandler;
843
- this.sessionHandler = config.sessionHandler || this.localSessionHandler;
844
- this.resourceHandler = config.resourceHandler || this.localResourceHandler;
845
- if (config.credentials !== void 0 && config.credentials.enabled !== false) {
846
- this.credentialsManager = new CredentialsManager(config.credentials);
1073
+ this.localAccountHandler = {
1074
+ accountStorage: {
1075
+ saveAccount: async (userId, provider, account) => {
1076
+ let userMap = this.userAccountStore.get(userId);
1077
+ if (!userMap) {
1078
+ userMap = /* @__PURE__ */ new Map();
1079
+ this.userAccountStore.set(userId, userMap);
1080
+ }
1081
+ userMap.set(provider.toLowerCase(), account);
1082
+ },
1083
+ getAccount: async (userId, provider) => {
1084
+ const userMap = this.userAccountStore.get(userId);
1085
+ return userMap?.get(provider.toLowerCase()) || null;
1086
+ },
1087
+ getUserAccounts: async (userId) => {
1088
+ const userMap = this.userAccountStore.get(userId);
1089
+ const result = {};
1090
+ if (userMap) {
1091
+ for (const [p, a] of userMap.entries()) {
1092
+ result[p] = a;
1093
+ }
1094
+ }
1095
+ return result;
1096
+ },
1097
+ deleteAccount: async (userId, provider) => {
1098
+ const userMap = this.userAccountStore.get(userId);
1099
+ if (userMap) {
1100
+ userMap.delete(provider.toLowerCase());
1101
+ }
1102
+ }
1103
+ }
1104
+ };
1105
+ const storage = config.storage;
1106
+ this.stateHandler = config.stateHandler || (storage?.stateStorage ? { storage: storage.stateStorage } : this.localStateHandler);
1107
+ this.sessionHandler = config.sessionHandler || (storage?.sessionStorage ? { storage: storage.sessionStorage } : this.localSessionHandler);
1108
+ this.resourceHandler = config.resourceHandler || (storage?.resourceStorage ? { resourceStorage: storage.resourceStorage } : this.localResourceHandler);
1109
+ this.accountHandler = storage?.accountStorage ? { accountStorage: storage.accountStorage } : this.localAccountHandler;
1110
+ const identityConfig = config.identityProvider || config.identity;
1111
+ if (identityConfig) {
1112
+ if (typeof identityConfig.signUp === "function") {
1113
+ this.identityProvider = identityConfig;
1114
+ if (this.identityProvider instanceof SelfManagedIdentityProvider) {
1115
+ this.credentialsManager = this.identityProvider.getCredentialsManager();
1116
+ }
1117
+ } else {
1118
+ const selfManaged = new SelfManagedIdentityProvider(identityConfig);
1119
+ this.identityProvider = selfManaged;
1120
+ this.credentialsManager = selfManaged.getCredentialsManager();
1121
+ }
1122
+ } else if (config.credentials !== void 0 && config.credentials.enabled !== false) {
1123
+ const selfManaged = new SelfManagedIdentityProvider(config.credentials);
1124
+ this.identityProvider = selfManaged;
1125
+ this.credentialsManager = selfManaged.getCredentialsManager();
847
1126
  }
1127
+ const oauthProviders = this.config.providers;
848
1128
  this.log("INFO", "Init", "Initializing Lixa instance", {
849
- providers: config.providers ? Object.keys(config.providers) : [],
850
- credentialsEnabled: this.credentialsManager !== void 0,
1129
+ providers: oauthProviders ? Object.keys(oauthProviders) : [],
1130
+ identityProvider: this.identityProvider ? this.identityProvider.name : void 0,
1131
+ identityEnabled: this.identityProvider !== void 0,
1132
+ credentialsEnabled: this.identityProvider !== void 0,
851
1133
  debug: this.debug
852
1134
  });
853
- if (config.providers) {
854
- for (const [providerName, providerConfig] of Object.entries(config.providers)) {
1135
+ if (oauthProviders) {
1136
+ for (const [providerName, providerConfig] of Object.entries(oauthProviders)) {
855
1137
  const name = providerName.toLowerCase();
856
1138
  const typedConfig = providerConfig;
857
1139
  this.validateProviderConfig(providerName, typedConfig);
@@ -880,6 +1162,20 @@ var Lixa = class _Lixa {
880
1162
  * @throws InvalidProviderConfigError when required fields are missing or invalid
881
1163
  */
882
1164
  validateProviderConfig(name, config) {
1165
+ if (!config.scopes || config.scopes.length === 0) {
1166
+ const p = config.provider;
1167
+ if (p?.defaultScopes && Array.isArray(p.defaultScopes) && p.defaultScopes.length > 0) {
1168
+ config.scopes = [...p.defaultScopes];
1169
+ } else if (p?.authScopes && Array.isArray(p.authScopes) && p.authScopes.length > 0) {
1170
+ config.scopes = [...p.authScopes];
1171
+ } else if (name === "google" || name.startsWith("google")) {
1172
+ config.scopes = ["openid", "profile", "email"];
1173
+ } else if (name === "github" || name.startsWith("github")) {
1174
+ config.scopes = ["read:user", "user:email"];
1175
+ } else if (name.startsWith("cognito")) {
1176
+ config.scopes = ["openid", "email", "profile"];
1177
+ }
1178
+ }
883
1179
  const requiredFields = ["clientId", "clientSecret", "redirectUri", "scopes"];
884
1180
  const missingFields = requiredFields.filter((field) => {
885
1181
  const value = config[field];
@@ -1216,7 +1512,7 @@ var Lixa = class _Lixa {
1216
1512
  this.log("INFO", "State", `Saved state for provider: ${providerType}`, { state: stateValue });
1217
1513
  const authNScopes = this.resolveAuthNScopes(
1218
1514
  providerType,
1219
- providerConfig.scopes,
1515
+ providerConfig.scopes || [],
1220
1516
  providerImpl,
1221
1517
  providerConfig.allowNonAuthScopes
1222
1518
  );
@@ -1302,7 +1598,8 @@ var Lixa = class _Lixa {
1302
1598
  async handleCallback({
1303
1599
  provider,
1304
1600
  code,
1305
- state
1601
+ state,
1602
+ sessionId
1306
1603
  }) {
1307
1604
  const providerType = String(provider).toLowerCase();
1308
1605
  this.log("INFO", "Auth", `Handling OAuth callback for provider: ${providerType}`);
@@ -1371,19 +1668,40 @@ var Lixa = class _Lixa {
1371
1668
  }
1372
1669
  const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1373
1670
  const linkingConfig = this.config.accountLinking;
1374
- const mode = String(linkingConfig?.mode || "");
1375
- const isLinkByEmail = mode === "AUTO_LINK_BY_VERIFIED_EMAIL" /* AUTO_LINK_BY_VERIFIED_EMAIL */ || mode === "AUTO_LINK_BY_VERIFIED_EMAIL" || mode === "linkByEmail";
1671
+ const mode = typeof linkingConfig === "object" ? String(linkingConfig?.mode || "") : "";
1672
+ const isLinkByEmail = linkingConfig === true || linkingConfig === "linkByEmail" || linkingConfig === "AUTO_LINK_BY_VERIFIED_EMAIL" /* AUTO_LINK_BY_VERIFIED_EMAIL */ || mode === "AUTO_LINK_BY_VERIFIED_EMAIL" /* AUTO_LINK_BY_VERIFIED_EMAIL */ || mode === "AUTO_LINK_BY_VERIFIED_EMAIL" || mode === "linkByEmail";
1376
1673
  const email = extractedUserInfo?.email;
1377
1674
  const isVerified = extractedUserInfo?.email_verified !== false;
1378
- const requireVerified = linkingConfig?.requireVerifiedEmail ?? true;
1675
+ const requireVerified = typeof linkingConfig === "object" ? linkingConfig?.requireVerifiedEmail ?? true : true;
1379
1676
  const canLink = isLinkByEmail && email && (!requireVerified || isVerified);
1677
+ if (sessionId) {
1678
+ const activeSession = await sessionStorage.getSession(sessionId);
1679
+ if (activeSession) {
1680
+ this.log("INFO", "AccountLinking", `Linking provider '${providerType}' directly to active session '${sessionId}'`);
1681
+ const linkedAcc2 = {
1682
+ provider: providerType,
1683
+ providerUserId: extractedUserInfo?.sub || extractedUserInfo?.id,
1684
+ email: extractedUserInfo?.email || activeSession.email,
1685
+ accessToken: tokens.access_token,
1686
+ raw: tokens,
1687
+ linkedAt: Date.now()
1688
+ };
1689
+ activeSession.accounts = activeSession.accounts || {};
1690
+ activeSession.accounts[providerType] = linkedAcc2;
1691
+ await sessionStorage.saveSession(sessionId, activeSession, 86400);
1692
+ if (this.accountHandler?.accountStorage) {
1693
+ const userKey = this.getUserKeyFromSession(activeSession);
1694
+ await this.accountHandler.accountStorage.saveAccount(userKey, providerType, linkedAcc2);
1695
+ }
1696
+ return sessionId;
1697
+ }
1698
+ }
1380
1699
  if (canLink && sessionStorage.getSessionByEmail) {
1381
1700
  const existingRecord = await sessionStorage.getSessionByEmail(email);
1382
1701
  if (existingRecord) {
1383
1702
  this.log("INFO", "AccountLinking", `Linking provider '${providerType}' to existing session for email '${email}'`);
1384
1703
  const { sessionId: existingSessionId, session: existingSession } = existingRecord;
1385
- existingSession.accounts = existingSession.accounts || {};
1386
- existingSession.accounts[providerType] = {
1704
+ const linkedAcc2 = {
1387
1705
  provider: providerType,
1388
1706
  providerUserId: extractedUserInfo?.sub || extractedUserInfo?.id,
1389
1707
  email,
@@ -1391,15 +1709,18 @@ var Lixa = class _Lixa {
1391
1709
  raw: tokens,
1392
1710
  linkedAt: Date.now()
1393
1711
  };
1712
+ existingSession.accounts = existingSession.accounts || {};
1713
+ existingSession.accounts[providerType] = linkedAcc2;
1394
1714
  existingSession.provider = providerType;
1395
- existingSession.token = tokens.access_token;
1396
- existingSession.raw = tokens;
1397
1715
  await sessionStorage.saveSession(existingSessionId, existingSession, 86400);
1716
+ if (this.accountHandler?.accountStorage) {
1717
+ const userKey = this.getUserKeyFromSession(existingSession);
1718
+ await this.accountHandler.accountStorage.saveAccount(userKey, providerType, linkedAcc2);
1719
+ }
1398
1720
  return existingSessionId;
1399
1721
  }
1400
1722
  }
1401
- session.accounts = session.accounts || {};
1402
- session.accounts[providerType] = {
1723
+ const linkedAcc = {
1403
1724
  provider: providerType,
1404
1725
  providerUserId: extractedUserInfo?.sub || extractedUserInfo?.id,
1405
1726
  email: extractedUserInfo?.email,
@@ -1407,12 +1728,18 @@ var Lixa = class _Lixa {
1407
1728
  raw: tokens,
1408
1729
  linkedAt: Date.now()
1409
1730
  };
1410
- const sessionId = randomBytes2(32).toString("hex");
1411
- session.id = sessionId;
1412
- this.log("INFO", "Session", "Storing session", { sessionId });
1413
- await sessionStorage.saveSession(sessionId, session, 86400);
1414
- this.log("INFO", "Session", "Session created successfully", { sessionId });
1415
- return sessionId;
1731
+ session.accounts = session.accounts || {};
1732
+ session.accounts[providerType] = linkedAcc;
1733
+ if (this.accountHandler?.accountStorage) {
1734
+ const userKey = this.getUserKeyFromSession(session);
1735
+ await this.accountHandler.accountStorage.saveAccount(userKey, providerType, linkedAcc);
1736
+ }
1737
+ const newSessionId = randomBytes2(32).toString("hex");
1738
+ session.sessionId = newSessionId;
1739
+ this.log("INFO", "Session", "Storing session", { sessionId: newSessionId });
1740
+ await sessionStorage.saveSession(newSessionId, session, 86400);
1741
+ this.log("INFO", "Session", "Session created successfully", { sessionId: newSessionId });
1742
+ return newSessionId;
1416
1743
  }
1417
1744
  /**
1418
1745
  * Explicitly link a new OAuth provider account to an active session.
@@ -1498,6 +1825,10 @@ var Lixa = class _Lixa {
1498
1825
  }
1499
1826
  delete session.accounts[providerToUnlink.toLowerCase()];
1500
1827
  await sessionStorage.saveSession(sessionId, session, 86400);
1828
+ if (this.accountHandler?.accountStorage) {
1829
+ const userKey = this.getUserKeyFromSession(session);
1830
+ await this.accountHandler.accountStorage.deleteAccount(userKey, providerToUnlink.toLowerCase());
1831
+ }
1501
1832
  return true;
1502
1833
  }
1503
1834
  /**
@@ -1512,7 +1843,7 @@ var Lixa = class _Lixa {
1512
1843
  * @returns The authorization URL for resource consent
1513
1844
  */
1514
1845
  async getResourceAuthUrl(params) {
1515
- const { sessionId, provider, scopes, state, prompt, extraConfig } = params;
1846
+ const { sessionId, provider, state, prompt, extraConfig } = params;
1516
1847
  const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1517
1848
  const activeSession = await sessionStorage.getSession(sessionId);
1518
1849
  if (!activeSession) {
@@ -1523,6 +1854,10 @@ var Lixa = class _Lixa {
1523
1854
  if (!providerConfig) {
1524
1855
  throw new ProviderNotConfiguredError(String(provider));
1525
1856
  }
1857
+ const requestedScopes = params.scopes && params.scopes.length > 0 ? params.scopes : providerConfig.resourceScopes || [];
1858
+ if (requestedScopes.length === 0) {
1859
+ throw new Error(`No scopes specified for resource '${provider}'. Provide scopes in getResourceAuthUrl() or configure 'resourceScopes' on the provider.`);
1860
+ }
1526
1861
  const providerImpl = this.getProvider(providerType, providerConfig);
1527
1862
  const stateValue = state || randomBytes2(16).toString("hex");
1528
1863
  const codeVerifier = randomBytes2(32).toString("hex");
@@ -1540,7 +1875,7 @@ var Lixa = class _Lixa {
1540
1875
  const searchParams = new URLSearchParams({
1541
1876
  client_id: providerConfig.clientId,
1542
1877
  redirect_uri: providerConfig.redirectUri,
1543
- scope: scopes.join(" "),
1878
+ scope: requestedScopes.join(" "),
1544
1879
  state: stateValue,
1545
1880
  response_type: "code",
1546
1881
  code_challenge: codeChallenge,
@@ -1552,7 +1887,7 @@ var Lixa = class _Lixa {
1552
1887
  return `${providerImpl.authorizationEndpoint}?${searchParams.toString()}`;
1553
1888
  }
1554
1889
  getUserKeyFromSession(session) {
1555
- return session.userId || session.email || session.id || "anonymous";
1890
+ return session.userId || session.email || session.sessionId || "anonymous";
1556
1891
  }
1557
1892
  /**
1558
1893
  * Handles the OAuth callback for a connected resource provider and stores resource tokens bound to user account.
@@ -1582,12 +1917,13 @@ var Lixa = class _Lixa {
1582
1917
  }
1583
1918
  const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier);
1584
1919
  const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
1920
+ const effectiveScopes = scopes || (tokens.scope ? tokens.scope.split(" ") : providerConfig.resourceScopes || []);
1585
1921
  const connectedResource = {
1586
1922
  provider: providerType,
1587
1923
  accessToken: tokens.access_token,
1588
1924
  refreshToken: tokens.refresh_token,
1589
1925
  expiresAt,
1590
- scopes: scopes || (tokens.scope ? tokens.scope.split(" ") : []),
1926
+ scopes: effectiveScopes,
1591
1927
  raw: tokens,
1592
1928
  connectedAt: Date.now()
1593
1929
  };
@@ -1599,6 +1935,40 @@ var Lixa = class _Lixa {
1599
1935
  await sessionStorage.saveSession(sessionId, activeSession, 86400);
1600
1936
  return activeSession;
1601
1937
  }
1938
+ /**
1939
+ * Retrieves configured default resource scopes for a provider.
1940
+ *
1941
+ * @param provider - Provider name (e.g. 'github', 'google')
1942
+ * @returns Array of configured resource scopes, or undefined if not configured
1943
+ */
1944
+ getResourceScopes(provider) {
1945
+ const providerConfig = this.findProviderByType(String(provider).toLowerCase());
1946
+ return providerConfig?.resourceScopes;
1947
+ }
1948
+ /**
1949
+ * Returns the resolved session cookie name.
1950
+ * Dynamically reflects any changes to the active identity provider.
1951
+ *
1952
+ * @param req - Optional request object for multi-tenant request-scoped cookie resolution
1953
+ * @returns Resolved session cookie name
1954
+ */
1955
+ getSessionCookieName(req) {
1956
+ if (typeof this.sessionCookieResolver === "function") {
1957
+ return this.sessionCookieResolver(this, req);
1958
+ }
1959
+ if (typeof this.sessionCookieResolver === "string" && this.sessionCookieResolver.length > 0) {
1960
+ return this.sessionCookieResolver;
1961
+ }
1962
+ return DEFAULT_SESSION_COOKIE_NAME;
1963
+ }
1964
+ /**
1965
+ * Sets or updates the session cookie name or dynamic resolver on this instance.
1966
+ *
1967
+ * @param resolver - Static cookie name or dynamic resolver function
1968
+ */
1969
+ setSessionCookieName(resolver) {
1970
+ this.sessionCookieResolver = resolver;
1971
+ }
1602
1972
  /**
1603
1973
  * Retrieves a connected resource for a specific User ID / Email directly (independent of session IDs).
1604
1974
  * Automatically refreshes expired access tokens if a refresh token is present.
@@ -1779,7 +2149,16 @@ var Lixa = class _Lixa {
1779
2149
  */
1780
2150
  async fetchSessionInfo(sessionId) {
1781
2151
  const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1782
- return await sessionStorage.getSession(sessionId);
2152
+ const session = await sessionStorage.getSession(sessionId);
2153
+ if (!session) return null;
2154
+ if (this.accountHandler?.accountStorage) {
2155
+ const userKey = this.getUserKeyFromSession(session);
2156
+ const persistentAccounts = await this.accountHandler.accountStorage.getUserAccounts(userKey);
2157
+ if (persistentAccounts && Object.keys(persistentAccounts).length > 0) {
2158
+ session.accounts = { ...persistentAccounts, ...session.accounts || {} };
2159
+ }
2160
+ }
2161
+ return session;
1783
2162
  }
1784
2163
  /**
1785
2164
  * Deletes a session from session storage (e.g. on logout).
@@ -1830,36 +2209,64 @@ var Lixa = class _Lixa {
1830
2209
  return response.json();
1831
2210
  }
1832
2211
  /**
1833
- * Checks if credentials (username and password) authentication is configured and enabled.
2212
+ * Checks if an identity provider (Self-Managed Database, AWS Cognito, Auth0, Okta, etc.) is configured.
2213
+ *
2214
+ * @returns True if identity provider is configured and available
2215
+ */
2216
+ isIdentityEnabled() {
2217
+ return this.identityProvider !== void 0;
2218
+ }
2219
+ /**
2220
+ * Checks if credentials / identity authentication is configured and enabled.
1834
2221
  *
1835
- * @returns True if credentials authentication is available
2222
+ * @returns True if credentials / identity authentication is available
1836
2223
  */
1837
2224
  isCredentialsEnabled() {
1838
- return this.credentialsManager !== void 0;
2225
+ return this.identityProvider !== void 0;
2226
+ }
2227
+ /**
2228
+ * Returns the configured IdentityProvider instance.
2229
+ */
2230
+ getIdentityProvider() {
2231
+ return this.identityProvider;
2232
+ }
2233
+ /**
2234
+ * Dynamically sets or updates the active identity provider (e.g. SelfManaged, Cognito, Auth0, Okta).
2235
+ *
2236
+ * @param provider - Identity provider instance or undefined to disable
2237
+ */
2238
+ setIdentityProvider(provider) {
2239
+ this.identityProvider = provider;
2240
+ if (provider instanceof SelfManagedIdentityProvider) {
2241
+ this.credentialsManager = provider.getCredentialsManager();
2242
+ } else {
2243
+ this.credentialsManager = void 0;
2244
+ }
1839
2245
  }
1840
2246
  /**
1841
- * Returns the underlying CredentialsManager instance if configured.
2247
+ * Returns the underlying CredentialsManager instance if using Self-Managed identity.
1842
2248
  */
1843
2249
  getCredentialsManager() {
1844
2250
  return this.credentialsManager;
1845
2251
  }
1846
2252
  /**
1847
- * Registers a new user with username/email and password.
1848
- * Automatically enforces password policy, hashes password with Scrypt/configured hasher,
1849
- * stores credentials, and creates a session (unless autoCreateSessionOnSignUp is false).
2253
+ * Registers a new user with the configured identity provider (Self-Managed DB, Cognito, Auth0, Okta).
2254
+ * Automatically creates an active session unless autoCreateSessionOnSignUp is explicitly set to false.
1850
2255
  *
1851
2256
  * @param params - Registration parameters (identifier, password, email, username, metadata)
1852
- * @returns Created user (without password hash) and optional active session
2257
+ * @returns Created user identity and optional active session
1853
2258
  * @throws WeakPasswordError if password does not meet policy requirements
1854
2259
  * @throws UserAlreadyExistsError if identifier is already registered
1855
- * @throws CredentialsNotConfiguredError if credentials auth is not configured
2260
+ * @throws IdentityNotConfiguredError if identity authentication is not configured
1856
2261
  */
1857
2262
  async signUp(params) {
1858
- if (!this.credentialsManager) {
1859
- throw new CredentialsNotConfiguredError();
2263
+ if (!this.identityProvider) {
2264
+ throw new IdentityNotConfiguredError();
1860
2265
  }
1861
- this.log("INFO", "Credentials", "Processing signUp request", { identifier: params.identifier });
1862
- const user = await this.credentialsManager.signUp(params);
2266
+ const rawProviderName = this.identityProvider.name;
2267
+ const providerName = rawProviderName === "self-managed" ? "credentials" : rawProviderName;
2268
+ this.log("INFO", "Identity", `Processing signUp request via ${providerName}`, { identifier: params.identifier });
2269
+ const user = await this.identityProvider.signUp(params);
1863
2270
  const autoSession = this.config.credentials?.autoCreateSessionOnSignUp ?? true;
1864
2271
  if (!autoSession) {
1865
2272
  return { user };
@@ -1867,8 +2274,9 @@ var Lixa = class _Lixa {
1867
2274
  const sessionId = randomBytes2(16).toString("hex");
1868
2275
  const token = randomBytes2(32).toString("hex");
1869
2276
  const ttl = this.config.credentials?.sessionTtlSeconds ?? 86400;
2277
+ const accountKey = providerName;
1870
2278
  const credentialsAccount = {
1871
- provider: "credentials",
2279
+ provider: providerName,
1872
2280
  providerUserId: user.id,
1873
2281
  email: user.email,
1874
2282
  accessToken: token,
@@ -1880,67 +2288,71 @@ var Lixa = class _Lixa {
1880
2288
  linkedAt: Date.now()
1881
2289
  };
1882
2290
  const session = {
1883
- id: sessionId,
2291
+ sessionId,
1884
2292
  userId: user.id,
1885
2293
  email: user.email,
1886
- token,
1887
- provider: "credentials",
2294
+ provider: providerName,
1888
2295
  accounts: {
1889
- credentials: credentialsAccount
2296
+ [accountKey]: credentialsAccount
1890
2297
  }
1891
2298
  };
1892
2299
  const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1893
2300
  await sessionStorage.saveSession(sessionId, session, ttl);
1894
- this.log("INFO", "Credentials", "Created session on signUp", { userId: user.id, sessionId });
2301
+ this.log("INFO", "Identity", "Created session on signUp", { userId: user.id, sessionId, provider: providerName });
1895
2302
  return { user, sessionId, session };
1896
2303
  }
1897
2304
  /**
1898
- * Authenticates a user with username/email and password.
1899
- * Performs constant-time verification with timing attack mitigation, and creates an active session.
1900
- * If account linking is configured with AUTO_LINK_BY_VERIFIED_EMAIL, merges with existing session.
2305
+ * Authenticates a user with credentials via the configured identity provider (Self-Managed DB, Cognito, Auth0, Okta).
2306
+ * Creates an active session and automatically links with existing sessions when AUTO_LINK_BY_VERIFIED_EMAIL is configured.
1901
2307
  *
1902
2308
  * @param params - Sign-in parameters (identifier, password)
1903
2309
  * @returns Authenticated user, session ID, and session object
1904
2310
  * @throws InvalidCredentialsError if authentication fails
1905
- * @throws CredentialsNotConfiguredError if credentials auth is not configured
2311
+ * @throws IdentityNotConfiguredError if identity authentication is not configured
1906
2312
  */
1907
2313
  async signIn(params) {
1908
- if (!this.credentialsManager) {
1909
- throw new CredentialsNotConfiguredError();
2314
+ if (!this.identityProvider) {
2315
+ throw new IdentityNotConfiguredError();
1910
2316
  }
1911
2317
  const lookupKey = params.username || params.identifier;
1912
- this.log("INFO", "Credentials", "Processing signIn request", { identifier: lookupKey });
1913
- const user = await this.credentialsManager.verifyCredentials(params);
1914
- if (!user) {
1915
- this.log("WARN", "Credentials", "Invalid credentials provided", { identifier: lookupKey });
1916
- throw new InvalidCredentialsError();
1917
- }
2318
+ const rawProviderName = this.identityProvider.name;
2319
+ const providerName = rawProviderName === "self-managed" ? "credentials" : rawProviderName;
2320
+ this.log("INFO", "Identity", `Processing signIn request via ${providerName}`, { identifier: lookupKey });
2321
+ const authResult = await this.identityProvider.signIn(params);
2322
+ const user = authResult.user;
2323
+ const tokens = authResult.tokens;
1918
2324
  const ttl = this.config.credentials?.sessionTtlSeconds ?? 86400;
1919
- const token = randomBytes2(32).toString("hex");
2325
+ const token = tokens?.accessToken || randomBytes2(32).toString("hex");
2326
+ const rawTokens = {
2327
+ access_token: token,
2328
+ token_type: tokens?.tokenType || "Bearer",
2329
+ expires_in: tokens?.expiresIn || ttl,
2330
+ id_token: tokens?.idToken,
2331
+ refresh_token: tokens?.refreshToken,
2332
+ ...tokens?.raw
2333
+ };
2334
+ const accountKey = providerName === "self-managed" ? "credentials" : providerName;
1920
2335
  const credentialsAccount = {
1921
- provider: "credentials",
2336
+ provider: providerName,
1922
2337
  providerUserId: user.id,
1923
2338
  email: user.email,
1924
2339
  accessToken: token,
1925
- raw: {
1926
- access_token: token,
1927
- token_type: "Bearer",
1928
- expires_in: ttl
1929
- },
2340
+ raw: rawTokens,
1930
2341
  linkedAt: Date.now()
1931
2342
  };
1932
2343
  const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1933
- const linkingMode = this.config.accountLinking?.mode;
1934
- const isAutoLink = linkingMode === "AUTO_LINK_BY_VERIFIED_EMAIL" /* AUTO_LINK_BY_VERIFIED_EMAIL */ || linkingMode === "AUTO_LINK_BY_VERIFIED_EMAIL" || linkingMode === "linkByEmail";
2344
+ const linkingConfig = this.config.accountLinking;
2345
+ const linkingMode = typeof linkingConfig === "object" ? linkingConfig?.mode : void 0;
2346
+ const isAutoLink = linkingConfig === true || linkingConfig === "linkByEmail" || linkingConfig === "AUTO_LINK_BY_VERIFIED_EMAIL" /* AUTO_LINK_BY_VERIFIED_EMAIL */ || linkingMode === "AUTO_LINK_BY_VERIFIED_EMAIL" /* AUTO_LINK_BY_VERIFIED_EMAIL */ || linkingMode === "AUTO_LINK_BY_VERIFIED_EMAIL" || linkingMode === "linkByEmail";
1935
2347
  if (isAutoLink && user.email && sessionStorage.getSessionByEmail) {
1936
2348
  const existing = await sessionStorage.getSessionByEmail(user.email);
1937
2349
  if (existing) {
1938
2350
  const mergedSession = { ...existing.session };
1939
- mergedSession.accounts = { ...mergedSession.accounts || {}, credentials: credentialsAccount };
2351
+ mergedSession.accounts = { ...mergedSession.accounts || {}, [accountKey]: credentialsAccount };
1940
2352
  if (!mergedSession.userId) mergedSession.userId = user.id;
1941
2353
  if (!mergedSession.email) mergedSession.email = user.email;
1942
2354
  await sessionStorage.saveSession(existing.sessionId, mergedSession, ttl);
1943
- this.log("INFO", "AccountLinking", "Linked credentials to existing session by email", {
2355
+ this.log("INFO", "AccountLinking", `Linked ${providerName} to existing session by email`, {
1944
2356
  sessionId: existing.sessionId,
1945
2357
  email: user.email
1946
2358
  });
@@ -1953,31 +2365,46 @@ var Lixa = class _Lixa {
1953
2365
  }
1954
2366
  const sessionId = randomBytes2(16).toString("hex");
1955
2367
  const session = {
1956
- id: sessionId,
2368
+ sessionId,
1957
2369
  userId: user.id,
1958
2370
  email: user.email,
1959
- token,
1960
- provider: "credentials",
2371
+ provider: providerName,
1961
2372
  accounts: {
1962
- credentials: credentialsAccount
2373
+ [accountKey]: credentialsAccount
1963
2374
  }
1964
2375
  };
2376
+ const userKey = this.getUserKeyFromSession(session);
2377
+ if (this.accountHandler?.accountStorage) {
2378
+ const persistentAccounts = await this.accountHandler.accountStorage.getUserAccounts(userKey);
2379
+ if (persistentAccounts && Object.keys(persistentAccounts).length > 0) {
2380
+ session.accounts = { ...persistentAccounts, ...session.accounts };
2381
+ }
2382
+ await this.accountHandler.accountStorage.saveAccount(userKey, accountKey, credentialsAccount);
2383
+ }
1965
2384
  await sessionStorage.saveSession(sessionId, session, ttl);
1966
- this.log("INFO", "Credentials", "User signed in successfully", { userId: user.id, sessionId });
2385
+ this.log("INFO", "Identity", `User signed in successfully via ${providerName}`, { userId: user.id, sessionId });
1967
2386
  return { user, sessionId, session };
1968
2387
  }
1969
2388
  /**
1970
2389
  * Verifies username/email and password credentials without generating a session.
1971
2390
  *
1972
2391
  * @param params - Verification parameters (identifier, password)
1973
- * @returns User credentials (without password hash) or null if invalid
1974
- * @throws CredentialsNotConfiguredError if credentials auth is not configured
2392
+ * @returns User credentials/identity or null if invalid
2393
+ * @throws IdentityNotConfiguredError if identity auth is not configured
1975
2394
  */
1976
2395
  async verifyCredentials(params) {
1977
- if (!this.credentialsManager) {
1978
- throw new CredentialsNotConfiguredError();
2396
+ if (!this.identityProvider) {
2397
+ throw new IdentityNotConfiguredError();
2398
+ }
2399
+ if (typeof this.identityProvider.verifyCredentials === "function") {
2400
+ return this.identityProvider.verifyCredentials(params);
2401
+ }
2402
+ try {
2403
+ const result = await this.identityProvider.signIn(params);
2404
+ return result.user;
2405
+ } catch {
2406
+ return null;
1979
2407
  }
1980
- return this.credentialsManager.verifyCredentials(params);
1981
2408
  }
1982
2409
  /**
1983
2410
  * Updates a user's password after verifying the current password and enforcing policy on the new password.
@@ -1987,17 +2414,140 @@ var Lixa = class _Lixa {
1987
2414
  * @throws UserNotFoundError if user is not found
1988
2415
  * @throws InvalidCredentialsError if current password is incorrect
1989
2416
  * @throws WeakPasswordError if new password does not meet policy requirements
1990
- * @throws CredentialsNotConfiguredError if credentials auth is not configured
2417
+ * @throws IdentityNotConfiguredError if identity auth is not configured
1991
2418
  */
1992
2419
  async changePassword(params) {
1993
- if (!this.credentialsManager) {
1994
- throw new CredentialsNotConfiguredError();
2420
+ if (!this.identityProvider) {
2421
+ throw new IdentityNotConfiguredError();
1995
2422
  }
1996
- this.log("INFO", "Credentials", "Processing changePassword request", {
2423
+ if (typeof this.identityProvider.changePassword !== "function") {
2424
+ throw new IdentityProviderError(
2425
+ `Password change is not supported by the '${this.identityProvider.name}' identity provider`,
2426
+ this.identityProvider.name
2427
+ );
2428
+ }
2429
+ this.log("INFO", "Identity", "Processing changePassword request", {
1997
2430
  userId: params.userId,
1998
2431
  identifier: params.identifier
1999
2432
  });
2000
- return this.credentialsManager.changePassword(params);
2433
+ return this.identityProvider.changePassword(params);
2434
+ }
2435
+ /**
2436
+ * Initiates a password reset (forgot password flow) through the configured identity provider.
2437
+ *
2438
+ * @param params - Forgot password parameters (identifier, optional metadata)
2439
+ * @returns ForgotPasswordResult indicating status and delivery medium (email/SMS)
2440
+ * @throws IdentityNotConfiguredError if identity auth is not configured
2441
+ */
2442
+ async forgotPassword(params) {
2443
+ if (!this.identityProvider) {
2444
+ throw new IdentityNotConfiguredError();
2445
+ }
2446
+ if (typeof this.identityProvider.forgotPassword !== "function") {
2447
+ throw new IdentityProviderError(
2448
+ `Password reset request is not supported by the '${this.identityProvider.name}' identity provider`,
2449
+ this.identityProvider.name
2450
+ );
2451
+ }
2452
+ this.log("INFO", "Identity", "Processing forgotPassword request", { identifier: params.identifier });
2453
+ return this.identityProvider.forgotPassword(params);
2454
+ }
2455
+ /**
2456
+ * Confirms a password reset using a verification code / token and sets the new password.
2457
+ *
2458
+ * @param params - Verification code, identifier, and new password
2459
+ * @returns True if password reset was successfully confirmed
2460
+ * @throws IdentityNotConfiguredError if identity auth is not configured
2461
+ */
2462
+ async confirmPasswordReset(params) {
2463
+ if (!this.identityProvider) {
2464
+ throw new IdentityNotConfiguredError();
2465
+ }
2466
+ if (typeof this.identityProvider.confirmPasswordReset !== "function") {
2467
+ throw new IdentityProviderError(
2468
+ `Confirm password reset is not supported by the '${this.identityProvider.name}' identity provider`,
2469
+ this.identityProvider.name
2470
+ );
2471
+ }
2472
+ this.log("INFO", "Identity", "Processing confirmPasswordReset request", { identifier: params.identifier });
2473
+ return this.identityProvider.confirmPasswordReset(params);
2474
+ }
2475
+ /**
2476
+ * Confirms user account registration using a verification code.
2477
+ *
2478
+ * @param params - Verification parameters including identifier and confirmationCode
2479
+ * @returns True if confirmation was successful
2480
+ * @throws IdentityNotConfiguredError if identity auth is not configured
2481
+ * @throws IdentityProviderError if identity provider does not support confirmation
2482
+ */
2483
+ async confirmSignUp(params) {
2484
+ if (!this.identityProvider) {
2485
+ throw new IdentityNotConfiguredError();
2486
+ }
2487
+ if (typeof this.identityProvider.confirmSignUp !== "function") {
2488
+ throw new IdentityProviderError(
2489
+ `Account confirmation is not supported by the '${this.identityProvider.name}' identity provider`,
2490
+ this.identityProvider.name
2491
+ );
2492
+ }
2493
+ this.log("INFO", "Identity", "Processing confirmSignUp request", { identifier: params.identifier });
2494
+ return this.identityProvider.confirmSignUp(params);
2495
+ }
2496
+ /**
2497
+ * Resends the sign up confirmation code to the user.
2498
+ *
2499
+ * @param params - Resend parameters including user identifier
2500
+ * @returns Details about the code delivery (medium, destination)
2501
+ * @throws IdentityNotConfiguredError if identity auth is not configured
2502
+ * @throws IdentityProviderError if identity provider does not support resending codes
2503
+ */
2504
+ async resendConfirmationCode(params) {
2505
+ if (!this.identityProvider) {
2506
+ throw new IdentityNotConfiguredError();
2507
+ }
2508
+ if (typeof this.identityProvider.resendConfirmationCode !== "function") {
2509
+ throw new IdentityProviderError(
2510
+ `Resending confirmation code is not supported by the '${this.identityProvider.name}' identity provider`,
2511
+ this.identityProvider.name
2512
+ );
2513
+ }
2514
+ this.log("INFO", "Identity", "Processing resendConfirmationCode request", { identifier: params.identifier });
2515
+ return this.identityProvider.resendConfirmationCode(params);
2516
+ }
2517
+ /**
2518
+ * Retrieves a user by their unique ID from the configured identity provider.
2519
+ *
2520
+ * @param id - Unique user ID
2521
+ * @returns UserIdentity or null if not found
2522
+ */
2523
+ async getUserById(id) {
2524
+ if (!this.identityProvider || typeof this.identityProvider.getUserById !== "function") {
2525
+ return null;
2526
+ }
2527
+ return this.identityProvider.getUserById(id);
2528
+ }
2529
+ /**
2530
+ * Retrieves a user by their identifier (username or email) from the configured identity provider.
2531
+ *
2532
+ * @param identifier - Username or email string
2533
+ * @returns UserIdentity or null if not found
2534
+ */
2535
+ async getUserByIdentifier(identifier) {
2536
+ if (!this.identityProvider || typeof this.identityProvider.getUserByIdentifier !== "function") {
2537
+ return null;
2538
+ }
2539
+ return this.identityProvider.getUserByIdentifier(identifier);
2540
+ }
2541
+ /**
2542
+ * Deletes a user account from the configured identity provider.
2543
+ *
2544
+ * @param id - Unique user ID
2545
+ */
2546
+ async deleteUser(id) {
2547
+ if (!this.identityProvider || typeof this.identityProvider.deleteUser !== "function") {
2548
+ return;
2549
+ }
2550
+ await this.identityProvider.deleteUser(id);
2001
2551
  }
2002
2552
  findProviderByType(providerType) {
2003
2553
  for (const [key, value] of Object.entries(this.config.providers || {})) {
@@ -2009,125 +2559,6 @@ var Lixa = class _Lixa {
2009
2559
  }
2010
2560
  };
2011
2561
 
2012
- // src/utils/cookies.ts
2013
- var DEFAULT_SESSION_COOKIE_NAME = "lixa_session";
2014
- var DEFAULT_STATE_COOKIE_NAME = "lixa_oauth_state";
2015
- var DEFAULT_SESSION_MAX_AGE_SECONDS = 24 * 60 * 60;
2016
- var DEFAULT_STATE_MAX_AGE_SECONDS = 5 * 60;
2017
- function isProductionEnvironment() {
2018
- return typeof process !== "undefined" && process.env?.NODE_ENV === "production";
2019
- }
2020
- function serializeCookie(name, value, options) {
2021
- const isProd = isProductionEnvironment();
2022
- const path = options?.path ?? "/";
2023
- const httpOnly = options?.httpOnly ?? true;
2024
- const secure = options?.secure ?? isProd;
2025
- const sameSite = options?.sameSite ?? "lax";
2026
- const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
2027
- if (path) {
2028
- parts.push(`Path=${path}`);
2029
- }
2030
- if (typeof options?.maxAge === "number") {
2031
- parts.push(`Max-Age=${Math.floor(options.maxAge)}`);
2032
- const expires = new Date(Date.now() + options.maxAge * 1e3).toUTCString();
2033
- parts.push(`Expires=${expires}`);
2034
- }
2035
- if (options?.domain) {
2036
- parts.push(`Domain=${options.domain}`);
2037
- }
2038
- if (httpOnly) {
2039
- parts.push("HttpOnly");
2040
- }
2041
- if (secure) {
2042
- parts.push("Secure");
2043
- }
2044
- if (sameSite) {
2045
- const capitalized = sameSite.charAt(0).toUpperCase() + sameSite.slice(1).toLowerCase();
2046
- parts.push(`SameSite=${capitalized}`);
2047
- }
2048
- return parts.join("; ");
2049
- }
2050
- function createSessionCookie(sessionId, options) {
2051
- const isProd = isProductionEnvironment();
2052
- const resolvedOptions = {
2053
- name: options?.name || DEFAULT_SESSION_COOKIE_NAME,
2054
- path: options?.path ?? "/",
2055
- maxAge: options?.maxAge ?? DEFAULT_SESSION_MAX_AGE_SECONDS,
2056
- httpOnly: options?.httpOnly ?? true,
2057
- secure: options?.secure ?? isProd,
2058
- sameSite: options?.sameSite ?? "lax",
2059
- domain: options?.domain
2060
- };
2061
- const name = resolvedOptions.name;
2062
- const header = serializeCookie(name, sessionId, resolvedOptions);
2063
- return {
2064
- name,
2065
- value: sessionId,
2066
- options: resolvedOptions,
2067
- header
2068
- };
2069
- }
2070
- function clearSessionCookie(options) {
2071
- const isProd = isProductionEnvironment();
2072
- const resolvedOptions = {
2073
- name: options?.name || DEFAULT_SESSION_COOKIE_NAME,
2074
- path: options?.path ?? "/",
2075
- maxAge: 0,
2076
- httpOnly: options?.httpOnly ?? true,
2077
- secure: options?.secure ?? isProd,
2078
- sameSite: options?.sameSite ?? "lax",
2079
- domain: options?.domain
2080
- };
2081
- const name = resolvedOptions.name;
2082
- const header = serializeCookie(name, "", resolvedOptions);
2083
- return {
2084
- name,
2085
- value: "",
2086
- options: resolvedOptions,
2087
- header
2088
- };
2089
- }
2090
- function createStateCookie(state, options) {
2091
- const isProd = isProductionEnvironment();
2092
- const resolvedOptions = {
2093
- name: options?.name || DEFAULT_STATE_COOKIE_NAME,
2094
- path: options?.path ?? "/",
2095
- maxAge: options?.maxAge ?? DEFAULT_STATE_MAX_AGE_SECONDS,
2096
- httpOnly: options?.httpOnly ?? true,
2097
- secure: options?.secure ?? isProd,
2098
- sameSite: options?.sameSite ?? "lax",
2099
- domain: options?.domain
2100
- };
2101
- const name = resolvedOptions.name;
2102
- const header = serializeCookie(name, state, resolvedOptions);
2103
- return {
2104
- name,
2105
- value: state,
2106
- options: resolvedOptions,
2107
- header
2108
- };
2109
- }
2110
- function clearStateCookie(options) {
2111
- const isProd = isProductionEnvironment();
2112
- const resolvedOptions = {
2113
- name: options?.name || DEFAULT_STATE_COOKIE_NAME,
2114
- path: options?.path ?? "/",
2115
- maxAge: 0,
2116
- httpOnly: options?.httpOnly ?? true,
2117
- secure: options?.secure ?? isProd,
2118
- sameSite: options?.sameSite ?? "lax",
2119
- domain: options?.domain
2120
- };
2121
- const name = resolvedOptions.name;
2122
- const header = serializeCookie(name, "", resolvedOptions);
2123
- return {
2124
- name,
2125
- value: "",
2126
- options: resolvedOptions,
2127
- header
2128
- };
2129
- }
2130
-
2131
2562
  // src/credentials/index.ts
2132
2563
  var credentials_exports = {};
2133
2564
  __export(credentials_exports, {
@@ -2137,6 +2568,12 @@ __export(credentials_exports, {
2137
2568
  ScryptPasswordHasher: () => ScryptPasswordHasher,
2138
2569
  validatePasswordPolicy: () => validatePasswordPolicy
2139
2570
  });
2571
+
2572
+ // src/identity/index.ts
2573
+ var identity_exports = {};
2574
+ __export(identity_exports, {
2575
+ SelfManagedIdentityProvider: () => SelfManagedIdentityProvider
2576
+ });
2140
2577
  export {
2141
2578
  AccountLinkingStrategy,
2142
2579
  AccountUnlinkError,
@@ -2147,6 +2584,8 @@ export {
2147
2584
  DEFAULT_STATE_COOKIE_NAME,
2148
2585
  DEFAULT_STATE_MAX_AGE_SECONDS,
2149
2586
  EmailNotVerifiedError,
2587
+ IdentityNotConfiguredError,
2588
+ IdentityProviderError,
2150
2589
  InvalidCredentialsError,
2151
2590
  InvalidOAuthCallbackError,
2152
2591
  InvalidProviderConfigError,
@@ -2158,6 +2597,7 @@ export {
2158
2597
  ProviderNotConfiguredError,
2159
2598
  RefreshTokenError,
2160
2599
  ScryptPasswordHasher,
2600
+ SelfManagedIdentityProvider,
2161
2601
  SessionNotFoundError,
2162
2602
  TokenExchangeError,
2163
2603
  UserAlreadyExistsError,
@@ -2172,6 +2612,7 @@ export {
2172
2612
  determineProviderFromIssuer,
2173
2613
  extractUserInfo,
2174
2614
  fetchUserInfo,
2615
+ identity_exports as identity,
2175
2616
  isProductionEnvironment,
2176
2617
  serializeCookie,
2177
2618
  validatePasswordPolicy