@vunexa/lixa 0.1.4 → 0.1.6-alpha.10

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
@@ -28,7 +28,7 @@ var LocalStateHandler = class {
28
28
  }
29
29
  };
30
30
  }
31
- // Default GenerateState implementation
31
+ // Default generateState implementation
32
32
  async generateState(provider) {
33
33
  const state = randomBytes(16).toString("hex");
34
34
  const codeVerifier = randomBytes(32).toString("hex");
@@ -41,6 +41,10 @@ var LocalStateHandler = class {
41
41
  }
42
42
  };
43
43
  }
44
+ // PascalCase alias for backward compatibility
45
+ async GenerateState(provider) {
46
+ return this.generateState(provider);
47
+ }
44
48
  };
45
49
 
46
50
  // src/lixa.ts
@@ -53,7 +57,20 @@ var LocalSessionHandler = class {
53
57
  emailToSessionMap = /* @__PURE__ */ new Map();
54
58
  sessionStorage;
55
59
  constructor(defaultTtlSeconds = 600) {
56
- this.cache = new NodeCache2({ stdTTL: defaultTtlSeconds });
60
+ this.cache = new NodeCache2({ stdTTL: defaultTtlSeconds, checkperiod: 60 });
61
+ this.cache.on("expired", (_key, value) => {
62
+ if (value && typeof value === "object" && value.email) {
63
+ this.emailToSessionMap.delete(String(value.email).toLowerCase());
64
+ }
65
+ });
66
+ this.cache.on("del", (_key, value) => {
67
+ if (value && typeof value === "object" && value.email) {
68
+ this.emailToSessionMap.delete(String(value.email).toLowerCase());
69
+ }
70
+ });
71
+ this.cache.on("flush", () => {
72
+ this.emailToSessionMap.clear();
73
+ });
57
74
  this.sessionStorage = {
58
75
  saveSession: async (sessionId, session, expiresInSeconds) => {
59
76
  this.cache.set(sessionId, session, expiresInSeconds);
@@ -84,8 +101,8 @@ var LocalSessionHandler = class {
84
101
  }
85
102
  };
86
103
  }
87
- // Default GenerateSession implementation
88
- async GenerateSession(tokenData, providerMetadata) {
104
+ // Default generateSession implementation
105
+ async generateSession(tokenData, _providerMetadata) {
89
106
  if (!tokenData.access_token || typeof tokenData.access_token !== "string") {
90
107
  throw new Error("No valid access token found in OAuth response");
91
108
  }
@@ -95,6 +112,10 @@ var LocalSessionHandler = class {
95
112
  };
96
113
  return session;
97
114
  }
115
+ // PascalCase alias for backward compatibility
116
+ async GenerateSession(tokenData, providerMetadata) {
117
+ return this.generateSession(tokenData, providerMetadata);
118
+ }
98
119
  };
99
120
 
100
121
  // src/utils/user-info.ts
@@ -166,16 +187,95 @@ async function extractUserInfo(tokenData, providerMetadata) {
166
187
  return { userInfo };
167
188
  }
168
189
 
190
+ // src/errors.ts
191
+ var LixaError = class extends Error {
192
+ /**
193
+ * Standard error code string.
194
+ */
195
+ code;
196
+ /**
197
+ * Additional error context data.
198
+ */
199
+ details;
200
+ constructor(message, code = "LIXA_ERROR", details) {
201
+ super(message);
202
+ this.name = this.constructor.name;
203
+ this.code = code;
204
+ this.details = details;
205
+ Object.setPrototypeOf(this, new.target.prototype);
206
+ }
207
+ };
208
+ var InvalidStateError = class extends LixaError {
209
+ constructor(message = "Invalid or expired state", details) {
210
+ super(message, "INVALID_STATE", details);
211
+ }
212
+ };
213
+ var ProviderNotConfiguredError = class extends LixaError {
214
+ constructor(provider, details) {
215
+ const message = details?.message && typeof details.message === "string" ? details.message : `Provider '${provider}' is not available. Either import it from '@vunexa/lixa-providers' and include it in the configuration, or provide a custom implementation using the 'provider' field: { provider: new CustomProvider(), clientId: '...', ... }`;
216
+ super(message, "PROVIDER_NOT_CONFIGURED", {
217
+ provider,
218
+ ...details
219
+ });
220
+ }
221
+ };
222
+ var InvalidProviderConfigError = class extends LixaError {
223
+ constructor(message, details) {
224
+ super(message, "INVALID_PROVIDER_CONFIG", details);
225
+ }
226
+ };
227
+ var InvalidOAuthCallbackError = class extends LixaError {
228
+ constructor(message, details) {
229
+ super(message, "INVALID_OAUTH_CALLBACK", details);
230
+ }
231
+ };
232
+ var TokenExchangeError = class extends LixaError {
233
+ status;
234
+ constructor(message, status, details) {
235
+ super(message, "TOKEN_EXCHANGE_FAILED", { status, ...details });
236
+ this.status = status;
237
+ }
238
+ };
239
+ var SessionNotFoundError = class extends LixaError {
240
+ constructor(message = "Active session not found or has expired", details) {
241
+ super(message, "SESSION_NOT_FOUND", details);
242
+ }
243
+ };
244
+ var EmailNotVerifiedError = class extends LixaError {
245
+ constructor(email, details) {
246
+ super(
247
+ `Cannot link account: email '${email || "unknown"}' is not verified by the identity provider`,
248
+ "EMAIL_NOT_VERIFIED",
249
+ { email, ...details }
250
+ );
251
+ }
252
+ };
253
+ var AccountUnlinkError = class extends LixaError {
254
+ constructor(message, details) {
255
+ super(message, "ACCOUNT_UNLINK_ERROR", details);
256
+ }
257
+ };
258
+ var RefreshTokenError = class extends LixaError {
259
+ constructor(message, details) {
260
+ super(message, "REFRESH_TOKEN_ERROR", details);
261
+ }
262
+ };
263
+
169
264
  // src/lixa.ts
170
265
  var Lixa = class _Lixa {
171
266
  static DEFAULT_PROVIDERS = /* @__PURE__ */ new Map();
172
267
  static CONFIGURED_PROVIDERS = /* @__PURE__ */ new Map();
173
268
  // Legacy registry for backward compatibility
174
- static LOCAL_STATE_HANDLER = new LocalStateHandler();
175
- static LOCAL_SESSION_HANDLER = new LocalSessionHandler();
269
+ // Instance-scoped fallback handlers to ensure no cross-instance state pollution
270
+ localStateHandler;
271
+ localSessionHandler;
272
+ localResourceHandler;
273
+ userResourceStore = /* @__PURE__ */ new Map();
274
+ refreshMutexes = /* @__PURE__ */ new Map();
176
275
  config;
177
276
  stateHandler;
178
277
  sessionHandler;
278
+ resourceHandler;
179
279
  debug;
180
280
  /**
181
281
  * Creates a new Lixa instance with the provided configuration.
@@ -192,9 +292,44 @@ var Lixa = class _Lixa {
192
292
  */
193
293
  constructor(config) {
194
294
  this.config = config;
195
- this.stateHandler = config.stateHandler || _Lixa.LOCAL_STATE_HANDLER;
196
- this.sessionHandler = config.sessionHandler || _Lixa.LOCAL_SESSION_HANDLER;
197
295
  this.debug = config.debug || false;
296
+ this.localStateHandler = new LocalStateHandler();
297
+ this.localSessionHandler = new LocalSessionHandler();
298
+ this.localResourceHandler = {
299
+ resourceStorage: {
300
+ saveResource: async (userId, provider, resource) => {
301
+ let userMap = this.userResourceStore.get(userId);
302
+ if (!userMap) {
303
+ userMap = /* @__PURE__ */ new Map();
304
+ this.userResourceStore.set(userId, userMap);
305
+ }
306
+ userMap.set(provider.toLowerCase(), resource);
307
+ },
308
+ getResource: async (userId, provider) => {
309
+ const userMap = this.userResourceStore.get(userId);
310
+ return userMap?.get(provider.toLowerCase()) || null;
311
+ },
312
+ getUserResources: async (userId) => {
313
+ const userMap = this.userResourceStore.get(userId);
314
+ const result = {};
315
+ if (userMap) {
316
+ for (const [p, r] of userMap.entries()) {
317
+ result[p] = r;
318
+ }
319
+ }
320
+ return result;
321
+ },
322
+ deleteResource: async (userId, provider) => {
323
+ const userMap = this.userResourceStore.get(userId);
324
+ if (userMap) {
325
+ userMap.delete(provider.toLowerCase());
326
+ }
327
+ }
328
+ }
329
+ };
330
+ this.stateHandler = config.stateHandler || this.localStateHandler;
331
+ this.sessionHandler = config.sessionHandler || this.localSessionHandler;
332
+ this.resourceHandler = config.resourceHandler || this.localResourceHandler;
198
333
  this.log("INFO", "Init", "Initializing Lixa instance", {
199
334
  providers: Object.keys(config.providers),
200
335
  debug: this.debug
@@ -209,8 +344,9 @@ var Lixa = class _Lixa {
209
344
  } else {
210
345
  if (!_Lixa.DEFAULT_PROVIDERS.has(name) && !_Lixa.CONFIGURED_PROVIDERS.has(name)) {
211
346
  this.log("ERROR", "Init", `Provider '${providerName}' not available`);
212
- throw new Error(
213
- `Provider '${providerName}' is not available. Either import it from '@vunexa/lixa-providers' and include it in the configuration, or provide a custom implementation using the 'provider' field: { provider: new CustomProvider(), clientId: '...', ... }`
347
+ throw new ProviderNotConfiguredError(
348
+ providerName,
349
+ { hint: `Ensure the provider is registered via '@vunexa/lixa-providers' or provided inline.` }
214
350
  );
215
351
  }
216
352
  this.log("INFO", "Init", `Using registered provider: ${providerName}`);
@@ -223,7 +359,7 @@ var Lixa = class _Lixa {
223
359
  *
224
360
  * @param name - The provider name
225
361
  * @param config - The provider configuration
226
- * @throws Error when required fields are missing or invalid
362
+ * @throws InvalidProviderConfigError when required fields are missing or invalid
227
363
  */
228
364
  validateProviderConfig(name, config) {
229
365
  const requiredFields = ["clientId", "clientSecret", "redirectUri", "scopes"];
@@ -232,18 +368,21 @@ var Lixa = class _Lixa {
232
368
  return value === void 0 || value === null || typeof value === "string" && value.trim() === "";
233
369
  });
234
370
  if (missingFields.length > 0) {
235
- throw new Error(
236
- `Provider '${name}' configuration is missing required fields: ${missingFields.join(", ")}`
371
+ throw new InvalidProviderConfigError(
372
+ `Provider '${name}' configuration is missing required fields: ${missingFields.join(", ")}`,
373
+ { provider: name, missingFields }
237
374
  );
238
375
  }
239
376
  if (!Array.isArray(config.scopes)) {
240
- throw new Error(
241
- `Provider '${name}' configuration error: 'scopes' must be an array of strings`
377
+ throw new InvalidProviderConfigError(
378
+ `Provider '${name}' configuration error: 'scopes' must be an array of strings`,
379
+ { provider: name }
242
380
  );
243
381
  }
244
382
  if (config.scopes.length === 0) {
245
- throw new Error(
246
- `Provider '${name}' configuration error: 'scopes' array cannot be empty`
383
+ throw new InvalidProviderConfigError(
384
+ `Provider '${name}' configuration error: 'scopes' array cannot be empty`,
385
+ { provider: name }
247
386
  );
248
387
  }
249
388
  }
@@ -252,7 +391,7 @@ var Lixa = class _Lixa {
252
391
  *
253
392
  * @param name - The provider name
254
393
  * @param provider - The provider implementation
255
- * @throws Error when required properties are missing
394
+ * @throws InvalidProviderConfigError when required properties are missing
256
395
  */
257
396
  validateProviderImplementation(name, provider) {
258
397
  const requiredProps = ["authorizationEndpoint", "tokenEndpoint", "userInfoEndpoint"];
@@ -261,24 +400,27 @@ var Lixa = class _Lixa {
261
400
  return !value || typeof value !== "string" || value.trim() === "";
262
401
  });
263
402
  if (missingProps.length > 0) {
264
- throw new Error(
265
- `Provider '${name}' implementation is missing required properties: ${missingProps.join(", ")}. All IProvider implementations must define: authorizationEndpoint, tokenEndpoint, and userInfoEndpoint.`
403
+ throw new InvalidProviderConfigError(
404
+ `Provider '${name}' implementation is missing required properties: ${missingProps.join(", ")}. All IProvider implementations must define: authorizationEndpoint, tokenEndpoint, and userInfoEndpoint.`,
405
+ { provider: name, missingProps }
266
406
  );
267
407
  }
268
408
  }
269
409
  /**
270
- * Structured debug logging with standardized format.
410
+ * Structured logging with standardized format and custom logger support.
271
411
  *
272
- * @param level - Log level (INFO, WARN, ERROR)
273
- * @param context - Context of the log (Init, Auth, Token, Session, State)
412
+ * @param level - Log level (INFO, WARN, ERROR, DEBUG)
413
+ * @param context - Context of the log (Init, Auth, Token, Session, State, AccountLinking, Resource)
274
414
  * @param message - Log message
275
415
  * @param data - Optional data to log
276
- *
277
- * @remarks
278
- * Format: [Lixa] [timestamp] [level] [context] message
279
- * Only logs when debug mode is enabled.
280
416
  */
281
417
  log(level, context, message, data) {
418
+ if (this.config.logger) {
419
+ try {
420
+ this.config.logger.log(level, context, message, data);
421
+ } catch {
422
+ }
423
+ }
282
424
  if (!this.debug) return;
283
425
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
284
426
  const prefix = `[Lixa] [${timestamp}] [${level}] [${context}]`;
@@ -329,8 +471,9 @@ var Lixa = class _Lixa {
329
471
  if (legacyProvider) {
330
472
  return legacyProvider;
331
473
  }
332
- throw new Error(
333
- `Provider '${name}' not found. Ensure the provider is included in the configuration with a 'provider' field, or registered using Lixa.registerProvider().`
474
+ throw new ProviderNotConfiguredError(
475
+ name,
476
+ { hint: `Ensure the provider is included in the configuration with a 'provider' field, or registered using Lixa.registerProvider().` }
334
477
  );
335
478
  }
336
479
  /**
@@ -520,23 +663,26 @@ var Lixa = class _Lixa {
520
663
  const providerConfig = this.findProviderByType(providerType);
521
664
  if (!providerConfig) {
522
665
  this.log("ERROR", "Auth", `Provider '${String(provider)}' is not configured`);
523
- throw new Error(`Provider '${String(provider)}' is not configured in this Lixa instance`);
666
+ throw new ProviderNotConfiguredError(String(provider), {
667
+ message: `Provider '${String(provider)}' is not configured in this Lixa instance`
668
+ });
524
669
  }
525
670
  const providerImpl = this.getProvider(providerType, providerConfig);
526
671
  let stateValue;
527
672
  let codeVerifier;
528
- if (this.stateHandler.generateState) {
529
- this.log("INFO", "State", "Calling custom GenerateState");
530
- const generated = await this.stateHandler.generateState(providerType);
673
+ const generateStateFn = this.stateHandler.generateState || this.stateHandler.GenerateState;
674
+ if (generateStateFn) {
675
+ this.log("INFO", "State", "Calling custom generateState");
676
+ const generated = await generateStateFn(providerType);
531
677
  stateValue = state || generated.state;
532
678
  codeVerifier = generated.data.codeVerifier;
533
- const storage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
679
+ const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
534
680
  await storage.saveState(stateValue, generated.data, 300);
535
681
  } else {
536
682
  this.log("INFO", "State", "Using default state generation");
537
683
  stateValue = state || randomBytes2(16).toString("hex");
538
684
  codeVerifier = randomBytes2(32).toString("hex");
539
- const storage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
685
+ const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
540
686
  await storage.saveState(
541
687
  stateValue,
542
688
  {
@@ -545,12 +691,17 @@ var Lixa = class _Lixa {
545
691
  codeVerifier
546
692
  },
547
693
  300
548
- // 5 minutes in seconds
694
+ // 5 minutes in seconds (synchronized with cookie TTL)
549
695
  );
550
696
  }
551
697
  const codeChallenge = _Lixa.buildCodeChallenge(codeVerifier);
552
698
  this.log("INFO", "State", `Saved state for provider: ${providerType}`, { state: stateValue });
553
- const authNScopes = this.resolveAuthNScopes(providerType, providerConfig.scopes, providerImpl);
699
+ const authNScopes = this.resolveAuthNScopes(
700
+ providerType,
701
+ providerConfig.scopes,
702
+ providerImpl,
703
+ providerConfig.allowNonAuthScopes
704
+ );
554
705
  const params = new URLSearchParams({
555
706
  client_id: providerConfig.clientId,
556
707
  redirect_uri: providerConfig.redirectUri,
@@ -569,9 +720,12 @@ var Lixa = class _Lixa {
569
720
  return authUrl;
570
721
  }
571
722
  /**
572
- * Restricts primary authentication scopes strictly to AuthN identity scopes.
723
+ * Restricts primary authentication scopes strictly to AuthN identity scopes unless allowNonAuthScopes is true.
573
724
  */
574
- resolveAuthNScopes(providerType, configuredScopes, providerImpl) {
725
+ resolveAuthNScopes(providerType, configuredScopes, providerImpl, allowNonAuthScopes) {
726
+ if (allowNonAuthScopes) {
727
+ return configuredScopes && configuredScopes.length > 0 ? configuredScopes : providerImpl.authScopes || ["openid", "email", "profile"];
728
+ }
575
729
  const defaultAuthScopes = {
576
730
  google: ["openid", "email", "profile"],
577
731
  github: ["read:user", "user:email"],
@@ -600,7 +754,7 @@ var Lixa = class _Lixa {
600
754
  "Auth",
601
755
  `Primary authentication is strictly limited to AuthN scopes. Excluded non-AuthN resource scopes: [${nonAuthNScopes.join(
602
756
  ", "
603
- )}]. Use lixa.getResourceAuthUrl() post-login to connect resource providers.`
757
+ )}]. Set 'allowNonAuthScopes: true' on provider config to include them, or use lixa.getResourceAuthUrl() post-login to connect resource providers.`
604
758
  );
605
759
  }
606
760
  if (validAuthNScopes.length > 0) {
@@ -636,18 +790,18 @@ var Lixa = class _Lixa {
636
790
  this.log("INFO", "Auth", `Handling OAuth callback for provider: ${providerType}`);
637
791
  if (!code || code.trim() === "") {
638
792
  this.log("ERROR", "Auth", "Invalid or missing authorization code in callback");
639
- throw new Error("Invalid or missing code in callback");
793
+ throw new InvalidOAuthCallbackError("Invalid or missing code in callback");
640
794
  }
641
795
  if (!state || state.trim() === "") {
642
796
  this.log("ERROR", "Auth", "Invalid or missing state in callback");
643
- throw new Error("Invalid or missing state in callback");
797
+ throw new InvalidOAuthCallbackError("Invalid or missing state in callback");
644
798
  }
645
799
  this.log("INFO", "State", "Validating state parameter", { state });
646
- const stateStorage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
800
+ const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
647
801
  const cachedState = await stateStorage.getState(state);
648
802
  if (!cachedState) {
649
803
  this.log("ERROR", "State", "State validation failed: state not found or expired", { state });
650
- throw new Error("Invalid or expired state");
804
+ throw new InvalidStateError("Invalid or expired state", { state });
651
805
  }
652
806
  this.log("INFO", "State", "State validated successfully, removing from cache");
653
807
  await stateStorage.deleteState(state);
@@ -655,7 +809,9 @@ var Lixa = class _Lixa {
655
809
  const providerConfig = this.findProviderByType(providerType);
656
810
  if (!providerConfig) {
657
811
  this.log("ERROR", "Auth", `Provider '${String(provider)}' is not configured`);
658
- throw new Error(`Provider '${String(provider)}' is not configured in this Lixa instance`);
812
+ throw new ProviderNotConfiguredError(String(provider), {
813
+ message: `Provider '${String(provider)}' is not configured in this Lixa instance`
814
+ });
659
815
  }
660
816
  const providerImpl = this.getProvider(providerType, providerConfig);
661
817
  this.log("INFO", "Token", `Exchanging authorization code for tokens`, { provider: providerType });
@@ -681,9 +837,12 @@ var Lixa = class _Lixa {
681
837
  extractedUserInfo = userInfo;
682
838
  } catch {
683
839
  }
684
- const generateSession = this.sessionHandler.generateSession || _Lixa.LOCAL_SESSION_HANDLER.GenerateSession.bind(_Lixa.LOCAL_SESSION_HANDLER);
685
- if (this.sessionHandler.generateSession) {
686
- this.log("INFO", "Session", "Calling custom GenerateSession");
840
+ const generateSession = this.sessionHandler.generateSession || this.sessionHandler.GenerateSession || (this.localSessionHandler.generateSession ? this.localSessionHandler.generateSession.bind(this.localSessionHandler) : void 0);
841
+ if (!generateSession) {
842
+ throw new Error("No session generation handler available");
843
+ }
844
+ if (this.sessionHandler.generateSession || this.sessionHandler.GenerateSession) {
845
+ this.log("INFO", "Session", "Calling custom generateSession");
687
846
  } else {
688
847
  this.log("INFO", "Session", "Using default session generation");
689
848
  }
@@ -692,7 +851,7 @@ var Lixa = class _Lixa {
692
851
  if (extractedUserInfo?.email && !session.email) {
693
852
  session.email = extractedUserInfo.email;
694
853
  }
695
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
854
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
696
855
  const linkingConfig = this.config.accountLinking;
697
856
  const mode = String(linkingConfig?.mode || "");
698
857
  const isLinkByEmail = mode === "AUTO_LINK_BY_VERIFIED_EMAIL" /* AUTO_LINK_BY_VERIFIED_EMAIL */ || mode === "AUTO_LINK_BY_VERIFIED_EMAIL" || mode === "linkByEmail";
@@ -745,27 +904,31 @@ var Lixa = class _Lixa {
745
904
  */
746
905
  async linkAccount(params) {
747
906
  const { sessionId, provider, code, state } = params;
748
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
907
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
749
908
  const existingSession = await sessionStorage.getSession(sessionId);
750
909
  if (!existingSession) {
751
- throw new Error("Invalid session ID. User must be authenticated to link an account.");
910
+ throw new SessionNotFoundError("Invalid session ID. User must be authenticated to link an account.", { sessionId });
911
+ }
912
+ if (!code || code.trim() === "") {
913
+ throw new InvalidOAuthCallbackError("Invalid or missing code in linkAccount");
752
914
  }
753
915
  const providerType = String(provider).toLowerCase();
754
916
  const providerConfig = this.findProviderByType(providerType);
755
917
  if (!providerConfig) {
756
- throw new Error(`Provider '${String(provider)}' is not configured`);
918
+ throw new ProviderNotConfiguredError(String(provider));
757
919
  }
758
920
  const providerImpl = this.getProvider(providerType, providerConfig);
759
- let codeVerifier = randomBytes2(32).toString("hex");
921
+ let codeVerifier;
760
922
  if (state) {
761
- const stateStorage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
923
+ const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
762
924
  const cachedState = await stateStorage.getState(state);
763
- if (cachedState) {
764
- codeVerifier = cachedState.codeVerifier;
765
- await stateStorage.deleteState(state);
925
+ if (!cachedState) {
926
+ throw new InvalidStateError("Invalid or expired state during account linking", { state });
766
927
  }
928
+ codeVerifier = cachedState.codeVerifier;
929
+ await stateStorage.deleteState(state);
767
930
  }
768
- const tokens = await this.exchangeCodeForToken(providerType, providerConfig, providerImpl, codeVerifier);
931
+ const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier || "");
769
932
  const providerMetadata = {
770
933
  name: providerType,
771
934
  endpoints: {
@@ -803,14 +966,17 @@ var Lixa = class _Lixa {
803
966
  * @returns Promise resolving to true on successful unlink
804
967
  */
805
968
  async unlinkAccount(sessionId, providerToUnlink) {
806
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
969
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
807
970
  const session = await sessionStorage.getSession(sessionId);
808
971
  if (!session || !session.accounts) {
809
- throw new Error("Session not found or has no linked accounts.");
972
+ throw new AccountUnlinkError("Session not found or has no linked accounts.", { sessionId });
810
973
  }
811
974
  const linkedProviders = Object.keys(session.accounts);
812
975
  if (linkedProviders.length <= 1) {
813
- throw new Error("Cannot unlink the only authentication provider for this account.");
976
+ throw new AccountUnlinkError("Cannot unlink the only authentication provider for this account.", {
977
+ sessionId,
978
+ provider: providerToUnlink
979
+ });
814
980
  }
815
981
  delete session.accounts[providerToUnlink.toLowerCase()];
816
982
  await sessionStorage.saveSession(sessionId, session, 86400);
@@ -829,20 +995,20 @@ var Lixa = class _Lixa {
829
995
  */
830
996
  async getResourceAuthUrl(params) {
831
997
  const { sessionId, provider, scopes, state, prompt, extraConfig } = params;
832
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
998
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
833
999
  const activeSession = await sessionStorage.getSession(sessionId);
834
1000
  if (!activeSession) {
835
- throw new Error("Authentication required. Active session must exist to connect resource providers.");
1001
+ throw new SessionNotFoundError("Authentication required. Active session must exist to connect resource providers.", { sessionId });
836
1002
  }
837
1003
  const providerType = String(provider).toLowerCase();
838
1004
  const providerConfig = this.findProviderByType(providerType);
839
1005
  if (!providerConfig) {
840
- throw new Error(`Provider '${String(provider)}' is not configured`);
1006
+ throw new ProviderNotConfiguredError(String(provider));
841
1007
  }
842
1008
  const providerImpl = this.getProvider(providerType, providerConfig);
843
1009
  const stateValue = state || randomBytes2(16).toString("hex");
844
1010
  const codeVerifier = randomBytes2(32).toString("hex");
845
- const storage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
1011
+ const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
846
1012
  await storage.saveState(
847
1013
  stateValue,
848
1014
  {
@@ -867,77 +1033,245 @@ var Lixa = class _Lixa {
867
1033
  });
868
1034
  return `${providerImpl.authorizationEndpoint}?${searchParams.toString()}`;
869
1035
  }
1036
+ getUserKeyFromSession(session) {
1037
+ return session.userId || session.email || session.id || "anonymous";
1038
+ }
870
1039
  /**
871
- * Handles the OAuth callback for a connected resource provider and stores resource tokens on the session.
872
- *
873
- * @param params - Object containing sessionId, provider, code, state, and requested scopes
874
- * @returns Updated Session containing stored resource tokens under session.resources[provider]
1040
+ * Handles the OAuth callback for a connected resource provider and stores resource tokens bound to user account.
875
1041
  */
876
1042
  async handleResourceCallback(params) {
877
1043
  const { sessionId, provider, code, state, scopes } = params;
878
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1044
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
879
1045
  const activeSession = await sessionStorage.getSession(sessionId);
880
1046
  if (!activeSession) {
881
- throw new Error("Authentication required. Active session not found for resource connection.");
1047
+ throw new SessionNotFoundError("Authentication required. Active session not found for resource connection.", { sessionId });
882
1048
  }
883
1049
  const providerType = String(provider).toLowerCase();
884
1050
  const providerConfig = this.findProviderByType(providerType);
885
1051
  if (!providerConfig) {
886
- throw new Error(`Provider '${String(provider)}' is not configured`);
1052
+ throw new ProviderNotConfiguredError(String(provider));
887
1053
  }
888
1054
  const providerImpl = this.getProvider(providerType, providerConfig);
889
1055
  let codeVerifier = randomBytes2(32).toString("hex");
890
1056
  if (state) {
891
- const stateStorage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
1057
+ const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
892
1058
  const cachedState = await stateStorage.getState(state);
893
- if (cachedState) {
894
- codeVerifier = cachedState.codeVerifier;
895
- await stateStorage.deleteState(state);
1059
+ if (!cachedState) {
1060
+ throw new InvalidStateError("Invalid or expired state during resource connection callback", { state });
896
1061
  }
1062
+ codeVerifier = cachedState.codeVerifier;
1063
+ await stateStorage.deleteState(state);
897
1064
  }
898
1065
  const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier);
899
- activeSession.resources = activeSession.resources || {};
900
- activeSession.resources[providerType] = {
1066
+ const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
1067
+ const connectedResource = {
901
1068
  provider: providerType,
902
1069
  accessToken: tokens.access_token,
903
1070
  refreshToken: tokens.refresh_token,
1071
+ expiresAt,
904
1072
  scopes: scopes || (tokens.scope ? tokens.scope.split(" ") : []),
905
1073
  raw: tokens,
906
1074
  connectedAt: Date.now()
907
1075
  };
1076
+ const userKey = this.getUserKeyFromSession(activeSession);
1077
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1078
+ await resourceStorage.saveResource(userKey, providerType, connectedResource);
1079
+ activeSession.resources = activeSession.resources || {};
1080
+ activeSession.resources[providerType] = connectedResource;
908
1081
  await sessionStorage.saveSession(sessionId, activeSession, 86400);
909
1082
  return activeSession;
910
1083
  }
911
1084
  /**
912
- * Retrieves a connected resource provider token for an active session.
1085
+ * Retrieves a connected resource for a specific User ID / Email directly (independent of session IDs).
1086
+ * Automatically refreshes expired access tokens if a refresh token is present.
1087
+ *
1088
+ * @param userIdOrEmail - User identifier or email
1089
+ * @param provider - Resource provider identifier (e.g. 'github', 'google')
1090
+ */
1091
+ async getUserResource(userIdOrEmail, provider) {
1092
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1093
+ const providerType = provider.toLowerCase();
1094
+ const resource = await resourceStorage.getResource(userIdOrEmail, providerType);
1095
+ if (!resource) return null;
1096
+ if (resource.refreshToken && resource.expiresAt && Date.now() >= resource.expiresAt - 6e4) {
1097
+ this.log("INFO", "Token", `Resource access token for user '${userIdOrEmail}' on '${providerType}' is expired. Auto-refreshing...`);
1098
+ try {
1099
+ return await this.refreshUserResourceToken(userIdOrEmail, providerType);
1100
+ } catch (error) {
1101
+ this.log("ERROR", "Token", `Failed to auto-refresh resource token for user '${userIdOrEmail}' on '${providerType}'`, { error: String(error) });
1102
+ }
1103
+ }
1104
+ return resource;
1105
+ }
1106
+ /**
1107
+ * Retrieves all connected resources for a specific User ID / Email.
1108
+ */
1109
+ async getUserResources(userIdOrEmail) {
1110
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1111
+ return await resourceStorage.getUserResources(userIdOrEmail);
1112
+ }
1113
+ /**
1114
+ * Retrieves a connected resource provider token for an active session, auto-refreshing expired tokens if possible.
913
1115
  *
914
1116
  * @param sessionId - Active session ID
915
- * @param provider - Provider identifier (e.g. 'github')
1117
+ * @param provider - Provider identifier (e.g. 'github', 'google')
916
1118
  */
917
1119
  async getConnectedResource(sessionId, provider) {
918
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1120
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
919
1121
  const activeSession = await sessionStorage.getSession(sessionId);
920
- if (!activeSession || !activeSession.resources) return null;
921
- return activeSession.resources[provider.toLowerCase()] || null;
1122
+ if (!activeSession) return null;
1123
+ const providerType = provider.toLowerCase();
1124
+ const userKey = this.getUserKeyFromSession(activeSession);
1125
+ let resource = await this.getUserResource(userKey, providerType);
1126
+ if (!resource && activeSession.resources) {
1127
+ resource = activeSession.resources[providerType] || null;
1128
+ }
1129
+ if (resource) {
1130
+ if (resource.refreshToken && resource.expiresAt && Date.now() >= resource.expiresAt - 6e4) {
1131
+ this.log("INFO", "Token", `Resource access token for user '${userKey}' on '${providerType}' is expired. Auto-refreshing...`);
1132
+ try {
1133
+ resource = await this.refreshResourceToken(sessionId, providerType);
1134
+ } catch (error) {
1135
+ this.log("ERROR", "Token", `Failed to auto-refresh resource token for user '${userKey}' on '${providerType}'`, { error: String(error) });
1136
+ }
1137
+ }
1138
+ activeSession.resources = activeSession.resources || {};
1139
+ activeSession.resources[providerType] = resource;
1140
+ }
1141
+ return resource;
922
1142
  }
923
1143
  /**
924
- * Disconnects a resource provider from an active session.
1144
+ * Refreshes a user's resource access token using its refresh token.
1145
+ * Deduplicates concurrent refresh requests via an in-flight promise mutex.
925
1146
  *
926
- * @param sessionId - Active session ID
927
- * @param provider - Provider identifier to disconnect
1147
+ * @param userIdOrEmail - User identifier or email
1148
+ * @param provider - Provider identifier (e.g. 'google', 'github')
928
1149
  */
929
- async disconnectResource(sessionId, provider) {
930
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1150
+ async refreshUserResourceToken(userIdOrEmail, provider, existingResource) {
1151
+ const mutexKey = `${userIdOrEmail.toLowerCase()}:${provider.toLowerCase()}`;
1152
+ const existingPromise = this.refreshMutexes.get(mutexKey);
1153
+ if (existingPromise) {
1154
+ this.log("INFO", "Token", `Concurrent refresh request detected for '${mutexKey}'. Reusing in-flight promise.`);
1155
+ return existingPromise;
1156
+ }
1157
+ const refreshPromise = this.executeRefreshUserResourceToken(userIdOrEmail, provider, existingResource).finally(() => {
1158
+ this.refreshMutexes.delete(mutexKey);
1159
+ });
1160
+ this.refreshMutexes.set(mutexKey, refreshPromise);
1161
+ return refreshPromise;
1162
+ }
1163
+ /**
1164
+ * Internal execution of refresh token exchange.
1165
+ */
1166
+ async executeRefreshUserResourceToken(userIdOrEmail, provider, existingResource) {
1167
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1168
+ const providerType = provider.toLowerCase();
1169
+ let resource = existingResource || await resourceStorage.getResource(userIdOrEmail, providerType);
1170
+ if (!resource || !resource.refreshToken) {
1171
+ throw new RefreshTokenError(
1172
+ `No refresh token available for user '${userIdOrEmail}' on connected resource '${provider}'`,
1173
+ { userId: userIdOrEmail, provider }
1174
+ );
1175
+ }
1176
+ const providerConfig = this.findProviderByType(providerType);
1177
+ if (!providerConfig) {
1178
+ throw new ProviderNotConfiguredError(provider);
1179
+ }
1180
+ const providerImpl = this.getProvider(providerType, providerConfig);
1181
+ const body = {
1182
+ client_id: providerConfig.clientId,
1183
+ client_secret: providerConfig.clientSecret,
1184
+ grant_type: "refresh_token",
1185
+ refresh_token: resource.refreshToken
1186
+ };
1187
+ const response = await fetch(providerImpl.tokenEndpoint, {
1188
+ method: "POST",
1189
+ headers: {
1190
+ "Content-Type": "application/x-www-form-urlencoded",
1191
+ Accept: "application/json"
1192
+ },
1193
+ body: new URLSearchParams(body).toString()
1194
+ });
1195
+ if (!response.ok) {
1196
+ const errorText = await response.text();
1197
+ throw new RefreshTokenError(
1198
+ `Failed to refresh resource token for user '${userIdOrEmail}' on '${provider}': ${response.status} - ${errorText}`,
1199
+ { userId: userIdOrEmail, provider, status: response.status }
1200
+ );
1201
+ }
1202
+ const tokens = await response.json();
1203
+ const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
1204
+ resource.accessToken = tokens.access_token;
1205
+ if (tokens.refresh_token) {
1206
+ resource.refreshToken = tokens.refresh_token;
1207
+ }
1208
+ if (expiresAt) {
1209
+ resource.expiresAt = expiresAt;
1210
+ }
1211
+ resource.raw = tokens;
1212
+ resource.connectedAt = Date.now();
1213
+ await resourceStorage.saveResource(userIdOrEmail, providerType, resource);
1214
+ return resource;
1215
+ }
1216
+ /**
1217
+ * Refreshes a connected resource access token for an active session.
1218
+ */
1219
+ async refreshResourceToken(sessionId, provider) {
1220
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
931
1221
  const activeSession = await sessionStorage.getSession(sessionId);
932
- if (!activeSession || !activeSession.resources) return false;
933
- delete activeSession.resources[provider.toLowerCase()];
1222
+ if (!activeSession) {
1223
+ throw new SessionNotFoundError("Active session not found.", { sessionId });
1224
+ }
1225
+ const providerType = provider.toLowerCase();
1226
+ const userKey = this.getUserKeyFromSession(activeSession);
1227
+ const existingResource = activeSession.resources ? activeSession.resources[providerType] : void 0;
1228
+ const refreshed = await this.refreshUserResourceToken(userKey, providerType, existingResource);
1229
+ activeSession.resources = activeSession.resources || {};
1230
+ activeSession.resources[providerType] = refreshed;
934
1231
  await sessionStorage.saveSession(sessionId, activeSession, 86400);
1232
+ return refreshed;
1233
+ }
1234
+ /**
1235
+ * Disconnects a resource provider for a specific User ID / Email.
1236
+ */
1237
+ async disconnectUserResource(userIdOrEmail, provider) {
1238
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1239
+ await resourceStorage.deleteResource(userIdOrEmail, provider.toLowerCase());
935
1240
  return true;
936
1241
  }
1242
+ /**
1243
+ * Disconnects a resource provider from an active session and user account.
1244
+ */
1245
+ async disconnectResource(sessionId, provider) {
1246
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1247
+ const activeSession = await sessionStorage.getSession(sessionId);
1248
+ if (activeSession) {
1249
+ const userKey = this.getUserKeyFromSession(activeSession);
1250
+ await this.disconnectUserResource(userKey, provider);
1251
+ if (activeSession.resources) {
1252
+ delete activeSession.resources[provider.toLowerCase()];
1253
+ await sessionStorage.saveSession(sessionId, activeSession, 86400);
1254
+ }
1255
+ return true;
1256
+ }
1257
+ return false;
1258
+ }
1259
+ /**
1260
+ * Retrieves active session details from session storage.
1261
+ */
937
1262
  async fetchSessionInfo(sessionId) {
938
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1263
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
939
1264
  return await sessionStorage.getSession(sessionId);
940
1265
  }
1266
+ /**
1267
+ * Deletes a session from session storage (e.g. on logout).
1268
+ *
1269
+ * @param sessionId - Active session identifier
1270
+ */
1271
+ async deleteSession(sessionId) {
1272
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1273
+ await sessionStorage.deleteSession(sessionId);
1274
+ }
941
1275
  async exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier) {
942
1276
  const body = {
943
1277
  client_id: providerConfig.clientId,
@@ -968,8 +1302,10 @@ var Lixa = class _Lixa {
968
1302
  statusText: response.statusText,
969
1303
  error: errorBody
970
1304
  });
971
- throw new Error(
972
- `Token exchange failed: ${response.status} ${response.statusText} - ${errorBody}`
1305
+ throw new TokenExchangeError(
1306
+ `Token exchange failed: ${response.status} ${response.statusText} - ${errorBody}`,
1307
+ response.status,
1308
+ { statusText: response.statusText, errorBody }
973
1309
  );
974
1310
  }
975
1311
  this.log("INFO", "Token", "Token exchange response received successfully");
@@ -984,12 +1320,151 @@ var Lixa = class _Lixa {
984
1320
  return void 0;
985
1321
  }
986
1322
  };
1323
+
1324
+ // src/utils/cookies.ts
1325
+ var DEFAULT_SESSION_COOKIE_NAME = "lixa_session";
1326
+ var DEFAULT_STATE_COOKIE_NAME = "lixa_oauth_state";
1327
+ var DEFAULT_SESSION_MAX_AGE_SECONDS = 24 * 60 * 60;
1328
+ var DEFAULT_STATE_MAX_AGE_SECONDS = 5 * 60;
1329
+ function isProductionEnvironment() {
1330
+ return typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1331
+ }
1332
+ function serializeCookie(name, value, options) {
1333
+ const isProd = isProductionEnvironment();
1334
+ const path = options?.path ?? "/";
1335
+ const httpOnly = options?.httpOnly ?? true;
1336
+ const secure = options?.secure ?? isProd;
1337
+ const sameSite = options?.sameSite ?? "lax";
1338
+ const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
1339
+ if (path) {
1340
+ parts.push(`Path=${path}`);
1341
+ }
1342
+ if (typeof options?.maxAge === "number") {
1343
+ parts.push(`Max-Age=${Math.floor(options.maxAge)}`);
1344
+ const expires = new Date(Date.now() + options.maxAge * 1e3).toUTCString();
1345
+ parts.push(`Expires=${expires}`);
1346
+ }
1347
+ if (options?.domain) {
1348
+ parts.push(`Domain=${options.domain}`);
1349
+ }
1350
+ if (httpOnly) {
1351
+ parts.push("HttpOnly");
1352
+ }
1353
+ if (secure) {
1354
+ parts.push("Secure");
1355
+ }
1356
+ if (sameSite) {
1357
+ const capitalized = sameSite.charAt(0).toUpperCase() + sameSite.slice(1).toLowerCase();
1358
+ parts.push(`SameSite=${capitalized}`);
1359
+ }
1360
+ return parts.join("; ");
1361
+ }
1362
+ function createSessionCookie(sessionId, options) {
1363
+ const isProd = isProductionEnvironment();
1364
+ const resolvedOptions = {
1365
+ name: options?.name || DEFAULT_SESSION_COOKIE_NAME,
1366
+ path: options?.path ?? "/",
1367
+ maxAge: options?.maxAge ?? DEFAULT_SESSION_MAX_AGE_SECONDS,
1368
+ httpOnly: options?.httpOnly ?? true,
1369
+ secure: options?.secure ?? isProd,
1370
+ sameSite: options?.sameSite ?? "lax",
1371
+ domain: options?.domain
1372
+ };
1373
+ const name = resolvedOptions.name;
1374
+ const header = serializeCookie(name, sessionId, resolvedOptions);
1375
+ return {
1376
+ name,
1377
+ value: sessionId,
1378
+ options: resolvedOptions,
1379
+ header
1380
+ };
1381
+ }
1382
+ function clearSessionCookie(options) {
1383
+ const isProd = isProductionEnvironment();
1384
+ const resolvedOptions = {
1385
+ name: options?.name || DEFAULT_SESSION_COOKIE_NAME,
1386
+ path: options?.path ?? "/",
1387
+ maxAge: 0,
1388
+ httpOnly: options?.httpOnly ?? true,
1389
+ secure: options?.secure ?? isProd,
1390
+ sameSite: options?.sameSite ?? "lax",
1391
+ domain: options?.domain
1392
+ };
1393
+ const name = resolvedOptions.name;
1394
+ const header = serializeCookie(name, "", resolvedOptions);
1395
+ return {
1396
+ name,
1397
+ value: "",
1398
+ options: resolvedOptions,
1399
+ header
1400
+ };
1401
+ }
1402
+ function createStateCookie(state, options) {
1403
+ const isProd = isProductionEnvironment();
1404
+ const resolvedOptions = {
1405
+ name: options?.name || DEFAULT_STATE_COOKIE_NAME,
1406
+ path: options?.path ?? "/",
1407
+ maxAge: options?.maxAge ?? DEFAULT_STATE_MAX_AGE_SECONDS,
1408
+ httpOnly: options?.httpOnly ?? true,
1409
+ secure: options?.secure ?? isProd,
1410
+ sameSite: options?.sameSite ?? "lax",
1411
+ domain: options?.domain
1412
+ };
1413
+ const name = resolvedOptions.name;
1414
+ const header = serializeCookie(name, state, resolvedOptions);
1415
+ return {
1416
+ name,
1417
+ value: state,
1418
+ options: resolvedOptions,
1419
+ header
1420
+ };
1421
+ }
1422
+ function clearStateCookie(options) {
1423
+ const isProd = isProductionEnvironment();
1424
+ const resolvedOptions = {
1425
+ name: options?.name || DEFAULT_STATE_COOKIE_NAME,
1426
+ path: options?.path ?? "/",
1427
+ maxAge: 0,
1428
+ httpOnly: options?.httpOnly ?? true,
1429
+ secure: options?.secure ?? isProd,
1430
+ sameSite: options?.sameSite ?? "lax",
1431
+ domain: options?.domain
1432
+ };
1433
+ const name = resolvedOptions.name;
1434
+ const header = serializeCookie(name, "", resolvedOptions);
1435
+ return {
1436
+ name,
1437
+ value: "",
1438
+ options: resolvedOptions,
1439
+ header
1440
+ };
1441
+ }
987
1442
  export {
988
1443
  AccountLinkingStrategy,
1444
+ AccountUnlinkError,
1445
+ DEFAULT_SESSION_COOKIE_NAME,
1446
+ DEFAULT_SESSION_MAX_AGE_SECONDS,
1447
+ DEFAULT_STATE_COOKIE_NAME,
1448
+ DEFAULT_STATE_MAX_AGE_SECONDS,
1449
+ EmailNotVerifiedError,
1450
+ InvalidOAuthCallbackError,
1451
+ InvalidProviderConfigError,
1452
+ InvalidStateError,
989
1453
  Lixa,
1454
+ LixaError,
1455
+ ProviderNotConfiguredError,
1456
+ RefreshTokenError,
1457
+ SessionNotFoundError,
1458
+ TokenExchangeError,
1459
+ clearSessionCookie,
1460
+ clearStateCookie,
1461
+ createSessionCookie,
1462
+ createStateCookie,
990
1463
  decodeIdToken,
991
1464
  determineProviderFromIssuer,
992
1465
  extractUserInfo,
993
- fetchUserInfo
1466
+ fetchUserInfo,
1467
+ isProductionEnvironment,
1468
+ serializeCookie
994
1469
  };
995
1470
  //# sourceMappingURL=index.js.map