@vunexa/lixa 0.1.6-alpha.7 → 0.1.6-alpha.9

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,36 @@ 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
+ clearSessionCookieOnResponse: () => clearSessionCookieOnResponse,
51
+ clearStateCookie: () => clearStateCookie,
52
+ clearStateCookieOnResponse: () => clearStateCookieOnResponse,
53
+ createRequireAuthMiddleware: () => createRequireAuthMiddleware,
54
+ createSessionCookie: () => createSessionCookie,
55
+ createStateCookie: () => createStateCookie,
35
56
  decodeIdToken: () => decodeIdToken,
36
57
  determineProviderFromIssuer: () => determineProviderFromIssuer,
37
58
  extractUserInfo: () => extractUserInfo,
38
- fetchUserInfo: () => fetchUserInfo
59
+ fetchUserInfo: () => fetchUserInfo,
60
+ isProductionEnvironment: () => isProductionEnvironment,
61
+ serializeCookie: () => serializeCookie,
62
+ setSessionCookieOnResponse: () => setSessionCookieOnResponse,
63
+ setStateCookieOnResponse: () => setStateCookieOnResponse
39
64
  });
40
65
  module.exports = __toCommonJS(src_exports);
41
66
 
@@ -69,7 +94,7 @@ var LocalStateHandler = class {
69
94
  }
70
95
  };
71
96
  }
72
- // Default GenerateState implementation
97
+ // Default generateState implementation
73
98
  async generateState(provider) {
74
99
  const state = (0, import_crypto.randomBytes)(16).toString("hex");
75
100
  const codeVerifier = (0, import_crypto.randomBytes)(32).toString("hex");
@@ -82,6 +107,10 @@ var LocalStateHandler = class {
82
107
  }
83
108
  };
84
109
  }
110
+ // PascalCase alias for backward compatibility
111
+ async GenerateState(provider) {
112
+ return this.generateState(provider);
113
+ }
85
114
  };
86
115
 
87
116
  // src/lixa.ts
@@ -94,7 +123,20 @@ var LocalSessionHandler = class {
94
123
  emailToSessionMap = /* @__PURE__ */ new Map();
95
124
  sessionStorage;
96
125
  constructor(defaultTtlSeconds = 600) {
97
- this.cache = new import_node_cache2.default({ stdTTL: defaultTtlSeconds });
126
+ this.cache = new import_node_cache2.default({ stdTTL: defaultTtlSeconds, checkperiod: 60 });
127
+ this.cache.on("expired", (_key, value) => {
128
+ if (value && typeof value === "object" && value.email) {
129
+ this.emailToSessionMap.delete(String(value.email).toLowerCase());
130
+ }
131
+ });
132
+ this.cache.on("del", (_key, value) => {
133
+ if (value && typeof value === "object" && value.email) {
134
+ this.emailToSessionMap.delete(String(value.email).toLowerCase());
135
+ }
136
+ });
137
+ this.cache.on("flush", () => {
138
+ this.emailToSessionMap.clear();
139
+ });
98
140
  this.sessionStorage = {
99
141
  saveSession: async (sessionId, session, expiresInSeconds) => {
100
142
  this.cache.set(sessionId, session, expiresInSeconds);
@@ -125,8 +167,8 @@ var LocalSessionHandler = class {
125
167
  }
126
168
  };
127
169
  }
128
- // Default GenerateSession implementation
129
- async GenerateSession(tokenData, providerMetadata) {
170
+ // Default generateSession implementation
171
+ async generateSession(tokenData, _providerMetadata) {
130
172
  if (!tokenData.access_token || typeof tokenData.access_token !== "string") {
131
173
  throw new Error("No valid access token found in OAuth response");
132
174
  }
@@ -136,6 +178,10 @@ var LocalSessionHandler = class {
136
178
  };
137
179
  return session;
138
180
  }
181
+ // PascalCase alias for backward compatibility
182
+ async GenerateSession(tokenData, providerMetadata) {
183
+ return this.generateSession(tokenData, providerMetadata);
184
+ }
139
185
  };
140
186
 
141
187
  // src/utils/user-info.ts
@@ -207,13 +253,91 @@ async function extractUserInfo(tokenData, providerMetadata) {
207
253
  return { userInfo };
208
254
  }
209
255
 
256
+ // src/errors.ts
257
+ var LixaError = class extends Error {
258
+ /**
259
+ * Standard error code string.
260
+ */
261
+ code;
262
+ /**
263
+ * Additional error context data.
264
+ */
265
+ details;
266
+ constructor(message, code = "LIXA_ERROR", details) {
267
+ super(message);
268
+ this.name = this.constructor.name;
269
+ this.code = code;
270
+ this.details = details;
271
+ Object.setPrototypeOf(this, new.target.prototype);
272
+ }
273
+ };
274
+ var InvalidStateError = class extends LixaError {
275
+ constructor(message = "Invalid or expired state", details) {
276
+ super(message, "INVALID_STATE", details);
277
+ }
278
+ };
279
+ var ProviderNotConfiguredError = class extends LixaError {
280
+ constructor(provider, details) {
281
+ 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: '...', ... }`;
282
+ super(message, "PROVIDER_NOT_CONFIGURED", {
283
+ provider,
284
+ ...details
285
+ });
286
+ }
287
+ };
288
+ var InvalidProviderConfigError = class extends LixaError {
289
+ constructor(message, details) {
290
+ super(message, "INVALID_PROVIDER_CONFIG", details);
291
+ }
292
+ };
293
+ var InvalidOAuthCallbackError = class extends LixaError {
294
+ constructor(message, details) {
295
+ super(message, "INVALID_OAUTH_CALLBACK", details);
296
+ }
297
+ };
298
+ var TokenExchangeError = class extends LixaError {
299
+ status;
300
+ constructor(message, status, details) {
301
+ super(message, "TOKEN_EXCHANGE_FAILED", { status, ...details });
302
+ this.status = status;
303
+ }
304
+ };
305
+ var SessionNotFoundError = class extends LixaError {
306
+ constructor(message = "Active session not found or has expired", details) {
307
+ super(message, "SESSION_NOT_FOUND", details);
308
+ }
309
+ };
310
+ var EmailNotVerifiedError = class extends LixaError {
311
+ constructor(email, details) {
312
+ super(
313
+ `Cannot link account: email '${email || "unknown"}' is not verified by the identity provider`,
314
+ "EMAIL_NOT_VERIFIED",
315
+ { email, ...details }
316
+ );
317
+ }
318
+ };
319
+ var AccountUnlinkError = class extends LixaError {
320
+ constructor(message, details) {
321
+ super(message, "ACCOUNT_UNLINK_ERROR", details);
322
+ }
323
+ };
324
+ var RefreshTokenError = class extends LixaError {
325
+ constructor(message, details) {
326
+ super(message, "REFRESH_TOKEN_ERROR", details);
327
+ }
328
+ };
329
+
210
330
  // src/lixa.ts
211
331
  var Lixa = class _Lixa {
212
332
  static DEFAULT_PROVIDERS = /* @__PURE__ */ new Map();
213
333
  static CONFIGURED_PROVIDERS = /* @__PURE__ */ new Map();
214
334
  // Legacy registry for backward compatibility
215
- static LOCAL_STATE_HANDLER = new LocalStateHandler();
216
- static LOCAL_SESSION_HANDLER = new LocalSessionHandler();
335
+ // Instance-scoped fallback handlers to ensure no cross-instance state pollution
336
+ localStateHandler;
337
+ localSessionHandler;
338
+ localResourceHandler;
339
+ userResourceStore = /* @__PURE__ */ new Map();
340
+ refreshMutexes = /* @__PURE__ */ new Map();
217
341
  config;
218
342
  stateHandler;
219
343
  sessionHandler;
@@ -234,10 +358,44 @@ var Lixa = class _Lixa {
234
358
  */
235
359
  constructor(config) {
236
360
  this.config = config;
237
- this.stateHandler = config.stateHandler || _Lixa.LOCAL_STATE_HANDLER;
238
- this.sessionHandler = config.sessionHandler || _Lixa.LOCAL_SESSION_HANDLER;
239
- this.resourceHandler = config.resourceHandler || _Lixa.LOCAL_RESOURCE_HANDLER;
240
361
  this.debug = config.debug || false;
362
+ this.localStateHandler = new LocalStateHandler();
363
+ this.localSessionHandler = new LocalSessionHandler();
364
+ this.localResourceHandler = {
365
+ resourceStorage: {
366
+ saveResource: async (userId, provider, resource) => {
367
+ let userMap = this.userResourceStore.get(userId);
368
+ if (!userMap) {
369
+ userMap = /* @__PURE__ */ new Map();
370
+ this.userResourceStore.set(userId, userMap);
371
+ }
372
+ userMap.set(provider.toLowerCase(), resource);
373
+ },
374
+ getResource: async (userId, provider) => {
375
+ const userMap = this.userResourceStore.get(userId);
376
+ return userMap?.get(provider.toLowerCase()) || null;
377
+ },
378
+ getUserResources: async (userId) => {
379
+ const userMap = this.userResourceStore.get(userId);
380
+ const result = {};
381
+ if (userMap) {
382
+ for (const [p, r] of userMap.entries()) {
383
+ result[p] = r;
384
+ }
385
+ }
386
+ return result;
387
+ },
388
+ deleteResource: async (userId, provider) => {
389
+ const userMap = this.userResourceStore.get(userId);
390
+ if (userMap) {
391
+ userMap.delete(provider.toLowerCase());
392
+ }
393
+ }
394
+ }
395
+ };
396
+ this.stateHandler = config.stateHandler || this.localStateHandler;
397
+ this.sessionHandler = config.sessionHandler || this.localSessionHandler;
398
+ this.resourceHandler = config.resourceHandler || this.localResourceHandler;
241
399
  this.log("INFO", "Init", "Initializing Lixa instance", {
242
400
  providers: Object.keys(config.providers),
243
401
  debug: this.debug
@@ -252,8 +410,9 @@ var Lixa = class _Lixa {
252
410
  } else {
253
411
  if (!_Lixa.DEFAULT_PROVIDERS.has(name) && !_Lixa.CONFIGURED_PROVIDERS.has(name)) {
254
412
  this.log("ERROR", "Init", `Provider '${providerName}' not available`);
255
- throw new Error(
256
- `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: '...', ... }`
413
+ throw new ProviderNotConfiguredError(
414
+ providerName,
415
+ { hint: `Ensure the provider is registered via '@vunexa/lixa-providers' or provided inline.` }
257
416
  );
258
417
  }
259
418
  this.log("INFO", "Init", `Using registered provider: ${providerName}`);
@@ -266,7 +425,7 @@ var Lixa = class _Lixa {
266
425
  *
267
426
  * @param name - The provider name
268
427
  * @param config - The provider configuration
269
- * @throws Error when required fields are missing or invalid
428
+ * @throws InvalidProviderConfigError when required fields are missing or invalid
270
429
  */
271
430
  validateProviderConfig(name, config) {
272
431
  const requiredFields = ["clientId", "clientSecret", "redirectUri", "scopes"];
@@ -275,18 +434,21 @@ var Lixa = class _Lixa {
275
434
  return value === void 0 || value === null || typeof value === "string" && value.trim() === "";
276
435
  });
277
436
  if (missingFields.length > 0) {
278
- throw new Error(
279
- `Provider '${name}' configuration is missing required fields: ${missingFields.join(", ")}`
437
+ throw new InvalidProviderConfigError(
438
+ `Provider '${name}' configuration is missing required fields: ${missingFields.join(", ")}`,
439
+ { provider: name, missingFields }
280
440
  );
281
441
  }
282
442
  if (!Array.isArray(config.scopes)) {
283
- throw new Error(
284
- `Provider '${name}' configuration error: 'scopes' must be an array of strings`
443
+ throw new InvalidProviderConfigError(
444
+ `Provider '${name}' configuration error: 'scopes' must be an array of strings`,
445
+ { provider: name }
285
446
  );
286
447
  }
287
448
  if (config.scopes.length === 0) {
288
- throw new Error(
289
- `Provider '${name}' configuration error: 'scopes' array cannot be empty`
449
+ throw new InvalidProviderConfigError(
450
+ `Provider '${name}' configuration error: 'scopes' array cannot be empty`,
451
+ { provider: name }
290
452
  );
291
453
  }
292
454
  }
@@ -295,7 +457,7 @@ var Lixa = class _Lixa {
295
457
  *
296
458
  * @param name - The provider name
297
459
  * @param provider - The provider implementation
298
- * @throws Error when required properties are missing
460
+ * @throws InvalidProviderConfigError when required properties are missing
299
461
  */
300
462
  validateProviderImplementation(name, provider) {
301
463
  const requiredProps = ["authorizationEndpoint", "tokenEndpoint", "userInfoEndpoint"];
@@ -304,24 +466,27 @@ var Lixa = class _Lixa {
304
466
  return !value || typeof value !== "string" || value.trim() === "";
305
467
  });
306
468
  if (missingProps.length > 0) {
307
- throw new Error(
308
- `Provider '${name}' implementation is missing required properties: ${missingProps.join(", ")}. All IProvider implementations must define: authorizationEndpoint, tokenEndpoint, and userInfoEndpoint.`
469
+ throw new InvalidProviderConfigError(
470
+ `Provider '${name}' implementation is missing required properties: ${missingProps.join(", ")}. All IProvider implementations must define: authorizationEndpoint, tokenEndpoint, and userInfoEndpoint.`,
471
+ { provider: name, missingProps }
309
472
  );
310
473
  }
311
474
  }
312
475
  /**
313
- * Structured debug logging with standardized format.
476
+ * Structured logging with standardized format and custom logger support.
314
477
  *
315
- * @param level - Log level (INFO, WARN, ERROR)
316
- * @param context - Context of the log (Init, Auth, Token, Session, State)
478
+ * @param level - Log level (INFO, WARN, ERROR, DEBUG)
479
+ * @param context - Context of the log (Init, Auth, Token, Session, State, AccountLinking, Resource)
317
480
  * @param message - Log message
318
481
  * @param data - Optional data to log
319
- *
320
- * @remarks
321
- * Format: [Lixa] [timestamp] [level] [context] message
322
- * Only logs when debug mode is enabled.
323
482
  */
324
483
  log(level, context, message, data) {
484
+ if (this.config.logger) {
485
+ try {
486
+ this.config.logger.log(level, context, message, data);
487
+ } catch {
488
+ }
489
+ }
325
490
  if (!this.debug) return;
326
491
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
327
492
  const prefix = `[Lixa] [${timestamp}] [${level}] [${context}]`;
@@ -372,8 +537,9 @@ var Lixa = class _Lixa {
372
537
  if (legacyProvider) {
373
538
  return legacyProvider;
374
539
  }
375
- throw new Error(
376
- `Provider '${name}' not found. Ensure the provider is included in the configuration with a 'provider' field, or registered using Lixa.registerProvider().`
540
+ throw new ProviderNotConfiguredError(
541
+ name,
542
+ { hint: `Ensure the provider is included in the configuration with a 'provider' field, or registered using Lixa.registerProvider().` }
377
543
  );
378
544
  }
379
545
  /**
@@ -563,23 +729,26 @@ var Lixa = class _Lixa {
563
729
  const providerConfig = this.findProviderByType(providerType);
564
730
  if (!providerConfig) {
565
731
  this.log("ERROR", "Auth", `Provider '${String(provider)}' is not configured`);
566
- throw new Error(`Provider '${String(provider)}' is not configured in this Lixa instance`);
732
+ throw new ProviderNotConfiguredError(String(provider), {
733
+ message: `Provider '${String(provider)}' is not configured in this Lixa instance`
734
+ });
567
735
  }
568
736
  const providerImpl = this.getProvider(providerType, providerConfig);
569
737
  let stateValue;
570
738
  let codeVerifier;
571
- if (this.stateHandler.generateState) {
572
- this.log("INFO", "State", "Calling custom GenerateState");
573
- const generated = await this.stateHandler.generateState(providerType);
739
+ const generateStateFn = this.stateHandler.generateState || this.stateHandler.GenerateState;
740
+ if (generateStateFn) {
741
+ this.log("INFO", "State", "Calling custom generateState");
742
+ const generated = await generateStateFn(providerType);
574
743
  stateValue = state || generated.state;
575
744
  codeVerifier = generated.data.codeVerifier;
576
- const storage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
745
+ const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
577
746
  await storage.saveState(stateValue, generated.data, 300);
578
747
  } else {
579
748
  this.log("INFO", "State", "Using default state generation");
580
749
  stateValue = state || (0, import_crypto2.randomBytes)(16).toString("hex");
581
750
  codeVerifier = (0, import_crypto2.randomBytes)(32).toString("hex");
582
- const storage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
751
+ const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
583
752
  await storage.saveState(
584
753
  stateValue,
585
754
  {
@@ -588,12 +757,17 @@ var Lixa = class _Lixa {
588
757
  codeVerifier
589
758
  },
590
759
  300
591
- // 5 minutes in seconds
760
+ // 5 minutes in seconds (synchronized with cookie TTL)
592
761
  );
593
762
  }
594
763
  const codeChallenge = _Lixa.buildCodeChallenge(codeVerifier);
595
764
  this.log("INFO", "State", `Saved state for provider: ${providerType}`, { state: stateValue });
596
- const authNScopes = this.resolveAuthNScopes(providerType, providerConfig.scopes, providerImpl);
765
+ const authNScopes = this.resolveAuthNScopes(
766
+ providerType,
767
+ providerConfig.scopes,
768
+ providerImpl,
769
+ providerConfig.allowNonAuthScopes
770
+ );
597
771
  const params = new URLSearchParams({
598
772
  client_id: providerConfig.clientId,
599
773
  redirect_uri: providerConfig.redirectUri,
@@ -612,9 +786,12 @@ var Lixa = class _Lixa {
612
786
  return authUrl;
613
787
  }
614
788
  /**
615
- * Restricts primary authentication scopes strictly to AuthN identity scopes.
789
+ * Restricts primary authentication scopes strictly to AuthN identity scopes unless allowNonAuthScopes is true.
616
790
  */
617
- resolveAuthNScopes(providerType, configuredScopes, providerImpl) {
791
+ resolveAuthNScopes(providerType, configuredScopes, providerImpl, allowNonAuthScopes) {
792
+ if (allowNonAuthScopes) {
793
+ return configuredScopes && configuredScopes.length > 0 ? configuredScopes : providerImpl.authScopes || ["openid", "email", "profile"];
794
+ }
618
795
  const defaultAuthScopes = {
619
796
  google: ["openid", "email", "profile"],
620
797
  github: ["read:user", "user:email"],
@@ -643,7 +820,7 @@ var Lixa = class _Lixa {
643
820
  "Auth",
644
821
  `Primary authentication is strictly limited to AuthN scopes. Excluded non-AuthN resource scopes: [${nonAuthNScopes.join(
645
822
  ", "
646
- )}]. Use lixa.getResourceAuthUrl() post-login to connect resource providers.`
823
+ )}]. Set 'allowNonAuthScopes: true' on provider config to include them, or use lixa.getResourceAuthUrl() post-login to connect resource providers.`
647
824
  );
648
825
  }
649
826
  if (validAuthNScopes.length > 0) {
@@ -679,18 +856,18 @@ var Lixa = class _Lixa {
679
856
  this.log("INFO", "Auth", `Handling OAuth callback for provider: ${providerType}`);
680
857
  if (!code || code.trim() === "") {
681
858
  this.log("ERROR", "Auth", "Invalid or missing authorization code in callback");
682
- throw new Error("Invalid or missing code in callback");
859
+ throw new InvalidOAuthCallbackError("Invalid or missing code in callback");
683
860
  }
684
861
  if (!state || state.trim() === "") {
685
862
  this.log("ERROR", "Auth", "Invalid or missing state in callback");
686
- throw new Error("Invalid or missing state in callback");
863
+ throw new InvalidOAuthCallbackError("Invalid or missing state in callback");
687
864
  }
688
865
  this.log("INFO", "State", "Validating state parameter", { state });
689
- const stateStorage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
866
+ const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
690
867
  const cachedState = await stateStorage.getState(state);
691
868
  if (!cachedState) {
692
869
  this.log("ERROR", "State", "State validation failed: state not found or expired", { state });
693
- throw new Error("Invalid or expired state");
870
+ throw new InvalidStateError("Invalid or expired state", { state });
694
871
  }
695
872
  this.log("INFO", "State", "State validated successfully, removing from cache");
696
873
  await stateStorage.deleteState(state);
@@ -698,7 +875,9 @@ var Lixa = class _Lixa {
698
875
  const providerConfig = this.findProviderByType(providerType);
699
876
  if (!providerConfig) {
700
877
  this.log("ERROR", "Auth", `Provider '${String(provider)}' is not configured`);
701
- throw new Error(`Provider '${String(provider)}' is not configured in this Lixa instance`);
878
+ throw new ProviderNotConfiguredError(String(provider), {
879
+ message: `Provider '${String(provider)}' is not configured in this Lixa instance`
880
+ });
702
881
  }
703
882
  const providerImpl = this.getProvider(providerType, providerConfig);
704
883
  this.log("INFO", "Token", `Exchanging authorization code for tokens`, { provider: providerType });
@@ -724,9 +903,12 @@ var Lixa = class _Lixa {
724
903
  extractedUserInfo = userInfo;
725
904
  } catch {
726
905
  }
727
- const generateSession = this.sessionHandler.generateSession || _Lixa.LOCAL_SESSION_HANDLER.GenerateSession.bind(_Lixa.LOCAL_SESSION_HANDLER);
728
- if (this.sessionHandler.generateSession) {
729
- this.log("INFO", "Session", "Calling custom GenerateSession");
906
+ const generateSession = this.sessionHandler.generateSession || this.sessionHandler.GenerateSession || (this.localSessionHandler.generateSession ? this.localSessionHandler.generateSession.bind(this.localSessionHandler) : void 0);
907
+ if (!generateSession) {
908
+ throw new Error("No session generation handler available");
909
+ }
910
+ if (this.sessionHandler.generateSession || this.sessionHandler.GenerateSession) {
911
+ this.log("INFO", "Session", "Calling custom generateSession");
730
912
  } else {
731
913
  this.log("INFO", "Session", "Using default session generation");
732
914
  }
@@ -735,7 +917,7 @@ var Lixa = class _Lixa {
735
917
  if (extractedUserInfo?.email && !session.email) {
736
918
  session.email = extractedUserInfo.email;
737
919
  }
738
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
920
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
739
921
  const linkingConfig = this.config.accountLinking;
740
922
  const mode = String(linkingConfig?.mode || "");
741
923
  const isLinkByEmail = mode === "AUTO_LINK_BY_VERIFIED_EMAIL" /* AUTO_LINK_BY_VERIFIED_EMAIL */ || mode === "AUTO_LINK_BY_VERIFIED_EMAIL" || mode === "linkByEmail";
@@ -788,27 +970,31 @@ var Lixa = class _Lixa {
788
970
  */
789
971
  async linkAccount(params) {
790
972
  const { sessionId, provider, code, state } = params;
791
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
973
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
792
974
  const existingSession = await sessionStorage.getSession(sessionId);
793
975
  if (!existingSession) {
794
- throw new Error("Invalid session ID. User must be authenticated to link an account.");
976
+ throw new SessionNotFoundError("Invalid session ID. User must be authenticated to link an account.", { sessionId });
977
+ }
978
+ if (!code || code.trim() === "") {
979
+ throw new InvalidOAuthCallbackError("Invalid or missing code in linkAccount");
795
980
  }
796
981
  const providerType = String(provider).toLowerCase();
797
982
  const providerConfig = this.findProviderByType(providerType);
798
983
  if (!providerConfig) {
799
- throw new Error(`Provider '${String(provider)}' is not configured`);
984
+ throw new ProviderNotConfiguredError(String(provider));
800
985
  }
801
986
  const providerImpl = this.getProvider(providerType, providerConfig);
802
- let codeVerifier = (0, import_crypto2.randomBytes)(32).toString("hex");
987
+ let codeVerifier;
803
988
  if (state) {
804
- const stateStorage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
989
+ const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
805
990
  const cachedState = await stateStorage.getState(state);
806
- if (cachedState) {
807
- codeVerifier = cachedState.codeVerifier;
808
- await stateStorage.deleteState(state);
991
+ if (!cachedState) {
992
+ throw new InvalidStateError("Invalid or expired state during account linking", { state });
809
993
  }
994
+ codeVerifier = cachedState.codeVerifier;
995
+ await stateStorage.deleteState(state);
810
996
  }
811
- const tokens = await this.exchangeCodeForToken(providerType, providerConfig, providerImpl, codeVerifier);
997
+ const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier || "");
812
998
  const providerMetadata = {
813
999
  name: providerType,
814
1000
  endpoints: {
@@ -846,14 +1032,17 @@ var Lixa = class _Lixa {
846
1032
  * @returns Promise resolving to true on successful unlink
847
1033
  */
848
1034
  async unlinkAccount(sessionId, providerToUnlink) {
849
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1035
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
850
1036
  const session = await sessionStorage.getSession(sessionId);
851
1037
  if (!session || !session.accounts) {
852
- throw new Error("Session not found or has no linked accounts.");
1038
+ throw new AccountUnlinkError("Session not found or has no linked accounts.", { sessionId });
853
1039
  }
854
1040
  const linkedProviders = Object.keys(session.accounts);
855
1041
  if (linkedProviders.length <= 1) {
856
- throw new Error("Cannot unlink the only authentication provider for this account.");
1042
+ throw new AccountUnlinkError("Cannot unlink the only authentication provider for this account.", {
1043
+ sessionId,
1044
+ provider: providerToUnlink
1045
+ });
857
1046
  }
858
1047
  delete session.accounts[providerToUnlink.toLowerCase()];
859
1048
  await sessionStorage.saveSession(sessionId, session, 86400);
@@ -872,20 +1061,20 @@ var Lixa = class _Lixa {
872
1061
  */
873
1062
  async getResourceAuthUrl(params) {
874
1063
  const { sessionId, provider, scopes, state, prompt, extraConfig } = params;
875
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1064
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
876
1065
  const activeSession = await sessionStorage.getSession(sessionId);
877
1066
  if (!activeSession) {
878
- throw new Error("Authentication required. Active session must exist to connect resource providers.");
1067
+ throw new SessionNotFoundError("Authentication required. Active session must exist to connect resource providers.", { sessionId });
879
1068
  }
880
1069
  const providerType = String(provider).toLowerCase();
881
1070
  const providerConfig = this.findProviderByType(providerType);
882
1071
  if (!providerConfig) {
883
- throw new Error(`Provider '${String(provider)}' is not configured`);
1072
+ throw new ProviderNotConfiguredError(String(provider));
884
1073
  }
885
1074
  const providerImpl = this.getProvider(providerType, providerConfig);
886
1075
  const stateValue = state || (0, import_crypto2.randomBytes)(16).toString("hex");
887
1076
  const codeVerifier = (0, import_crypto2.randomBytes)(32).toString("hex");
888
- const storage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
1077
+ const storage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
889
1078
  await storage.saveState(
890
1079
  stateValue,
891
1080
  {
@@ -910,45 +1099,6 @@ var Lixa = class _Lixa {
910
1099
  });
911
1100
  return `${providerImpl.authorizationEndpoint}?${searchParams.toString()}`;
912
1101
  }
913
- /**
914
- * Handles the OAuth callback for a connected resource provider and stores resource tokens on the session.
915
- *
916
- * @param params - Object containing sessionId, provider, code, state, and requested scopes
917
- * @returns Updated Session containing stored resource tokens under session.resources[provider]
918
- */
919
- static userResourceStore = /* @__PURE__ */ new Map();
920
- static LOCAL_RESOURCE_HANDLER = {
921
- resourceStorage: {
922
- saveResource: async (userId, provider, resource) => {
923
- let userMap = _Lixa.userResourceStore.get(userId);
924
- if (!userMap) {
925
- userMap = /* @__PURE__ */ new Map();
926
- _Lixa.userResourceStore.set(userId, userMap);
927
- }
928
- userMap.set(provider.toLowerCase(), resource);
929
- },
930
- getResource: async (userId, provider) => {
931
- const userMap = _Lixa.userResourceStore.get(userId);
932
- return userMap?.get(provider.toLowerCase()) || null;
933
- },
934
- getUserResources: async (userId) => {
935
- const userMap = _Lixa.userResourceStore.get(userId);
936
- const result = {};
937
- if (userMap) {
938
- for (const [p, r] of userMap.entries()) {
939
- result[p] = r;
940
- }
941
- }
942
- return result;
943
- },
944
- deleteResource: async (userId, provider) => {
945
- const userMap = _Lixa.userResourceStore.get(userId);
946
- if (userMap) {
947
- userMap.delete(provider.toLowerCase());
948
- }
949
- }
950
- }
951
- };
952
1102
  getUserKeyFromSession(session) {
953
1103
  return session.userId || session.email || session.id || "anonymous";
954
1104
  }
@@ -957,25 +1107,26 @@ var Lixa = class _Lixa {
957
1107
  */
958
1108
  async handleResourceCallback(params) {
959
1109
  const { sessionId, provider, code, state, scopes } = params;
960
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1110
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
961
1111
  const activeSession = await sessionStorage.getSession(sessionId);
962
1112
  if (!activeSession) {
963
- throw new Error("Authentication required. Active session not found for resource connection.");
1113
+ throw new SessionNotFoundError("Authentication required. Active session not found for resource connection.", { sessionId });
964
1114
  }
965
1115
  const providerType = String(provider).toLowerCase();
966
1116
  const providerConfig = this.findProviderByType(providerType);
967
1117
  if (!providerConfig) {
968
- throw new Error(`Provider '${String(provider)}' is not configured`);
1118
+ throw new ProviderNotConfiguredError(String(provider));
969
1119
  }
970
1120
  const providerImpl = this.getProvider(providerType, providerConfig);
971
1121
  let codeVerifier = (0, import_crypto2.randomBytes)(32).toString("hex");
972
1122
  if (state) {
973
- const stateStorage = this.stateHandler.stateStorage || _Lixa.LOCAL_STATE_HANDLER.stateStorage;
1123
+ const stateStorage = this.stateHandler.stateStorage || this.localStateHandler.stateStorage;
974
1124
  const cachedState = await stateStorage.getState(state);
975
- if (cachedState) {
976
- codeVerifier = cachedState.codeVerifier;
977
- await stateStorage.deleteState(state);
1125
+ if (!cachedState) {
1126
+ throw new InvalidStateError("Invalid or expired state during resource connection callback", { state });
978
1127
  }
1128
+ codeVerifier = cachedState.codeVerifier;
1129
+ await stateStorage.deleteState(state);
979
1130
  }
980
1131
  const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier);
981
1132
  const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
@@ -989,7 +1140,7 @@ var Lixa = class _Lixa {
989
1140
  connectedAt: Date.now()
990
1141
  };
991
1142
  const userKey = this.getUserKeyFromSession(activeSession);
992
- const resourceStorage = this.resourceHandler.resourceStorage || _Lixa.LOCAL_RESOURCE_HANDLER.resourceStorage;
1143
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
993
1144
  await resourceStorage.saveResource(userKey, providerType, connectedResource);
994
1145
  activeSession.resources = activeSession.resources || {};
995
1146
  activeSession.resources[providerType] = connectedResource;
@@ -1004,7 +1155,7 @@ var Lixa = class _Lixa {
1004
1155
  * @param provider - Resource provider identifier (e.g. 'github', 'google')
1005
1156
  */
1006
1157
  async getUserResource(userIdOrEmail, provider) {
1007
- const resourceStorage = this.resourceHandler.resourceStorage || _Lixa.LOCAL_RESOURCE_HANDLER.resourceStorage;
1158
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1008
1159
  const providerType = provider.toLowerCase();
1009
1160
  const resource = await resourceStorage.getResource(userIdOrEmail, providerType);
1010
1161
  if (!resource) return null;
@@ -1022,7 +1173,7 @@ var Lixa = class _Lixa {
1022
1173
  * Retrieves all connected resources for a specific User ID / Email.
1023
1174
  */
1024
1175
  async getUserResources(userIdOrEmail) {
1025
- const resourceStorage = this.resourceHandler.resourceStorage || _Lixa.LOCAL_RESOURCE_HANDLER.resourceStorage;
1176
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1026
1177
  return await resourceStorage.getUserResources(userIdOrEmail);
1027
1178
  }
1028
1179
  /**
@@ -1032,7 +1183,7 @@ var Lixa = class _Lixa {
1032
1183
  * @param provider - Provider identifier (e.g. 'github', 'google')
1033
1184
  */
1034
1185
  async getConnectedResource(sessionId, provider) {
1035
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1186
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1036
1187
  const activeSession = await sessionStorage.getSession(sessionId);
1037
1188
  if (!activeSession) return null;
1038
1189
  const providerType = provider.toLowerCase();
@@ -1057,20 +1208,40 @@ var Lixa = class _Lixa {
1057
1208
  }
1058
1209
  /**
1059
1210
  * Refreshes a user's resource access token using its refresh token.
1211
+ * Deduplicates concurrent refresh requests via an in-flight promise mutex.
1060
1212
  *
1061
1213
  * @param userIdOrEmail - User identifier or email
1062
1214
  * @param provider - Provider identifier (e.g. 'google', 'github')
1063
1215
  */
1064
1216
  async refreshUserResourceToken(userIdOrEmail, provider, existingResource) {
1065
- const resourceStorage = this.resourceHandler.resourceStorage || _Lixa.LOCAL_RESOURCE_HANDLER.resourceStorage;
1217
+ const mutexKey = `${userIdOrEmail.toLowerCase()}:${provider.toLowerCase()}`;
1218
+ const existingPromise = this.refreshMutexes.get(mutexKey);
1219
+ if (existingPromise) {
1220
+ this.log("INFO", "Token", `Concurrent refresh request detected for '${mutexKey}'. Reusing in-flight promise.`);
1221
+ return existingPromise;
1222
+ }
1223
+ const refreshPromise = this.executeRefreshUserResourceToken(userIdOrEmail, provider, existingResource).finally(() => {
1224
+ this.refreshMutexes.delete(mutexKey);
1225
+ });
1226
+ this.refreshMutexes.set(mutexKey, refreshPromise);
1227
+ return refreshPromise;
1228
+ }
1229
+ /**
1230
+ * Internal execution of refresh token exchange.
1231
+ */
1232
+ async executeRefreshUserResourceToken(userIdOrEmail, provider, existingResource) {
1233
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1066
1234
  const providerType = provider.toLowerCase();
1067
1235
  let resource = existingResource || await resourceStorage.getResource(userIdOrEmail, providerType);
1068
1236
  if (!resource || !resource.refreshToken) {
1069
- throw new Error(`No refresh token available for user '${userIdOrEmail}' on connected resource '${provider}'`);
1237
+ throw new RefreshTokenError(
1238
+ `No refresh token available for user '${userIdOrEmail}' on connected resource '${provider}'`,
1239
+ { userId: userIdOrEmail, provider }
1240
+ );
1070
1241
  }
1071
1242
  const providerConfig = this.findProviderByType(providerType);
1072
1243
  if (!providerConfig) {
1073
- throw new Error(`Provider '${provider}' is not configured`);
1244
+ throw new ProviderNotConfiguredError(provider);
1074
1245
  }
1075
1246
  const providerImpl = this.getProvider(providerType, providerConfig);
1076
1247
  const body = {
@@ -1089,7 +1260,10 @@ var Lixa = class _Lixa {
1089
1260
  });
1090
1261
  if (!response.ok) {
1091
1262
  const errorText = await response.text();
1092
- throw new Error(`Failed to refresh resource token for user '${userIdOrEmail}' on '${provider}': ${response.status} - ${errorText}`);
1263
+ throw new RefreshTokenError(
1264
+ `Failed to refresh resource token for user '${userIdOrEmail}' on '${provider}': ${response.status} - ${errorText}`,
1265
+ { userId: userIdOrEmail, provider, status: response.status }
1266
+ );
1093
1267
  }
1094
1268
  const tokens = await response.json();
1095
1269
  const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
@@ -1109,10 +1283,10 @@ var Lixa = class _Lixa {
1109
1283
  * Refreshes a connected resource access token for an active session.
1110
1284
  */
1111
1285
  async refreshResourceToken(sessionId, provider) {
1112
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1286
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1113
1287
  const activeSession = await sessionStorage.getSession(sessionId);
1114
1288
  if (!activeSession) {
1115
- throw new Error("Active session not found.");
1289
+ throw new SessionNotFoundError("Active session not found.", { sessionId });
1116
1290
  }
1117
1291
  const providerType = provider.toLowerCase();
1118
1292
  const userKey = this.getUserKeyFromSession(activeSession);
@@ -1127,7 +1301,7 @@ var Lixa = class _Lixa {
1127
1301
  * Disconnects a resource provider for a specific User ID / Email.
1128
1302
  */
1129
1303
  async disconnectUserResource(userIdOrEmail, provider) {
1130
- const resourceStorage = this.resourceHandler.resourceStorage || _Lixa.LOCAL_RESOURCE_HANDLER.resourceStorage;
1304
+ const resourceStorage = this.resourceHandler.resourceStorage || this.localResourceHandler.resourceStorage;
1131
1305
  await resourceStorage.deleteResource(userIdOrEmail, provider.toLowerCase());
1132
1306
  return true;
1133
1307
  }
@@ -1135,7 +1309,7 @@ var Lixa = class _Lixa {
1135
1309
  * Disconnects a resource provider from an active session and user account.
1136
1310
  */
1137
1311
  async disconnectResource(sessionId, provider) {
1138
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1312
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1139
1313
  const activeSession = await sessionStorage.getSession(sessionId);
1140
1314
  if (activeSession) {
1141
1315
  const userKey = this.getUserKeyFromSession(activeSession);
@@ -1148,10 +1322,22 @@ var Lixa = class _Lixa {
1148
1322
  }
1149
1323
  return false;
1150
1324
  }
1325
+ /**
1326
+ * Retrieves active session details from session storage.
1327
+ */
1151
1328
  async fetchSessionInfo(sessionId) {
1152
- const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
1329
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1153
1330
  return await sessionStorage.getSession(sessionId);
1154
1331
  }
1332
+ /**
1333
+ * Deletes a session from session storage (e.g. on logout).
1334
+ *
1335
+ * @param sessionId - Active session identifier
1336
+ */
1337
+ async deleteSession(sessionId) {
1338
+ const sessionStorage = this.sessionHandler.sessionStorage || this.localSessionHandler.sessionStorage;
1339
+ await sessionStorage.deleteSession(sessionId);
1340
+ }
1155
1341
  async exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier) {
1156
1342
  const body = {
1157
1343
  client_id: providerConfig.clientId,
@@ -1182,8 +1368,10 @@ var Lixa = class _Lixa {
1182
1368
  statusText: response.statusText,
1183
1369
  error: errorBody
1184
1370
  });
1185
- throw new Error(
1186
- `Token exchange failed: ${response.status} ${response.statusText} - ${errorBody}`
1371
+ throw new TokenExchangeError(
1372
+ `Token exchange failed: ${response.status} ${response.statusText} - ${errorBody}`,
1373
+ response.status,
1374
+ { statusText: response.statusText, errorBody }
1187
1375
  );
1188
1376
  }
1189
1377
  this.log("INFO", "Token", "Token exchange response received successfully");
@@ -1198,13 +1386,205 @@ var Lixa = class _Lixa {
1198
1386
  return void 0;
1199
1387
  }
1200
1388
  };
1389
+
1390
+ // src/utils/cookies.ts
1391
+ var DEFAULT_SESSION_COOKIE_NAME = "lixa_session";
1392
+ var DEFAULT_STATE_COOKIE_NAME = "lixa_oauth_state";
1393
+ var DEFAULT_SESSION_MAX_AGE_SECONDS = 24 * 60 * 60;
1394
+ var DEFAULT_STATE_MAX_AGE_SECONDS = 5 * 60;
1395
+ function isProductionEnvironment() {
1396
+ return typeof process !== "undefined" && process.env?.NODE_ENV === "production";
1397
+ }
1398
+ function serializeCookie(name, value, options) {
1399
+ const isProd = isProductionEnvironment();
1400
+ const path = options?.path ?? "/";
1401
+ const httpOnly = options?.httpOnly ?? true;
1402
+ const secure = options?.secure ?? isProd;
1403
+ const sameSite = options?.sameSite ?? "lax";
1404
+ const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`];
1405
+ if (path) {
1406
+ parts.push(`Path=${path}`);
1407
+ }
1408
+ if (typeof options?.maxAge === "number") {
1409
+ parts.push(`Max-Age=${Math.floor(options.maxAge)}`);
1410
+ const expires = new Date(Date.now() + options.maxAge * 1e3).toUTCString();
1411
+ parts.push(`Expires=${expires}`);
1412
+ }
1413
+ if (options?.domain) {
1414
+ parts.push(`Domain=${options.domain}`);
1415
+ }
1416
+ if (httpOnly) {
1417
+ parts.push("HttpOnly");
1418
+ }
1419
+ if (secure) {
1420
+ parts.push("Secure");
1421
+ }
1422
+ if (sameSite) {
1423
+ const capitalized = sameSite.charAt(0).toUpperCase() + sameSite.slice(1).toLowerCase();
1424
+ parts.push(`SameSite=${capitalized}`);
1425
+ }
1426
+ return parts.join("; ");
1427
+ }
1428
+ function createSessionCookie(sessionId, options) {
1429
+ const isProd = isProductionEnvironment();
1430
+ const resolvedOptions = {
1431
+ name: options?.name || DEFAULT_SESSION_COOKIE_NAME,
1432
+ path: options?.path ?? "/",
1433
+ maxAge: options?.maxAge ?? DEFAULT_SESSION_MAX_AGE_SECONDS,
1434
+ httpOnly: options?.httpOnly ?? true,
1435
+ secure: options?.secure ?? isProd,
1436
+ sameSite: options?.sameSite ?? "lax",
1437
+ domain: options?.domain
1438
+ };
1439
+ const name = resolvedOptions.name;
1440
+ const header = serializeCookie(name, sessionId, resolvedOptions);
1441
+ return {
1442
+ name,
1443
+ value: sessionId,
1444
+ options: resolvedOptions,
1445
+ header
1446
+ };
1447
+ }
1448
+ function clearSessionCookie(options) {
1449
+ const isProd = isProductionEnvironment();
1450
+ const resolvedOptions = {
1451
+ name: options?.name || DEFAULT_SESSION_COOKIE_NAME,
1452
+ path: options?.path ?? "/",
1453
+ maxAge: 0,
1454
+ httpOnly: options?.httpOnly ?? true,
1455
+ secure: options?.secure ?? isProd,
1456
+ sameSite: options?.sameSite ?? "lax",
1457
+ domain: options?.domain
1458
+ };
1459
+ const name = resolvedOptions.name;
1460
+ const header = serializeCookie(name, "", resolvedOptions);
1461
+ return {
1462
+ name,
1463
+ value: "",
1464
+ options: resolvedOptions,
1465
+ header
1466
+ };
1467
+ }
1468
+ function createStateCookie(state, options) {
1469
+ const isProd = isProductionEnvironment();
1470
+ const resolvedOptions = {
1471
+ name: options?.name || DEFAULT_STATE_COOKIE_NAME,
1472
+ path: options?.path ?? "/",
1473
+ maxAge: options?.maxAge ?? DEFAULT_STATE_MAX_AGE_SECONDS,
1474
+ httpOnly: options?.httpOnly ?? true,
1475
+ secure: options?.secure ?? isProd,
1476
+ sameSite: options?.sameSite ?? "lax",
1477
+ domain: options?.domain
1478
+ };
1479
+ const name = resolvedOptions.name;
1480
+ const header = serializeCookie(name, state, resolvedOptions);
1481
+ return {
1482
+ name,
1483
+ value: state,
1484
+ options: resolvedOptions,
1485
+ header
1486
+ };
1487
+ }
1488
+ function clearStateCookie(options) {
1489
+ const isProd = isProductionEnvironment();
1490
+ const resolvedOptions = {
1491
+ name: options?.name || DEFAULT_STATE_COOKIE_NAME,
1492
+ path: options?.path ?? "/",
1493
+ maxAge: 0,
1494
+ httpOnly: options?.httpOnly ?? true,
1495
+ secure: options?.secure ?? isProd,
1496
+ sameSite: options?.sameSite ?? "lax",
1497
+ domain: options?.domain
1498
+ };
1499
+ const name = resolvedOptions.name;
1500
+ const header = serializeCookie(name, "", resolvedOptions);
1501
+ return {
1502
+ name,
1503
+ value: "",
1504
+ options: resolvedOptions,
1505
+ header
1506
+ };
1507
+ }
1508
+
1509
+ // src/express.ts
1510
+ function setSessionCookieOnResponse(res, sessionId, options) {
1511
+ const cookie = createSessionCookie(sessionId, options);
1512
+ res.cookie(cookie.name, cookie.value, cookie.options);
1513
+ }
1514
+ function clearSessionCookieOnResponse(res, options) {
1515
+ const cookie = clearSessionCookie(options);
1516
+ res.cookie(cookie.name, cookie.value, cookie.options);
1517
+ }
1518
+ function setStateCookieOnResponse(res, state, options) {
1519
+ const cookie = createStateCookie(state, options);
1520
+ res.cookie(cookie.name, cookie.value, cookie.options);
1521
+ }
1522
+ function clearStateCookieOnResponse(res, options) {
1523
+ const cookie = clearStateCookie(options);
1524
+ res.cookie(cookie.name, cookie.value, cookie.options);
1525
+ }
1526
+ function createRequireAuthMiddleware(lixa, options) {
1527
+ const cookieName = options?.sessionCookieName || DEFAULT_SESSION_COOKIE_NAME;
1528
+ return async (req, res, next) => {
1529
+ try {
1530
+ const sessionId = req.cookies?.[cookieName];
1531
+ if (!sessionId) {
1532
+ if (options?.unauthorizedHandler) {
1533
+ options.unauthorizedHandler(req, res);
1534
+ return;
1535
+ }
1536
+ res.status(401).json({ error: "Unauthorized: Missing session cookie" });
1537
+ return;
1538
+ }
1539
+ const session = await lixa.fetchSessionInfo(sessionId);
1540
+ if (!session) {
1541
+ if (options?.unauthorizedHandler) {
1542
+ options.unauthorizedHandler(req, res);
1543
+ return;
1544
+ }
1545
+ res.status(401).json({ error: "Unauthorized: Invalid or expired session" });
1546
+ return;
1547
+ }
1548
+ req.sessionInfo = session;
1549
+ req.sessionId = sessionId;
1550
+ next();
1551
+ } catch (error) {
1552
+ next(error);
1553
+ }
1554
+ };
1555
+ }
1201
1556
  // Annotate the CommonJS export names for ESM import in node:
1202
1557
  0 && (module.exports = {
1203
1558
  AccountLinkingStrategy,
1559
+ AccountUnlinkError,
1560
+ DEFAULT_SESSION_COOKIE_NAME,
1561
+ DEFAULT_SESSION_MAX_AGE_SECONDS,
1562
+ DEFAULT_STATE_COOKIE_NAME,
1563
+ DEFAULT_STATE_MAX_AGE_SECONDS,
1564
+ EmailNotVerifiedError,
1565
+ InvalidOAuthCallbackError,
1566
+ InvalidProviderConfigError,
1567
+ InvalidStateError,
1204
1568
  Lixa,
1569
+ LixaError,
1570
+ ProviderNotConfiguredError,
1571
+ RefreshTokenError,
1572
+ SessionNotFoundError,
1573
+ TokenExchangeError,
1574
+ clearSessionCookie,
1575
+ clearSessionCookieOnResponse,
1576
+ clearStateCookie,
1577
+ clearStateCookieOnResponse,
1578
+ createRequireAuthMiddleware,
1579
+ createSessionCookie,
1580
+ createStateCookie,
1205
1581
  decodeIdToken,
1206
1582
  determineProviderFromIssuer,
1207
1583
  extractUserInfo,
1208
- fetchUserInfo
1584
+ fetchUserInfo,
1585
+ isProductionEnvironment,
1586
+ serializeCookie,
1587
+ setSessionCookieOnResponse,
1588
+ setStateCookieOnResponse
1209
1589
  });
1210
1590
  //# sourceMappingURL=index.cjs.map