@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.cjs CHANGED
@@ -31,11 +31,31 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  AccountLinkingStrategy: () => AccountLinkingStrategy,
34
+ AccountUnlinkError: () => AccountUnlinkError,
35
+ DEFAULT_SESSION_COOKIE_NAME: () => DEFAULT_SESSION_COOKIE_NAME,
36
+ DEFAULT_SESSION_MAX_AGE_SECONDS: () => DEFAULT_SESSION_MAX_AGE_SECONDS,
37
+ DEFAULT_STATE_COOKIE_NAME: () => DEFAULT_STATE_COOKIE_NAME,
38
+ DEFAULT_STATE_MAX_AGE_SECONDS: () => DEFAULT_STATE_MAX_AGE_SECONDS,
39
+ EmailNotVerifiedError: () => EmailNotVerifiedError,
40
+ InvalidOAuthCallbackError: () => InvalidOAuthCallbackError,
41
+ InvalidProviderConfigError: () => InvalidProviderConfigError,
42
+ InvalidStateError: () => InvalidStateError,
34
43
  Lixa: () => Lixa,
44
+ LixaError: () => LixaError,
45
+ ProviderNotConfiguredError: () => ProviderNotConfiguredError,
46
+ RefreshTokenError: () => RefreshTokenError,
47
+ SessionNotFoundError: () => SessionNotFoundError,
48
+ TokenExchangeError: () => TokenExchangeError,
49
+ clearSessionCookie: () => clearSessionCookie,
50
+ clearStateCookie: () => clearStateCookie,
51
+ createSessionCookie: () => createSessionCookie,
52
+ createStateCookie: () => createStateCookie,
35
53
  decodeIdToken: () => decodeIdToken,
36
54
  determineProviderFromIssuer: () => determineProviderFromIssuer,
37
55
  extractUserInfo: () => extractUserInfo,
38
- fetchUserInfo: () => fetchUserInfo
56
+ fetchUserInfo: () => fetchUserInfo,
57
+ isProductionEnvironment: () => isProductionEnvironment,
58
+ serializeCookie: () => serializeCookie
39
59
  });
40
60
  module.exports = __toCommonJS(src_exports);
41
61
 
@@ -69,7 +89,7 @@ var LocalStateHandler = class {
69
89
  }
70
90
  };
71
91
  }
72
- // Default GenerateState implementation
92
+ // Default generateState implementation
73
93
  async generateState(provider) {
74
94
  const state = (0, import_crypto.randomBytes)(16).toString("hex");
75
95
  const codeVerifier = (0, import_crypto.randomBytes)(32).toString("hex");
@@ -82,6 +102,10 @@ var LocalStateHandler = class {
82
102
  }
83
103
  };
84
104
  }
105
+ // PascalCase alias for backward compatibility
106
+ async GenerateState(provider) {
107
+ return this.generateState(provider);
108
+ }
85
109
  };
86
110
 
87
111
  // src/lixa.ts
@@ -94,7 +118,20 @@ var LocalSessionHandler = class {
94
118
  emailToSessionMap = /* @__PURE__ */ new Map();
95
119
  sessionStorage;
96
120
  constructor(defaultTtlSeconds = 600) {
97
- this.cache = new import_node_cache2.default({ stdTTL: defaultTtlSeconds });
121
+ this.cache = new import_node_cache2.default({ stdTTL: defaultTtlSeconds, checkperiod: 60 });
122
+ this.cache.on("expired", (_key, value) => {
123
+ if (value && typeof value === "object" && value.email) {
124
+ this.emailToSessionMap.delete(String(value.email).toLowerCase());
125
+ }
126
+ });
127
+ this.cache.on("del", (_key, value) => {
128
+ if (value && typeof value === "object" && value.email) {
129
+ this.emailToSessionMap.delete(String(value.email).toLowerCase());
130
+ }
131
+ });
132
+ this.cache.on("flush", () => {
133
+ this.emailToSessionMap.clear();
134
+ });
98
135
  this.sessionStorage = {
99
136
  saveSession: async (sessionId, session, expiresInSeconds) => {
100
137
  this.cache.set(sessionId, session, expiresInSeconds);
@@ -125,8 +162,8 @@ var LocalSessionHandler = class {
125
162
  }
126
163
  };
127
164
  }
128
- // Default GenerateSession implementation
129
- async GenerateSession(tokenData, providerMetadata) {
165
+ // Default generateSession implementation
166
+ async generateSession(tokenData, _providerMetadata) {
130
167
  if (!tokenData.access_token || typeof tokenData.access_token !== "string") {
131
168
  throw new Error("No valid access token found in OAuth response");
132
169
  }
@@ -136,6 +173,10 @@ var LocalSessionHandler = class {
136
173
  };
137
174
  return session;
138
175
  }
176
+ // PascalCase alias for backward compatibility
177
+ async GenerateSession(tokenData, providerMetadata) {
178
+ return this.generateSession(tokenData, providerMetadata);
179
+ }
139
180
  };
140
181
 
141
182
  // src/utils/user-info.ts
@@ -207,16 +248,95 @@ async function extractUserInfo(tokenData, providerMetadata) {
207
248
  return { userInfo };
208
249
  }
209
250
 
251
+ // src/errors.ts
252
+ var LixaError = class extends Error {
253
+ /**
254
+ * Standard error code string.
255
+ */
256
+ code;
257
+ /**
258
+ * Additional error context data.
259
+ */
260
+ details;
261
+ constructor(message, code = "LIXA_ERROR", details) {
262
+ super(message);
263
+ this.name = this.constructor.name;
264
+ this.code = code;
265
+ this.details = details;
266
+ Object.setPrototypeOf(this, new.target.prototype);
267
+ }
268
+ };
269
+ var InvalidStateError = class extends LixaError {
270
+ constructor(message = "Invalid or expired state", details) {
271
+ super(message, "INVALID_STATE", details);
272
+ }
273
+ };
274
+ var ProviderNotConfiguredError = class extends LixaError {
275
+ constructor(provider, details) {
276
+ 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: '...', ... }`;
277
+ super(message, "PROVIDER_NOT_CONFIGURED", {
278
+ provider,
279
+ ...details
280
+ });
281
+ }
282
+ };
283
+ var InvalidProviderConfigError = class extends LixaError {
284
+ constructor(message, details) {
285
+ super(message, "INVALID_PROVIDER_CONFIG", details);
286
+ }
287
+ };
288
+ var InvalidOAuthCallbackError = class extends LixaError {
289
+ constructor(message, details) {
290
+ super(message, "INVALID_OAUTH_CALLBACK", details);
291
+ }
292
+ };
293
+ var TokenExchangeError = class extends LixaError {
294
+ status;
295
+ constructor(message, status, details) {
296
+ super(message, "TOKEN_EXCHANGE_FAILED", { status, ...details });
297
+ this.status = status;
298
+ }
299
+ };
300
+ var SessionNotFoundError = class extends LixaError {
301
+ constructor(message = "Active session not found or has expired", details) {
302
+ super(message, "SESSION_NOT_FOUND", details);
303
+ }
304
+ };
305
+ var EmailNotVerifiedError = class extends LixaError {
306
+ constructor(email, details) {
307
+ super(
308
+ `Cannot link account: email '${email || "unknown"}' is not verified by the identity provider`,
309
+ "EMAIL_NOT_VERIFIED",
310
+ { email, ...details }
311
+ );
312
+ }
313
+ };
314
+ var AccountUnlinkError = class extends LixaError {
315
+ constructor(message, details) {
316
+ super(message, "ACCOUNT_UNLINK_ERROR", details);
317
+ }
318
+ };
319
+ var RefreshTokenError = class extends LixaError {
320
+ constructor(message, details) {
321
+ super(message, "REFRESH_TOKEN_ERROR", details);
322
+ }
323
+ };
324
+
210
325
  // src/lixa.ts
211
326
  var Lixa = class _Lixa {
212
327
  static DEFAULT_PROVIDERS = /* @__PURE__ */ new Map();
213
328
  static CONFIGURED_PROVIDERS = /* @__PURE__ */ new Map();
214
329
  // Legacy registry for backward compatibility
215
- static LOCAL_STATE_HANDLER = new LocalStateHandler();
216
- static LOCAL_SESSION_HANDLER = new LocalSessionHandler();
330
+ // Instance-scoped fallback handlers to ensure no cross-instance state pollution
331
+ localStateHandler;
332
+ localSessionHandler;
333
+ localResourceHandler;
334
+ userResourceStore = /* @__PURE__ */ new Map();
335
+ refreshMutexes = /* @__PURE__ */ new Map();
217
336
  config;
218
337
  stateHandler;
219
338
  sessionHandler;
339
+ resourceHandler;
220
340
  debug;
221
341
  /**
222
342
  * Creates a new Lixa instance with the provided configuration.
@@ -233,9 +353,44 @@ var Lixa = class _Lixa {
233
353
  */
234
354
  constructor(config) {
235
355
  this.config = config;
236
- this.stateHandler = config.stateHandler || _Lixa.LOCAL_STATE_HANDLER;
237
- this.sessionHandler = config.sessionHandler || _Lixa.LOCAL_SESSION_HANDLER;
238
356
  this.debug = config.debug || false;
357
+ this.localStateHandler = new LocalStateHandler();
358
+ this.localSessionHandler = new LocalSessionHandler();
359
+ this.localResourceHandler = {
360
+ resourceStorage: {
361
+ saveResource: async (userId, provider, resource) => {
362
+ let userMap = this.userResourceStore.get(userId);
363
+ if (!userMap) {
364
+ userMap = /* @__PURE__ */ new Map();
365
+ this.userResourceStore.set(userId, userMap);
366
+ }
367
+ userMap.set(provider.toLowerCase(), resource);
368
+ },
369
+ getResource: async (userId, provider) => {
370
+ const userMap = this.userResourceStore.get(userId);
371
+ return userMap?.get(provider.toLowerCase()) || null;
372
+ },
373
+ getUserResources: async (userId) => {
374
+ const userMap = this.userResourceStore.get(userId);
375
+ const result = {};
376
+ if (userMap) {
377
+ for (const [p, r] of userMap.entries()) {
378
+ result[p] = r;
379
+ }
380
+ }
381
+ return result;
382
+ },
383
+ deleteResource: async (userId, provider) => {
384
+ const userMap = this.userResourceStore.get(userId);
385
+ if (userMap) {
386
+ userMap.delete(provider.toLowerCase());
387
+ }
388
+ }
389
+ }
390
+ };
391
+ this.stateHandler = config.stateHandler || this.localStateHandler;
392
+ this.sessionHandler = config.sessionHandler || this.localSessionHandler;
393
+ this.resourceHandler = config.resourceHandler || this.localResourceHandler;
239
394
  this.log("INFO", "Init", "Initializing Lixa instance", {
240
395
  providers: Object.keys(config.providers),
241
396
  debug: this.debug
@@ -250,8 +405,9 @@ var Lixa = class _Lixa {
250
405
  } else {
251
406
  if (!_Lixa.DEFAULT_PROVIDERS.has(name) && !_Lixa.CONFIGURED_PROVIDERS.has(name)) {
252
407
  this.log("ERROR", "Init", `Provider '${providerName}' not available`);
253
- throw new Error(
254
- `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: '...', ... }`
408
+ throw new ProviderNotConfiguredError(
409
+ providerName,
410
+ { hint: `Ensure the provider is registered via '@vunexa/lixa-providers' or provided inline.` }
255
411
  );
256
412
  }
257
413
  this.log("INFO", "Init", `Using registered provider: ${providerName}`);
@@ -264,7 +420,7 @@ var Lixa = class _Lixa {
264
420
  *
265
421
  * @param name - The provider name
266
422
  * @param config - The provider configuration
267
- * @throws Error when required fields are missing or invalid
423
+ * @throws InvalidProviderConfigError when required fields are missing or invalid
268
424
  */
269
425
  validateProviderConfig(name, config) {
270
426
  const requiredFields = ["clientId", "clientSecret", "redirectUri", "scopes"];
@@ -273,18 +429,21 @@ var Lixa = class _Lixa {
273
429
  return value === void 0 || value === null || typeof value === "string" && value.trim() === "";
274
430
  });
275
431
  if (missingFields.length > 0) {
276
- throw new Error(
277
- `Provider '${name}' configuration is missing required fields: ${missingFields.join(", ")}`
432
+ throw new InvalidProviderConfigError(
433
+ `Provider '${name}' configuration is missing required fields: ${missingFields.join(", ")}`,
434
+ { provider: name, missingFields }
278
435
  );
279
436
  }
280
437
  if (!Array.isArray(config.scopes)) {
281
- throw new Error(
282
- `Provider '${name}' configuration error: 'scopes' must be an array of strings`
438
+ throw new InvalidProviderConfigError(
439
+ `Provider '${name}' configuration error: 'scopes' must be an array of strings`,
440
+ { provider: name }
283
441
  );
284
442
  }
285
443
  if (config.scopes.length === 0) {
286
- throw new Error(
287
- `Provider '${name}' configuration error: 'scopes' array cannot be empty`
444
+ throw new InvalidProviderConfigError(
445
+ `Provider '${name}' configuration error: 'scopes' array cannot be empty`,
446
+ { provider: name }
288
447
  );
289
448
  }
290
449
  }
@@ -293,7 +452,7 @@ var Lixa = class _Lixa {
293
452
  *
294
453
  * @param name - The provider name
295
454
  * @param provider - The provider implementation
296
- * @throws Error when required properties are missing
455
+ * @throws InvalidProviderConfigError when required properties are missing
297
456
  */
298
457
  validateProviderImplementation(name, provider) {
299
458
  const requiredProps = ["authorizationEndpoint", "tokenEndpoint", "userInfoEndpoint"];
@@ -302,24 +461,27 @@ var Lixa = class _Lixa {
302
461
  return !value || typeof value !== "string" || value.trim() === "";
303
462
  });
304
463
  if (missingProps.length > 0) {
305
- throw new Error(
306
- `Provider '${name}' implementation is missing required properties: ${missingProps.join(", ")}. All IProvider implementations must define: authorizationEndpoint, tokenEndpoint, and userInfoEndpoint.`
464
+ throw new InvalidProviderConfigError(
465
+ `Provider '${name}' implementation is missing required properties: ${missingProps.join(", ")}. All IProvider implementations must define: authorizationEndpoint, tokenEndpoint, and userInfoEndpoint.`,
466
+ { provider: name, missingProps }
307
467
  );
308
468
  }
309
469
  }
310
470
  /**
311
- * Structured debug logging with standardized format.
471
+ * Structured logging with standardized format and custom logger support.
312
472
  *
313
- * @param level - Log level (INFO, WARN, ERROR)
314
- * @param context - Context of the log (Init, Auth, Token, Session, State)
473
+ * @param level - Log level (INFO, WARN, ERROR, DEBUG)
474
+ * @param context - Context of the log (Init, Auth, Token, Session, State, AccountLinking, Resource)
315
475
  * @param message - Log message
316
476
  * @param data - Optional data to log
317
- *
318
- * @remarks
319
- * Format: [Lixa] [timestamp] [level] [context] message
320
- * Only logs when debug mode is enabled.
321
477
  */
322
478
  log(level, context, message, data) {
479
+ if (this.config.logger) {
480
+ try {
481
+ this.config.logger.log(level, context, message, data);
482
+ } catch {
483
+ }
484
+ }
323
485
  if (!this.debug) return;
324
486
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
325
487
  const prefix = `[Lixa] [${timestamp}] [${level}] [${context}]`;
@@ -370,8 +532,9 @@ var Lixa = class _Lixa {
370
532
  if (legacyProvider) {
371
533
  return legacyProvider;
372
534
  }
373
- throw new Error(
374
- `Provider '${name}' not found. Ensure the provider is included in the configuration with a 'provider' field, or registered using Lixa.registerProvider().`
535
+ throw new ProviderNotConfiguredError(
536
+ name,
537
+ { hint: `Ensure the provider is included in the configuration with a 'provider' field, or registered using Lixa.registerProvider().` }
375
538
  );
376
539
  }
377
540
  /**
@@ -561,23 +724,26 @@ var Lixa = class _Lixa {
561
724
  const providerConfig = this.findProviderByType(providerType);
562
725
  if (!providerConfig) {
563
726
  this.log("ERROR", "Auth", `Provider '${String(provider)}' is not configured`);
564
- throw new Error(`Provider '${String(provider)}' is not configured in this Lixa instance`);
727
+ throw new ProviderNotConfiguredError(String(provider), {
728
+ message: `Provider '${String(provider)}' is not configured in this Lixa instance`
729
+ });
565
730
  }
566
731
  const providerImpl = this.getProvider(providerType, providerConfig);
567
732
  let stateValue;
568
733
  let codeVerifier;
569
- if (this.stateHandler.generateState) {
570
- this.log("INFO", "State", "Calling custom GenerateState");
571
- const generated = await this.stateHandler.generateState(providerType);
734
+ const generateStateFn = this.stateHandler.generateState || this.stateHandler.GenerateState;
735
+ if (generateStateFn) {
736
+ this.log("INFO", "State", "Calling custom generateState");
737
+ const generated = await generateStateFn(providerType);
572
738
  stateValue = state || generated.state;
573
739
  codeVerifier = generated.data.codeVerifier;
574
- const storage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
740
+ const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
575
741
  await storage.saveState(stateValue, generated.data, 300);
576
742
  } else {
577
743
  this.log("INFO", "State", "Using default state generation");
578
744
  stateValue = state || (0, import_crypto2.randomBytes)(16).toString("hex");
579
745
  codeVerifier = (0, import_crypto2.randomBytes)(32).toString("hex");
580
- const storage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
746
+ const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
581
747
  await storage.saveState(
582
748
  stateValue,
583
749
  {
@@ -586,12 +752,17 @@ var Lixa = class _Lixa {
586
752
  codeVerifier
587
753
  },
588
754
  300
589
- // 5 minutes in seconds
755
+ // 5 minutes in seconds (synchronized with cookie TTL)
590
756
  );
591
757
  }
592
758
  const codeChallenge = _Lixa.buildCodeChallenge(codeVerifier);
593
759
  this.log("INFO", "State", `Saved state for provider: ${providerType}`, { state: stateValue });
594
- const authNScopes = this.resolveAuthNScopes(providerType, providerConfig.scopes, providerImpl);
760
+ const authNScopes = this.resolveAuthNScopes(
761
+ providerType,
762
+ providerConfig.scopes,
763
+ providerImpl,
764
+ providerConfig.allowNonAuthScopes
765
+ );
595
766
  const params = new URLSearchParams({
596
767
  client_id: providerConfig.clientId,
597
768
  redirect_uri: providerConfig.redirectUri,
@@ -610,9 +781,12 @@ var Lixa = class _Lixa {
610
781
  return authUrl;
611
782
  }
612
783
  /**
613
- * Restricts primary authentication scopes strictly to AuthN identity scopes.
784
+ * Restricts primary authentication scopes strictly to AuthN identity scopes unless allowNonAuthScopes is true.
614
785
  */
615
- resolveAuthNScopes(providerType, configuredScopes, providerImpl) {
786
+ resolveAuthNScopes(providerType, configuredScopes, providerImpl, allowNonAuthScopes) {
787
+ if (allowNonAuthScopes) {
788
+ return configuredScopes && configuredScopes.length > 0 ? configuredScopes : providerImpl.authScopes || ["openid", "email", "profile"];
789
+ }
616
790
  const defaultAuthScopes = {
617
791
  google: ["openid", "email", "profile"],
618
792
  github: ["read:user", "user:email"],
@@ -641,7 +815,7 @@ var Lixa = class _Lixa {
641
815
  "Auth",
642
816
  `Primary authentication is strictly limited to AuthN scopes. Excluded non-AuthN resource scopes: [${nonAuthNScopes.join(
643
817
  ", "
644
- )}]. Use lixa.getResourceAuthUrl() post-login to connect resource providers.`
818
+ )}]. Set 'allowNonAuthScopes: true' on provider config to include them, or use lixa.getResourceAuthUrl() post-login to connect resource providers.`
645
819
  );
646
820
  }
647
821
  if (validAuthNScopes.length > 0) {
@@ -677,18 +851,18 @@ var Lixa = class _Lixa {
677
851
  this.log("INFO", "Auth", `Handling OAuth callback for provider: ${providerType}`);
678
852
  if (!code || code.trim() === "") {
679
853
  this.log("ERROR", "Auth", "Invalid or missing authorization code in callback");
680
- throw new Error("Invalid or missing code in callback");
854
+ throw new InvalidOAuthCallbackError("Invalid or missing code in callback");
681
855
  }
682
856
  if (!state || state.trim() === "") {
683
857
  this.log("ERROR", "Auth", "Invalid or missing state in callback");
684
- throw new Error("Invalid or missing state in callback");
858
+ throw new InvalidOAuthCallbackError("Invalid or missing state in callback");
685
859
  }
686
860
  this.log("INFO", "State", "Validating state parameter", { state });
687
- const stateStorage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
861
+ const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
688
862
  const cachedState = await stateStorage.getState(state);
689
863
  if (!cachedState) {
690
864
  this.log("ERROR", "State", "State validation failed: state not found or expired", { state });
691
- throw new Error("Invalid or expired state");
865
+ throw new InvalidStateError("Invalid or expired state", { state });
692
866
  }
693
867
  this.log("INFO", "State", "State validated successfully, removing from cache");
694
868
  await stateStorage.deleteState(state);
@@ -696,7 +870,9 @@ var Lixa = class _Lixa {
696
870
  const providerConfig = this.findProviderByType(providerType);
697
871
  if (!providerConfig) {
698
872
  this.log("ERROR", "Auth", `Provider '${String(provider)}' is not configured`);
699
- throw new Error(`Provider '${String(provider)}' is not configured in this Lixa instance`);
873
+ throw new ProviderNotConfiguredError(String(provider), {
874
+ message: `Provider '${String(provider)}' is not configured in this Lixa instance`
875
+ });
700
876
  }
701
877
  const providerImpl = this.getProvider(providerType, providerConfig);
702
878
  this.log("INFO", "Token", `Exchanging authorization code for tokens`, { provider: providerType });
@@ -722,9 +898,12 @@ var Lixa = class _Lixa {
722
898
  extractedUserInfo = userInfo;
723
899
  } catch {
724
900
  }
725
- const generateSession = this.sessionHandler.generateSession || _Lixa.LOCAL_SESSION_HANDLER.GenerateSession.bind(_Lixa.LOCAL_SESSION_HANDLER);
726
- if (this.sessionHandler.generateSession) {
727
- this.log("INFO", "Session", "Calling custom GenerateSession");
901
+ const generateSession = this.sessionHandler.generateSession || this.sessionHandler.GenerateSession || (this.localSessionHandler.generateSession ? this.localSessionHandler.generateSession.bind(this.localSessionHandler) : void 0);
902
+ if (!generateSession) {
903
+ throw new Error("No session generation handler available");
904
+ }
905
+ if (this.sessionHandler.generateSession || this.sessionHandler.GenerateSession) {
906
+ this.log("INFO", "Session", "Calling custom generateSession");
728
907
  } else {
729
908
  this.log("INFO", "Session", "Using default session generation");
730
909
  }
@@ -733,7 +912,7 @@ var Lixa = class _Lixa {
733
912
  if (extractedUserInfo?.email && !session.email) {
734
913
  session.email = extractedUserInfo.email;
735
914
  }
736
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
915
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
737
916
  const linkingConfig = this.config.accountLinking;
738
917
  const mode = String(linkingConfig?.mode || "");
739
918
  const isLinkByEmail = mode === "AUTO_LINK_BY_VERIFIED_EMAIL" /* AUTO_LINK_BY_VERIFIED_EMAIL */ || mode === "AUTO_LINK_BY_VERIFIED_EMAIL" || mode === "linkByEmail";
@@ -786,27 +965,31 @@ var Lixa = class _Lixa {
786
965
  */
787
966
  async linkAccount(params) {
788
967
  const { sessionId, provider, code, state } = params;
789
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
968
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
790
969
  const existingSession = await sessionStorage.getSession(sessionId);
791
970
  if (!existingSession) {
792
- throw new Error("Invalid session ID. User must be authenticated to link an account.");
971
+ throw new SessionNotFoundError("Invalid session ID. User must be authenticated to link an account.", { sessionId });
972
+ }
973
+ if (!code || code.trim() === "") {
974
+ throw new InvalidOAuthCallbackError("Invalid or missing code in linkAccount");
793
975
  }
794
976
  const providerType = String(provider).toLowerCase();
795
977
  const providerConfig = this.findProviderByType(providerType);
796
978
  if (!providerConfig) {
797
- throw new Error(`Provider '${String(provider)}' is not configured`);
979
+ throw new ProviderNotConfiguredError(String(provider));
798
980
  }
799
981
  const providerImpl = this.getProvider(providerType, providerConfig);
800
- let codeVerifier = (0, import_crypto2.randomBytes)(32).toString("hex");
982
+ let codeVerifier;
801
983
  if (state) {
802
- const stateStorage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
984
+ const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
803
985
  const cachedState = await stateStorage.getState(state);
804
- if (cachedState) {
805
- codeVerifier = cachedState.codeVerifier;
806
- await stateStorage.deleteState(state);
986
+ if (!cachedState) {
987
+ throw new InvalidStateError("Invalid or expired state during account linking", { state });
807
988
  }
989
+ codeVerifier = cachedState.codeVerifier;
990
+ await stateStorage.deleteState(state);
808
991
  }
809
- const tokens = await this.exchangeCodeForToken(providerType, providerConfig, providerImpl, codeVerifier);
992
+ const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier || "");
810
993
  const providerMetadata = {
811
994
  name: providerType,
812
995
  endpoints: {
@@ -844,14 +1027,17 @@ var Lixa = class _Lixa {
844
1027
  * @returns Promise resolving to true on successful unlink
845
1028
  */
846
1029
  async unlinkAccount(sessionId, providerToUnlink) {
847
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1030
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
848
1031
  const session = await sessionStorage.getSession(sessionId);
849
1032
  if (!session || !session.accounts) {
850
- throw new Error("Session not found or has no linked accounts.");
1033
+ throw new AccountUnlinkError("Session not found or has no linked accounts.", { sessionId });
851
1034
  }
852
1035
  const linkedProviders = Object.keys(session.accounts);
853
1036
  if (linkedProviders.length <= 1) {
854
- throw new Error("Cannot unlink the only authentication provider for this account.");
1037
+ throw new AccountUnlinkError("Cannot unlink the only authentication provider for this account.", {
1038
+ sessionId,
1039
+ provider: providerToUnlink
1040
+ });
855
1041
  }
856
1042
  delete session.accounts[providerToUnlink.toLowerCase()];
857
1043
  await sessionStorage.saveSession(sessionId, session, 86400);
@@ -870,20 +1056,20 @@ var Lixa = class _Lixa {
870
1056
  */
871
1057
  async getResourceAuthUrl(params) {
872
1058
  const { sessionId, provider, scopes, state, prompt, extraConfig } = params;
873
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1059
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
874
1060
  const activeSession = await sessionStorage.getSession(sessionId);
875
1061
  if (!activeSession) {
876
- throw new Error("Authentication required. Active session must exist to connect resource providers.");
1062
+ throw new SessionNotFoundError("Authentication required. Active session must exist to connect resource providers.", { sessionId });
877
1063
  }
878
1064
  const providerType = String(provider).toLowerCase();
879
1065
  const providerConfig = this.findProviderByType(providerType);
880
1066
  if (!providerConfig) {
881
- throw new Error(`Provider '${String(provider)}' is not configured`);
1067
+ throw new ProviderNotConfiguredError(String(provider));
882
1068
  }
883
1069
  const providerImpl = this.getProvider(providerType, providerConfig);
884
1070
  const stateValue = state || (0, import_crypto2.randomBytes)(16).toString("hex");
885
1071
  const codeVerifier = (0, import_crypto2.randomBytes)(32).toString("hex");
886
- const storage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
1072
+ const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
887
1073
  await storage.saveState(
888
1074
  stateValue,
889
1075
  {
@@ -908,77 +1094,245 @@ var Lixa = class _Lixa {
908
1094
  });
909
1095
  return `${providerImpl.authorizationEndpoint}?${searchParams.toString()}`;
910
1096
  }
1097
+ getUserKeyFromSession(session) {
1098
+ return session.userId || session.email || session.id || "anonymous";
1099
+ }
911
1100
  /**
912
- * Handles the OAuth callback for a connected resource provider and stores resource tokens on the session.
913
- *
914
- * @param params - Object containing sessionId, provider, code, state, and requested scopes
915
- * @returns Updated Session containing stored resource tokens under session.resources[provider]
1101
+ * Handles the OAuth callback for a connected resource provider and stores resource tokens bound to user account.
916
1102
  */
917
1103
  async handleResourceCallback(params) {
918
1104
  const { sessionId, provider, code, state, scopes } = params;
919
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1105
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
920
1106
  const activeSession = await sessionStorage.getSession(sessionId);
921
1107
  if (!activeSession) {
922
- throw new Error("Authentication required. Active session not found for resource connection.");
1108
+ throw new SessionNotFoundError("Authentication required. Active session not found for resource connection.", { sessionId });
923
1109
  }
924
1110
  const providerType = String(provider).toLowerCase();
925
1111
  const providerConfig = this.findProviderByType(providerType);
926
1112
  if (!providerConfig) {
927
- throw new Error(`Provider '${String(provider)}' is not configured`);
1113
+ throw new ProviderNotConfiguredError(String(provider));
928
1114
  }
929
1115
  const providerImpl = this.getProvider(providerType, providerConfig);
930
1116
  let codeVerifier = (0, import_crypto2.randomBytes)(32).toString("hex");
931
1117
  if (state) {
932
- const stateStorage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
1118
+ const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
933
1119
  const cachedState = await stateStorage.getState(state);
934
- if (cachedState) {
935
- codeVerifier = cachedState.codeVerifier;
936
- await stateStorage.deleteState(state);
1120
+ if (!cachedState) {
1121
+ throw new InvalidStateError("Invalid or expired state during resource connection callback", { state });
937
1122
  }
1123
+ codeVerifier = cachedState.codeVerifier;
1124
+ await stateStorage.deleteState(state);
938
1125
  }
939
1126
  const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier);
940
- activeSession.resources = activeSession.resources || {};
941
- activeSession.resources[providerType] = {
1127
+ const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
1128
+ const connectedResource = {
942
1129
  provider: providerType,
943
1130
  accessToken: tokens.access_token,
944
1131
  refreshToken: tokens.refresh_token,
1132
+ expiresAt,
945
1133
  scopes: scopes || (tokens.scope ? tokens.scope.split(" ") : []),
946
1134
  raw: tokens,
947
1135
  connectedAt: Date.now()
948
1136
  };
1137
+ const userKey = this.getUserKeyFromSession(activeSession);
1138
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1139
+ await resourceStorage.saveResource(userKey, providerType, connectedResource);
1140
+ activeSession.resources = activeSession.resources || {};
1141
+ activeSession.resources[providerType] = connectedResource;
949
1142
  await sessionStorage.saveSession(sessionId, activeSession, 86400);
950
1143
  return activeSession;
951
1144
  }
952
1145
  /**
953
- * Retrieves a connected resource provider token for an active session.
1146
+ * Retrieves a connected resource for a specific User ID / Email directly (independent of session IDs).
1147
+ * Automatically refreshes expired access tokens if a refresh token is present.
1148
+ *
1149
+ * @param userIdOrEmail - User identifier or email
1150
+ * @param provider - Resource provider identifier (e.g. 'github', 'google')
1151
+ */
1152
+ async getUserResource(userIdOrEmail, provider) {
1153
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1154
+ const providerType = provider.toLowerCase();
1155
+ const resource = await resourceStorage.getResource(userIdOrEmail, providerType);
1156
+ if (!resource) return null;
1157
+ if (resource.refreshToken && resource.expiresAt && Date.now() >= resource.expiresAt - 6e4) {
1158
+ this.log("INFO", "Token", `Resource access token for user '${userIdOrEmail}' on '${providerType}' is expired. Auto-refreshing...`);
1159
+ try {
1160
+ return await this.refreshUserResourceToken(userIdOrEmail, providerType);
1161
+ } catch (error) {
1162
+ this.log("ERROR", "Token", `Failed to auto-refresh resource token for user '${userIdOrEmail}' on '${providerType}'`, { error: String(error) });
1163
+ }
1164
+ }
1165
+ return resource;
1166
+ }
1167
+ /**
1168
+ * Retrieves all connected resources for a specific User ID / Email.
1169
+ */
1170
+ async getUserResources(userIdOrEmail) {
1171
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1172
+ return await resourceStorage.getUserResources(userIdOrEmail);
1173
+ }
1174
+ /**
1175
+ * Retrieves a connected resource provider token for an active session, auto-refreshing expired tokens if possible.
954
1176
  *
955
1177
  * @param sessionId - Active session ID
956
- * @param provider - Provider identifier (e.g. 'github')
1178
+ * @param provider - Provider identifier (e.g. 'github', 'google')
957
1179
  */
958
1180
  async getConnectedResource(sessionId, provider) {
959
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1181
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
960
1182
  const activeSession = await sessionStorage.getSession(sessionId);
961
- if (!activeSession || !activeSession.resources) return null;
962
- return activeSession.resources[provider.toLowerCase()] || null;
1183
+ if (!activeSession) return null;
1184
+ const providerType = provider.toLowerCase();
1185
+ const userKey = this.getUserKeyFromSession(activeSession);
1186
+ let resource = await this.getUserResource(userKey, providerType);
1187
+ if (!resource && activeSession.resources) {
1188
+ resource = activeSession.resources[providerType] || null;
1189
+ }
1190
+ if (resource) {
1191
+ if (resource.refreshToken && resource.expiresAt && Date.now() >= resource.expiresAt - 6e4) {
1192
+ this.log("INFO", "Token", `Resource access token for user '${userKey}' on '${providerType}' is expired. Auto-refreshing...`);
1193
+ try {
1194
+ resource = await this.refreshResourceToken(sessionId, providerType);
1195
+ } catch (error) {
1196
+ this.log("ERROR", "Token", `Failed to auto-refresh resource token for user '${userKey}' on '${providerType}'`, { error: String(error) });
1197
+ }
1198
+ }
1199
+ activeSession.resources = activeSession.resources || {};
1200
+ activeSession.resources[providerType] = resource;
1201
+ }
1202
+ return resource;
963
1203
  }
964
1204
  /**
965
- * Disconnects a resource provider from an active session.
1205
+ * Refreshes a user's resource access token using its refresh token.
1206
+ * Deduplicates concurrent refresh requests via an in-flight promise mutex.
966
1207
  *
967
- * @param sessionId - Active session ID
968
- * @param provider - Provider identifier to disconnect
1208
+ * @param userIdOrEmail - User identifier or email
1209
+ * @param provider - Provider identifier (e.g. 'google', 'github')
969
1210
  */
970
- async disconnectResource(sessionId, provider) {
971
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1211
+ async refreshUserResourceToken(userIdOrEmail, provider, existingResource) {
1212
+ const mutexKey = `${userIdOrEmail.toLowerCase()}:${provider.toLowerCase()}`;
1213
+ const existingPromise = this.refreshMutexes.get(mutexKey);
1214
+ if (existingPromise) {
1215
+ this.log("INFO", "Token", `Concurrent refresh request detected for '${mutexKey}'. Reusing in-flight promise.`);
1216
+ return existingPromise;
1217
+ }
1218
+ const refreshPromise = this.executeRefreshUserResourceToken(userIdOrEmail, provider, existingResource).finally(() => {
1219
+ this.refreshMutexes.delete(mutexKey);
1220
+ });
1221
+ this.refreshMutexes.set(mutexKey, refreshPromise);
1222
+ return refreshPromise;
1223
+ }
1224
+ /**
1225
+ * Internal execution of refresh token exchange.
1226
+ */
1227
+ async executeRefreshUserResourceToken(userIdOrEmail, provider, existingResource) {
1228
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1229
+ const providerType = provider.toLowerCase();
1230
+ let resource = existingResource || await resourceStorage.getResource(userIdOrEmail, providerType);
1231
+ if (!resource || !resource.refreshToken) {
1232
+ throw new RefreshTokenError(
1233
+ `No refresh token available for user '${userIdOrEmail}' on connected resource '${provider}'`,
1234
+ { userId: userIdOrEmail, provider }
1235
+ );
1236
+ }
1237
+ const providerConfig = this.findProviderByType(providerType);
1238
+ if (!providerConfig) {
1239
+ throw new ProviderNotConfiguredError(provider);
1240
+ }
1241
+ const providerImpl = this.getProvider(providerType, providerConfig);
1242
+ const body = {
1243
+ client_id: providerConfig.clientId,
1244
+ client_secret: providerConfig.clientSecret,
1245
+ grant_type: "refresh_token",
1246
+ refresh_token: resource.refreshToken
1247
+ };
1248
+ const response = await fetch(providerImpl.tokenEndpoint, {
1249
+ method: "POST",
1250
+ headers: {
1251
+ "Content-Type": "application/x-www-form-urlencoded",
1252
+ Accept: "application/json"
1253
+ },
1254
+ body: new URLSearchParams(body).toString()
1255
+ });
1256
+ if (!response.ok) {
1257
+ const errorText = await response.text();
1258
+ throw new RefreshTokenError(
1259
+ `Failed to refresh resource token for user '${userIdOrEmail}' on '${provider}': ${response.status} - ${errorText}`,
1260
+ { userId: userIdOrEmail, provider, status: response.status }
1261
+ );
1262
+ }
1263
+ const tokens = await response.json();
1264
+ const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
1265
+ resource.accessToken = tokens.access_token;
1266
+ if (tokens.refresh_token) {
1267
+ resource.refreshToken = tokens.refresh_token;
1268
+ }
1269
+ if (expiresAt) {
1270
+ resource.expiresAt = expiresAt;
1271
+ }
1272
+ resource.raw = tokens;
1273
+ resource.connectedAt = Date.now();
1274
+ await resourceStorage.saveResource(userIdOrEmail, providerType, resource);
1275
+ return resource;
1276
+ }
1277
+ /**
1278
+ * Refreshes a connected resource access token for an active session.
1279
+ */
1280
+ async refreshResourceToken(sessionId, provider) {
1281
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
972
1282
  const activeSession = await sessionStorage.getSession(sessionId);
973
- if (!activeSession || !activeSession.resources) return false;
974
- delete activeSession.resources[provider.toLowerCase()];
1283
+ if (!activeSession) {
1284
+ throw new SessionNotFoundError("Active session not found.", { sessionId });
1285
+ }
1286
+ const providerType = provider.toLowerCase();
1287
+ const userKey = this.getUserKeyFromSession(activeSession);
1288
+ const existingResource = activeSession.resources ? activeSession.resources[providerType] : void 0;
1289
+ const refreshed = await this.refreshUserResourceToken(userKey, providerType, existingResource);
1290
+ activeSession.resources = activeSession.resources || {};
1291
+ activeSession.resources[providerType] = refreshed;
975
1292
  await sessionStorage.saveSession(sessionId, activeSession, 86400);
1293
+ return refreshed;
1294
+ }
1295
+ /**
1296
+ * Disconnects a resource provider for a specific User ID / Email.
1297
+ */
1298
+ async disconnectUserResource(userIdOrEmail, provider) {
1299
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1300
+ await resourceStorage.deleteResource(userIdOrEmail, provider.toLowerCase());
976
1301
  return true;
977
1302
  }
1303
+ /**
1304
+ * Disconnects a resource provider from an active session and user account.
1305
+ */
1306
+ async disconnectResource(sessionId, provider) {
1307
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1308
+ const activeSession = await sessionStorage.getSession(sessionId);
1309
+ if (activeSession) {
1310
+ const userKey = this.getUserKeyFromSession(activeSession);
1311
+ await this.disconnectUserResource(userKey, provider);
1312
+ if (activeSession.resources) {
1313
+ delete activeSession.resources[provider.toLowerCase()];
1314
+ await sessionStorage.saveSession(sessionId, activeSession, 86400);
1315
+ }
1316
+ return true;
1317
+ }
1318
+ return false;
1319
+ }
1320
+ /**
1321
+ * Retrieves active session details from session storage.
1322
+ */
978
1323
  async fetchSessionInfo(sessionId) {
979
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1324
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
980
1325
  return await sessionStorage.getSession(sessionId);
981
1326
  }
1327
+ /**
1328
+ * Deletes a session from session storage (e.g. on logout).
1329
+ *
1330
+ * @param sessionId - Active session identifier
1331
+ */
1332
+ async deleteSession(sessionId) {
1333
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1334
+ await sessionStorage.deleteSession(sessionId);
1335
+ }
982
1336
  async exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier) {
983
1337
  const body = {
984
1338
  client_id: providerConfig.clientId,
@@ -1009,8 +1363,10 @@ var Lixa = class _Lixa {
1009
1363
  statusText: response.statusText,
1010
1364
  error: errorBody
1011
1365
  });
1012
- throw new Error(
1013
- `Token exchange failed: ${response.status} ${response.statusText} - ${errorBody}`
1366
+ throw new TokenExchangeError(
1367
+ `Token exchange failed: ${response.status} ${response.statusText} - ${errorBody}`,
1368
+ response.status,
1369
+ { statusText: response.statusText, errorBody }
1014
1370
  );
1015
1371
  }
1016
1372
  this.log("INFO", "Token", "Token exchange response received successfully");
@@ -1025,13 +1381,152 @@ var Lixa = class _Lixa {
1025
1381
  return void 0;
1026
1382
  }
1027
1383
  };
1384
+
1385
+ // src/utils/cookies.ts
1386
+ var DEFAULT_SESSION_COOKIE_NAME = "lixa_session";
1387
+ var DEFAULT_STATE_COOKIE_NAME = "lixa_oauth_state";
1388
+ var DEFAULT_SESSION_MAX_AGE_SECONDS = 24 * 60 * 60;
1389
+ var DEFAULT_STATE_MAX_AGE_SECONDS = 5 * 60;
1390
+ function isProductionEnvironment() {
1391
+ return typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1392
+ }
1393
+ function serializeCookie(name, value, options) {
1394
+ const isProd = isProductionEnvironment();
1395
+ const path = options?.path ?? "/";
1396
+ const httpOnly = options?.httpOnly ?? true;
1397
+ const secure = options?.secure ?? isProd;
1398
+ const sameSite = options?.sameSite ?? "lax";
1399
+ const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
1400
+ if (path) {
1401
+ parts.push(`Path=${path}`);
1402
+ }
1403
+ if (typeof options?.maxAge === "number") {
1404
+ parts.push(`Max-Age=${Math.floor(options.maxAge)}`);
1405
+ const expires = new Date(Date.now() + options.maxAge * 1e3).toUTCString();
1406
+ parts.push(`Expires=${expires}`);
1407
+ }
1408
+ if (options?.domain) {
1409
+ parts.push(`Domain=${options.domain}`);
1410
+ }
1411
+ if (httpOnly) {
1412
+ parts.push("HttpOnly");
1413
+ }
1414
+ if (secure) {
1415
+ parts.push("Secure");
1416
+ }
1417
+ if (sameSite) {
1418
+ const capitalized = sameSite.charAt(0).toUpperCase() + sameSite.slice(1).toLowerCase();
1419
+ parts.push(`SameSite=${capitalized}`);
1420
+ }
1421
+ return parts.join("; ");
1422
+ }
1423
+ function createSessionCookie(sessionId, options) {
1424
+ const isProd = isProductionEnvironment();
1425
+ const resolvedOptions = {
1426
+ name: options?.name || DEFAULT_SESSION_COOKIE_NAME,
1427
+ path: options?.path ?? "/",
1428
+ maxAge: options?.maxAge ?? DEFAULT_SESSION_MAX_AGE_SECONDS,
1429
+ httpOnly: options?.httpOnly ?? true,
1430
+ secure: options?.secure ?? isProd,
1431
+ sameSite: options?.sameSite ?? "lax",
1432
+ domain: options?.domain
1433
+ };
1434
+ const name = resolvedOptions.name;
1435
+ const header = serializeCookie(name, sessionId, resolvedOptions);
1436
+ return {
1437
+ name,
1438
+ value: sessionId,
1439
+ options: resolvedOptions,
1440
+ header
1441
+ };
1442
+ }
1443
+ function clearSessionCookie(options) {
1444
+ const isProd = isProductionEnvironment();
1445
+ const resolvedOptions = {
1446
+ name: options?.name || DEFAULT_SESSION_COOKIE_NAME,
1447
+ path: options?.path ?? "/",
1448
+ maxAge: 0,
1449
+ httpOnly: options?.httpOnly ?? true,
1450
+ secure: options?.secure ?? isProd,
1451
+ sameSite: options?.sameSite ?? "lax",
1452
+ domain: options?.domain
1453
+ };
1454
+ const name = resolvedOptions.name;
1455
+ const header = serializeCookie(name, "", resolvedOptions);
1456
+ return {
1457
+ name,
1458
+ value: "",
1459
+ options: resolvedOptions,
1460
+ header
1461
+ };
1462
+ }
1463
+ function createStateCookie(state, options) {
1464
+ const isProd = isProductionEnvironment();
1465
+ const resolvedOptions = {
1466
+ name: options?.name || DEFAULT_STATE_COOKIE_NAME,
1467
+ path: options?.path ?? "/",
1468
+ maxAge: options?.maxAge ?? DEFAULT_STATE_MAX_AGE_SECONDS,
1469
+ httpOnly: options?.httpOnly ?? true,
1470
+ secure: options?.secure ?? isProd,
1471
+ sameSite: options?.sameSite ?? "lax",
1472
+ domain: options?.domain
1473
+ };
1474
+ const name = resolvedOptions.name;
1475
+ const header = serializeCookie(name, state, resolvedOptions);
1476
+ return {
1477
+ name,
1478
+ value: state,
1479
+ options: resolvedOptions,
1480
+ header
1481
+ };
1482
+ }
1483
+ function clearStateCookie(options) {
1484
+ const isProd = isProductionEnvironment();
1485
+ const resolvedOptions = {
1486
+ name: options?.name || DEFAULT_STATE_COOKIE_NAME,
1487
+ path: options?.path ?? "/",
1488
+ maxAge: 0,
1489
+ httpOnly: options?.httpOnly ?? true,
1490
+ secure: options?.secure ?? isProd,
1491
+ sameSite: options?.sameSite ?? "lax",
1492
+ domain: options?.domain
1493
+ };
1494
+ const name = resolvedOptions.name;
1495
+ const header = serializeCookie(name, "", resolvedOptions);
1496
+ return {
1497
+ name,
1498
+ value: "",
1499
+ options: resolvedOptions,
1500
+ header
1501
+ };
1502
+ }
1028
1503
  // Annotate the CommonJS export names for ESM import in node:
1029
1504
  0 && (module.exports = {
1030
1505
  AccountLinkingStrategy,
1506
+ AccountUnlinkError,
1507
+ DEFAULT_SESSION_COOKIE_NAME,
1508
+ DEFAULT_SESSION_MAX_AGE_SECONDS,
1509
+ DEFAULT_STATE_COOKIE_NAME,
1510
+ DEFAULT_STATE_MAX_AGE_SECONDS,
1511
+ EmailNotVerifiedError,
1512
+ InvalidOAuthCallbackError,
1513
+ InvalidProviderConfigError,
1514
+ InvalidStateError,
1031
1515
  Lixa,
1516
+ LixaError,
1517
+ ProviderNotConfiguredError,
1518
+ RefreshTokenError,
1519
+ SessionNotFoundError,
1520
+ TokenExchangeError,
1521
+ clearSessionCookie,
1522
+ clearStateCookie,
1523
+ createSessionCookie,
1524
+ createStateCookie,
1032
1525
  decodeIdToken,
1033
1526
  determineProviderFromIssuer,
1034
1527
  extractUserInfo,
1035
- fetchUserInfo
1528
+ fetchUserInfo,
1529
+ isProductionEnvironment,
1530
+ serializeCookie
1036
1531
  });
1037
1532
  //# sourceMappingURL=index.cjs.map