@syncello/auth 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -108,9 +108,10 @@ declare function createSession(db: DrizzleDB$2, tables: Pick<SessionTables, 'ses
108
108
  * @param tables - Session tables (sessions, users)
109
109
  * @param sessionId - Session ID from cookie
110
110
  * @param currentFingerprint - Current browser fingerprint (optional)
111
+ * @param sessionMaxLifetimeMs - Absolute session lifetime cap in milliseconds (optional)
111
112
  * @returns Session data or null if invalid
112
113
  */
113
- declare function validateSession(db: DrizzleDB$2, tables: SessionTables, sessionId: string, currentFingerprint?: string): Promise<SessionData | null>;
114
+ declare function validateSession(db: DrizzleDB$2, tables: SessionTables, sessionId: string, currentFingerprint?: string, sessionMaxLifetimeMs?: number): Promise<SessionData | null>;
114
115
  /**
115
116
  * Refresh session expiration (sliding window)
116
117
  * @param db - Drizzle database instance
@@ -161,6 +162,7 @@ declare function hashToken(token: string): Promise<string>;
161
162
  */
162
163
  declare const AUTH_DEFAULTS: {
163
164
  readonly SESSION_TTL_DAYS: 7;
165
+ readonly SESSION_MAX_LIFETIME_DAYS: 30;
164
166
  readonly LOCKOUT_DURATION_MINUTES: 30;
165
167
  readonly LOCKOUT_MAX_ATTEMPTS: 5;
166
168
  readonly PASSWORD_RESET_TTL_MINUTES: 15;
package/dist/index.d.ts CHANGED
@@ -108,9 +108,10 @@ declare function createSession(db: DrizzleDB$2, tables: Pick<SessionTables, 'ses
108
108
  * @param tables - Session tables (sessions, users)
109
109
  * @param sessionId - Session ID from cookie
110
110
  * @param currentFingerprint - Current browser fingerprint (optional)
111
+ * @param sessionMaxLifetimeMs - Absolute session lifetime cap in milliseconds (optional)
111
112
  * @returns Session data or null if invalid
112
113
  */
113
- declare function validateSession(db: DrizzleDB$2, tables: SessionTables, sessionId: string, currentFingerprint?: string): Promise<SessionData | null>;
114
+ declare function validateSession(db: DrizzleDB$2, tables: SessionTables, sessionId: string, currentFingerprint?: string, sessionMaxLifetimeMs?: number): Promise<SessionData | null>;
114
115
  /**
115
116
  * Refresh session expiration (sliding window)
116
117
  * @param db - Drizzle database instance
@@ -161,6 +162,7 @@ declare function hashToken(token: string): Promise<string>;
161
162
  */
162
163
  declare const AUTH_DEFAULTS: {
163
164
  readonly SESSION_TTL_DAYS: 7;
165
+ readonly SESSION_MAX_LIFETIME_DAYS: 30;
164
166
  readonly LOCKOUT_DURATION_MINUTES: 30;
165
167
  readonly LOCKOUT_MAX_ATTEMPTS: 5;
166
168
  readonly PASSWORD_RESET_TTL_MINUTES: 15;
package/dist/index.js CHANGED
@@ -252,6 +252,9 @@ async function hashToken(token) {
252
252
  // src/core/config.ts
253
253
  var AUTH_DEFAULTS = {
254
254
  SESSION_TTL_DAYS: 7,
255
+ // Absolute cap: a session dies this long after creation regardless of
256
+ // sliding-window activity (limits the value of a stolen session token).
257
+ SESSION_MAX_LIFETIME_DAYS: 30,
255
258
  LOCKOUT_DURATION_MINUTES: 30,
256
259
  LOCKOUT_MAX_ATTEMPTS: 5,
257
260
  PASSWORD_RESET_TTL_MINUTES: 15,
@@ -284,7 +287,7 @@ async function createSession(db, tables2, userId, fingerprint, ipAddress, sessio
284
287
  });
285
288
  return sessionId;
286
289
  }
287
- async function validateSession(db, tables2, sessionId, currentFingerprint) {
290
+ async function validateSession(db, tables2, sessionId, currentFingerprint, sessionMaxLifetimeMs = AUTH_DEFAULTS.SESSION_MAX_LIFETIME_DAYS * 24 * 60 * 60 * 1e3) {
288
291
  const { sessions, users } = tables2;
289
292
  logger_default.debug("Validating session", {
290
293
  sessionId: sessionId?.slice(0, 8),
@@ -311,6 +314,17 @@ async function validateSession(db, tables2, sessionId, currentFingerprint) {
311
314
  return null;
312
315
  }
313
316
  const row = result[0];
317
+ if (Date.now() - row.sessionCreatedAt > sessionMaxLifetimeMs) {
318
+ logger_default.info("Session exceeded absolute lifetime - revoked", {
319
+ type: "security",
320
+ event: "session_max_lifetime_exceeded",
321
+ severity: "low",
322
+ sessionId: sessionId.slice(0, 8),
323
+ userId: row.userId
324
+ });
325
+ await deleteSession(db, { sessions }, sessionId);
326
+ return null;
327
+ }
314
328
  if (currentFingerprint && row.sessionFingerprint && row.sessionFingerprint !== currentFingerprint) {
315
329
  logger_default.info("Session fingerprint mismatch - likely SSR request", {
316
330
  type: "security",
@@ -589,8 +603,7 @@ function getSessionCookieName(env) {
589
603
  return COOKIE_NAMES[environment] || COOKIE_NAMES.development;
590
604
  }
591
605
  function isProduction(env) {
592
- const environment = env?.ENVIRONMENT;
593
- return environment === "production" || environment === "staging";
606
+ return env?.ENVIRONMENT !== "development";
594
607
  }
595
608
  function getCookieSecurityFlags(env) {
596
609
  if (isProduction(env)) {
@@ -2235,6 +2248,7 @@ var CloudflareEmailAdapter = class {
2235
2248
  logger_default.error("Cloudflare send error", {
2236
2249
  provider: this.providerName,
2237
2250
  error: errorText,
2251
+ errorName: error instanceof Error ? error.name : typeof error,
2238
2252
  to: options.to,
2239
2253
  subject: options.subject
2240
2254
  });