@strapi/core 0.0.0-experimental.00b482b8dcda6164537baf70d52b4b2535560c36 → 0.0.0-experimental.011bfeb83dc6344ffdc0f9c757af54f61c74e22f

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.

Potentially problematic release.


This version of @strapi/core might be problematic. Click here for more details.

Files changed (52) hide show
  1. package/dist/Strapi.d.ts.map +1 -1
  2. package/dist/Strapi.js +16 -1
  3. package/dist/Strapi.js.map +1 -1
  4. package/dist/Strapi.mjs +16 -1
  5. package/dist/Strapi.mjs.map +1 -1
  6. package/dist/package.json.js +12 -12
  7. package/dist/package.json.mjs +12 -12
  8. package/dist/providers/index.js +1 -1
  9. package/dist/providers/index.js.map +1 -1
  10. package/dist/providers/index.mjs +1 -1
  11. package/dist/providers/index.mjs.map +1 -1
  12. package/dist/providers/{sessionManager.d.ts → session-manager.d.ts} +1 -1
  13. package/dist/providers/session-manager.d.ts.map +1 -0
  14. package/dist/providers/session-manager.js +23 -0
  15. package/dist/providers/session-manager.js.map +1 -0
  16. package/dist/providers/session-manager.mjs +21 -0
  17. package/dist/providers/session-manager.mjs.map +1 -0
  18. package/dist/services/document-service/components.d.ts +26 -1
  19. package/dist/services/document-service/components.d.ts.map +1 -1
  20. package/dist/services/document-service/components.js +16 -4
  21. package/dist/services/document-service/components.js.map +1 -1
  22. package/dist/services/document-service/components.mjs +15 -5
  23. package/dist/services/document-service/components.mjs.map +1 -1
  24. package/dist/services/document-service/utils/clean-component-join-table.d.ts +7 -0
  25. package/dist/services/document-service/utils/clean-component-join-table.d.ts.map +1 -0
  26. package/dist/services/metrics/index.js +2 -1
  27. package/dist/services/metrics/index.js.map +1 -1
  28. package/dist/services/metrics/index.mjs +2 -1
  29. package/dist/services/metrics/index.mjs.map +1 -1
  30. package/dist/services/metrics/middleware.d.ts +2 -1
  31. package/dist/services/metrics/middleware.d.ts.map +1 -1
  32. package/dist/services/metrics/middleware.js +2 -2
  33. package/dist/services/metrics/middleware.js.map +1 -1
  34. package/dist/services/metrics/middleware.mjs +2 -2
  35. package/dist/services/metrics/middleware.mjs.map +1 -1
  36. package/dist/services/metrics/sender.d.ts.map +1 -1
  37. package/dist/services/metrics/sender.js +2 -1
  38. package/dist/services/metrics/sender.js.map +1 -1
  39. package/dist/services/metrics/sender.mjs +2 -1
  40. package/dist/services/metrics/sender.mjs.map +1 -1
  41. package/dist/services/session-manager.d.ts +72 -20
  42. package/dist/services/session-manager.d.ts.map +1 -1
  43. package/dist/services/session-manager.js +199 -50
  44. package/dist/services/session-manager.js.map +1 -1
  45. package/dist/services/session-manager.mjs +199 -50
  46. package/dist/services/session-manager.mjs.map +1 -1
  47. package/package.json +12 -12
  48. package/dist/providers/sessionManager.d.ts.map +0 -1
  49. package/dist/providers/sessionManager.js +0 -36
  50. package/dist/providers/sessionManager.js.map +0 -1
  51. package/dist/providers/sessionManager.mjs +0 -34
  52. package/dist/providers/sessionManager.mjs.map +0 -1
@@ -63,7 +63,82 @@ class DatabaseSessionProvider {
63
63
  this.contentType = contentType;
64
64
  }
65
65
  }
66
+ class OriginSessionManager {
67
+ async generateRefreshToken(userId, deviceId, options) {
68
+ return this.sessionManager.generateRefreshToken(userId, deviceId, this.origin, options);
69
+ }
70
+ async generateAccessToken(refreshToken) {
71
+ return this.sessionManager.generateAccessToken(refreshToken, this.origin);
72
+ }
73
+ async rotateRefreshToken(refreshToken) {
74
+ return this.sessionManager.rotateRefreshToken(refreshToken, this.origin);
75
+ }
76
+ validateAccessToken(token) {
77
+ return this.sessionManager.validateAccessToken(token, this.origin);
78
+ }
79
+ async validateRefreshToken(token) {
80
+ return this.sessionManager.validateRefreshToken(token, this.origin);
81
+ }
82
+ async invalidateRefreshToken(userId, deviceId) {
83
+ return this.sessionManager.invalidateRefreshToken(this.origin, userId, deviceId);
84
+ }
85
+ /**
86
+ * Returns true when a session exists and is not expired for this origin.
87
+ * If the session exists but is expired, it will be deleted as part of this check.
88
+ */ async isSessionActive(sessionId) {
89
+ return this.sessionManager.isSessionActive(sessionId, this.origin);
90
+ }
91
+ constructor(sessionManager, origin){
92
+ this.sessionManager = sessionManager;
93
+ this.origin = origin;
94
+ }
95
+ }
66
96
  class SessionManager {
97
+ /**
98
+ * Define configuration for a specific origin
99
+ */ defineOrigin(origin, config) {
100
+ this.originConfigs.set(origin, config);
101
+ }
102
+ /**
103
+ * Check if an origin is defined
104
+ */ hasOrigin(origin) {
105
+ return this.originConfigs.has(origin);
106
+ }
107
+ /**
108
+ * Get configuration for a specific origin, throw error if not defined
109
+ */ getConfigForOrigin(origin) {
110
+ const originConfig = this.originConfigs.get(origin);
111
+ if (originConfig) {
112
+ return originConfig;
113
+ }
114
+ throw new Error(`SessionManager: Origin '${origin}' is not defined. Please define it using defineOrigin('${origin}', config).`);
115
+ }
116
+ /**
117
+ * Get the appropriate JWT key based on the algorithm
118
+ */ getJwtKey(config, algorithm, operation) {
119
+ const isAsymmetric = algorithm.startsWith('RS') || algorithm.startsWith('ES') || algorithm.startsWith('PS');
120
+ if (isAsymmetric) {
121
+ // For asymmetric algorithms, check if user has provided proper key configuration
122
+ if (operation === 'sign') {
123
+ const privateKey = config.jwtOptions?.privateKey;
124
+ if (privateKey) {
125
+ return privateKey;
126
+ }
127
+ throw new Error(`SessionManager: Private key is required for asymmetric algorithm ${algorithm}. Please configure admin.auth.options.privateKey.`);
128
+ } else {
129
+ const publicKey = config.jwtOptions?.publicKey;
130
+ if (publicKey) {
131
+ return publicKey;
132
+ }
133
+ throw new Error(`SessionManager: Public key is required for asymmetric algorithm ${algorithm}. Please configure admin.auth.options.publicKey.`);
134
+ }
135
+ } else {
136
+ if (!config.jwtSecret) {
137
+ throw new Error(`SessionManager: Secret key is required for symmetric algorithm ${algorithm}`);
138
+ }
139
+ return config.jwtSecret;
140
+ }
141
+ }
67
142
  generateSessionId() {
68
143
  return crypto.randomBytes(16).toString('hex');
69
144
  }
@@ -74,13 +149,24 @@ class SessionManager {
74
149
  await this.provider.deleteExpired();
75
150
  }
76
151
  }
152
+ /**
153
+ * Get the cleanup every calls threshold
154
+ */ get cleanupThreshold() {
155
+ return this.cleanupEveryCalls;
156
+ }
77
157
  async generateRefreshToken(userId, deviceId, origin, options) {
158
+ if (!origin || typeof origin !== 'string') {
159
+ throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
160
+ }
78
161
  await this.maybeCleanupExpired();
162
+ const config = this.getConfigForOrigin(origin);
163
+ const algorithm = config.algorithm || constants.DEFAULT_ALGORITHM;
164
+ const jwtKey = this.getJwtKey(config, algorithm, 'sign');
79
165
  const sessionId = this.generateSessionId();
80
- const familyType = options?.familyType ?? 'refresh';
81
- const isRefresh = familyType === 'refresh';
82
- const idleLifespan = isRefresh ? this.config.idleRefreshTokenLifespan : this.config.idleSessionLifespan;
83
- const maxLifespan = isRefresh ? this.config.maxRefreshTokenLifespan : this.config.maxSessionLifespan;
166
+ const tokenType = options?.type ?? 'refresh';
167
+ const isRefresh = tokenType === 'refresh';
168
+ const idleLifespan = isRefresh ? config.idleRefreshTokenLifespan : config.idleSessionLifespan;
169
+ const maxLifespan = isRefresh ? config.maxRefreshTokenLifespan : config.maxSessionLifespan;
84
170
  const now = Date.now();
85
171
  const expiresAt = new Date(now + idleLifespan * 1000);
86
172
  const absoluteExpiresAt = new Date(now + maxLifespan * 1000);
@@ -88,12 +174,12 @@ class SessionManager {
88
174
  const record = await this.provider.create({
89
175
  userId,
90
176
  sessionId,
91
- deviceId,
177
+ ...deviceId && {
178
+ deviceId
179
+ },
92
180
  origin,
93
- parentId: null,
94
181
  childId: null,
95
- familyId: sessionId,
96
- type: familyType,
182
+ type: tokenType,
97
183
  status: 'active',
98
184
  expiresAt,
99
185
  absoluteExpiresAt
@@ -107,23 +193,33 @@ class SessionManager {
107
193
  iat: issuedAtSeconds,
108
194
  exp: expiresAtSeconds
109
195
  };
110
- const token = jwt.sign(payload, this.config.jwtSecret, {
111
- algorithm: this.config.algorithm ?? constants.DEFAULT_ALGORITHM,
112
- noTimestamp: true
196
+ // Filter out conflicting options that are already handled by the payload or used for key selection
197
+ const jwtOptions = config.jwtOptions || {};
198
+ const { expiresIn, privateKey, publicKey, ...jwtSignOptions } = jwtOptions;
199
+ const token = jwt.sign(payload, jwtKey, {
200
+ algorithm,
201
+ noTimestamp: true,
202
+ ...jwtSignOptions
113
203
  });
114
204
  return {
115
205
  token,
116
206
  sessionId,
117
- absoluteExpiresAt: absoluteExpiresAt.toISOString(),
118
- familyId: record.familyId
207
+ absoluteExpiresAt: absoluteExpiresAt.toISOString()
119
208
  };
120
209
  }
121
- validateAccessToken(token) {
210
+ validateAccessToken(token, origin) {
211
+ if (!origin || typeof origin !== 'string') {
212
+ throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
213
+ }
122
214
  try {
123
- const payload = jwt.verify(token, this.config.jwtSecret, {
215
+ const config = this.getConfigForOrigin(origin);
216
+ const algorithm = config.algorithm || constants.DEFAULT_ALGORITHM;
217
+ const jwtKey = this.getJwtKey(config, algorithm, 'verify');
218
+ const payload = jwt.verify(token, jwtKey, {
124
219
  algorithms: [
125
- this.config.algorithm ?? constants.DEFAULT_ALGORITHM
126
- ]
220
+ algorithm
221
+ ],
222
+ ...config.jwtOptions
127
223
  });
128
224
  // Ensure this is an access token
129
225
  if (!payload || payload.type !== 'access') {
@@ -143,14 +239,21 @@ class SessionManager {
143
239
  };
144
240
  }
145
241
  }
146
- async validateRefreshToken(token) {
242
+ async validateRefreshToken(token, origin) {
243
+ if (!origin || typeof origin !== 'string') {
244
+ throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
245
+ }
147
246
  try {
247
+ const config = this.getConfigForOrigin(origin);
248
+ const algorithm = config.algorithm || constants.DEFAULT_ALGORITHM;
249
+ const jwtKey = this.getJwtKey(config, algorithm, 'verify');
148
250
  const verifyOptions = {
149
251
  algorithms: [
150
- this.config.algorithm ?? constants.DEFAULT_ALGORITHM
151
- ]
252
+ algorithm
253
+ ],
254
+ ...config.jwtOptions
152
255
  };
153
- const payload = jwt.verify(token, this.config.jwtSecret, verifyOptions);
256
+ const payload = jwt.verify(token, jwtKey, verifyOptions);
154
257
  if (payload.type !== 'refresh') {
155
258
  return {
156
259
  isValid: false
@@ -206,8 +309,11 @@ class SessionManager {
206
309
  deviceId
207
310
  });
208
311
  }
209
- async generateAccessToken(refreshToken) {
210
- const validation = await this.validateRefreshToken(refreshToken);
312
+ async generateAccessToken(refreshToken, origin) {
313
+ if (!origin || typeof origin !== 'string') {
314
+ throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
315
+ }
316
+ const validation = await this.validateRefreshToken(refreshToken, origin);
211
317
  if (!validation.isValid) {
212
318
  return {
213
319
  error: 'invalid_refresh_token'
@@ -218,20 +324,34 @@ class SessionManager {
218
324
  sessionId: validation.sessionId,
219
325
  type: 'access'
220
326
  };
221
- const token = jwt.sign(payload, this.config.jwtSecret, {
222
- algorithm: this.config.algorithm ?? constants.DEFAULT_ALGORITHM,
223
- expiresIn: this.config.accessTokenLifespan
327
+ const config = this.getConfigForOrigin(origin);
328
+ const algorithm = config.algorithm || constants.DEFAULT_ALGORITHM;
329
+ const jwtKey = this.getJwtKey(config, algorithm, 'sign');
330
+ // Filter out conflicting options that are already handled by the payload or used for key selection
331
+ const jwtOptions = config.jwtOptions || {};
332
+ const { expiresIn, privateKey, publicKey, ...jwtSignOptions } = jwtOptions;
333
+ const token = jwt.sign(payload, jwtKey, {
334
+ algorithm,
335
+ expiresIn: config.accessTokenLifespan,
336
+ ...jwtSignOptions
224
337
  });
225
338
  return {
226
339
  token
227
340
  };
228
341
  }
229
- async rotateRefreshToken(refreshToken) {
342
+ async rotateRefreshToken(refreshToken, origin) {
343
+ if (!origin || typeof origin !== 'string') {
344
+ throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
345
+ }
230
346
  try {
231
- const payload = jwt.verify(refreshToken, this.config.jwtSecret, {
347
+ const config = this.getConfigForOrigin(origin);
348
+ const algorithm = config.algorithm || constants.DEFAULT_ALGORITHM;
349
+ const jwtKey = this.getJwtKey(config, algorithm, 'verify');
350
+ const payload = jwt.verify(refreshToken, jwtKey, {
232
351
  algorithms: [
233
- this.config.algorithm ?? constants.DEFAULT_ALGORITHM
234
- ]
352
+ algorithm
353
+ ],
354
+ ...config.jwtOptions
235
355
  });
236
356
  if (!payload || payload.type !== 'refresh') {
237
357
  return {
@@ -257,9 +377,12 @@ class SessionManager {
257
377
  iat: childIat,
258
378
  exp: childExp
259
379
  };
260
- const childToken = jwt.sign(childPayload, this.config.jwtSecret, {
261
- algorithm: this.config.algorithm ?? constants.DEFAULT_ALGORITHM,
262
- noTimestamp: true
380
+ // Filter out conflicting options that are already handled by the payload
381
+ const { expiresIn, ...jwtSignOptions } = config.jwtOptions || {};
382
+ const childToken = jwt.sign(childPayload, jwtKey, {
383
+ algorithm,
384
+ noTimestamp: true,
385
+ ...jwtSignOptions
263
386
  });
264
387
  let absoluteExpiresAt;
265
388
  if (child.absoluteExpiresAt) {
@@ -271,14 +394,13 @@ class SessionManager {
271
394
  token: childToken,
272
395
  sessionId: child.sessionId,
273
396
  absoluteExpiresAt,
274
- familyId: String(child.familyId ?? child.sessionId),
275
397
  type: child.type ?? 'refresh'
276
398
  };
277
399
  }
278
400
  }
279
401
  const now = Date.now();
280
- const familyType = current.type ?? 'refresh';
281
- const idleLifespan = familyType === 'refresh' ? this.config.idleRefreshTokenLifespan : this.config.idleSessionLifespan;
402
+ const tokenType = current.type ?? 'refresh';
403
+ const idleLifespan = tokenType === 'refresh' ? config.idleRefreshTokenLifespan : config.idleSessionLifespan;
282
404
  // Enforce idle window since creation of the current token
283
405
  if (current.createdAt && now - new Date(current.createdAt).getTime() > idleLifespan * 1000) {
284
406
  return {
@@ -298,12 +420,12 @@ class SessionManager {
298
420
  const childRecord = await this.provider.create({
299
421
  userId: current.userId,
300
422
  sessionId: childSessionId,
301
- deviceId: current.deviceId,
423
+ ...current.deviceId && {
424
+ deviceId: current.deviceId
425
+ },
302
426
  origin: current.origin,
303
- parentId: current.sessionId,
304
427
  childId: null,
305
- familyId: current.familyId ?? current.sessionId,
306
- type: familyType,
428
+ type: tokenType,
307
429
  status: 'active',
308
430
  expiresAt: childExpiresAt,
309
431
  absoluteExpiresAt: current.absoluteExpiresAt ?? new Date(absolute)
@@ -317,9 +439,12 @@ class SessionManager {
317
439
  iat: childIat,
318
440
  exp: childExp
319
441
  };
320
- const childToken = jwt.sign(payloadOut, this.config.jwtSecret, {
321
- algorithm: this.config.algorithm ?? constants.DEFAULT_ALGORITHM,
322
- noTimestamp: true
442
+ // Filter out conflicting options that are already handled by the payload
443
+ const { expiresIn, ...jwtSignOptions } = config.jwtOptions || {};
444
+ const childToken = jwt.sign(payloadOut, jwtKey, {
445
+ algorithm,
446
+ noTimestamp: true,
447
+ ...jwtSignOptions
323
448
  });
324
449
  await this.provider.updateBySessionId(current.sessionId, {
325
450
  status: 'rotated',
@@ -335,8 +460,7 @@ class SessionManager {
335
460
  token: childToken,
336
461
  sessionId: childSessionId,
337
462
  absoluteExpiresAt,
338
- familyId: String(childRecord.familyId ?? childRecord.sessionId),
339
- type: familyType
463
+ type: tokenType
340
464
  };
341
465
  } catch {
342
466
  return {
@@ -347,11 +471,14 @@ class SessionManager {
347
471
  /**
348
472
  * Returns true when a session exists and is not expired.
349
473
  * If the session exists but is expired, it will be deleted as part of this check.
350
- */ async isSessionActive(sessionId) {
474
+ */ async isSessionActive(sessionId, origin) {
351
475
  const session = await this.provider.findBySessionId(sessionId);
352
476
  if (!session) {
353
477
  return false;
354
478
  }
479
+ if (session.origin !== origin) {
480
+ return false;
481
+ }
355
482
  if (new Date(session.expiresAt) <= new Date()) {
356
483
  // Clean up expired session eagerly
357
484
  await this.provider.deleteBySessionId(sessionId);
@@ -359,20 +486,42 @@ class SessionManager {
359
486
  }
360
487
  return true;
361
488
  }
362
- constructor(provider, config){
489
+ constructor(provider){
490
+ // Store origin-specific configurations
491
+ this.originConfigs = new Map();
363
492
  // Run expired cleanup only every N calls to avoid extra queries
364
493
  this.cleanupInvocationCounter = 0;
365
494
  this.cleanupEveryCalls = 50;
366
495
  this.provider = provider;
367
- this.config = config;
368
496
  }
369
497
  }
370
498
  const createDatabaseProvider = (db, contentType)=>{
371
499
  return new DatabaseSessionProvider(db, contentType);
372
500
  };
373
- const createSessionManager = ({ db, config })=>{
501
+ const createSessionManager = ({ db })=>{
374
502
  const provider = createDatabaseProvider(db, 'admin::session');
375
- return new SessionManager(provider, config);
503
+ const sessionManager = new SessionManager(provider);
504
+ // Add callable functionality
505
+ const fluentApi = (origin)=>{
506
+ if (!origin || typeof origin !== 'string') {
507
+ throw new Error('SessionManager: Origin parameter is required and must be a non-empty string');
508
+ }
509
+ return new OriginSessionManager(sessionManager, origin);
510
+ };
511
+ // Attach only the public SessionManagerService API to the callable
512
+ const api = fluentApi;
513
+ api.generateSessionId = sessionManager.generateSessionId.bind(sessionManager);
514
+ api.defineOrigin = sessionManager.defineOrigin.bind(sessionManager);
515
+ api.hasOrigin = sessionManager.hasOrigin.bind(sessionManager);
516
+ // Note: isSessionActive is origin-scoped and exposed on OriginSessionManager only
517
+ // Forward the cleanupThreshold getter (used in tests)
518
+ Object.defineProperty(api, 'cleanupThreshold', {
519
+ get () {
520
+ return sessionManager.cleanupThreshold;
521
+ },
522
+ enumerable: true
523
+ });
524
+ return api;
376
525
  };
377
526
 
378
527
  exports.createDatabaseProvider = createDatabaseProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.js","sources":["../../src/services/session-manager.ts"],"sourcesContent":["import crypto from 'crypto';\nimport jwt from 'jsonwebtoken';\nimport type { Algorithm, VerifyOptions } from 'jsonwebtoken';\nimport type { Database } from '@strapi/database';\nimport { DEFAULT_ALGORITHM } from '../constants';\n\nexport interface SessionProvider {\n create(session: SessionData): Promise<SessionData>;\n findBySessionId(sessionId: string): Promise<SessionData | null>;\n updateBySessionId(sessionId: string, data: Partial<SessionData>): Promise<void>;\n deleteBySessionId(sessionId: string): Promise<void>;\n deleteExpired(): Promise<void>;\n deleteBy(criteria: { userId?: string; origin?: string; deviceId?: string }): Promise<void>;\n}\n\nexport interface SessionData {\n id?: string;\n userId: string; // User ID stored as string (key-value store)\n sessionId: string;\n deviceId: string;\n origin: string;\n parentId?: string | null;\n childId?: string | null;\n familyId?: string | null;\n type?: 'refresh' | 'session';\n status?: 'active' | 'rotated' | 'revoked';\n expiresAt: Date;\n absoluteExpiresAt?: Date | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nexport interface RefreshTokenPayload {\n userId: string;\n sessionId: string;\n type: 'refresh';\n exp: number;\n iat: number;\n}\n\nexport interface AccessTokenPayload {\n userId: string;\n sessionId: string;\n type: 'access';\n exp: number;\n iat: number;\n}\n\nexport type TokenPayload = RefreshTokenPayload | AccessTokenPayload;\n\nexport interface ValidateRefreshTokenResult {\n isValid: boolean;\n userId?: string;\n sessionId?: string;\n error?:\n | 'invalid_token'\n | 'token_expired'\n | 'session_not_found'\n | 'session_expired'\n | 'wrong_token_type';\n}\n\nclass DatabaseSessionProvider implements SessionProvider {\n private db: Database;\n\n private contentType: string;\n\n constructor(db: Database, contentType: string) {\n this.db = db;\n this.contentType = contentType;\n }\n\n async create(session: SessionData): Promise<SessionData> {\n const result = await this.db.query(this.contentType).create({\n data: session,\n });\n return result as SessionData;\n }\n\n async findBySessionId(sessionId: string): Promise<SessionData | null> {\n const result = await this.db.query(this.contentType).findOne({\n where: { sessionId },\n });\n return result as SessionData | null;\n }\n\n async updateBySessionId(sessionId: string, data: Partial<SessionData>): Promise<void> {\n await this.db.query(this.contentType).update({ where: { sessionId }, data });\n }\n\n async deleteBySessionId(sessionId: string): Promise<void> {\n await this.db.query(this.contentType).delete({\n where: { sessionId },\n });\n }\n\n async deleteExpired(): Promise<void> {\n await this.db.query(this.contentType).deleteMany({\n where: { absoluteExpiresAt: { $lt: new Date() } },\n });\n }\n\n async deleteBy(criteria: { userId?: string; origin?: string; deviceId?: string }): Promise<void> {\n await this.db.query(this.contentType).deleteMany({\n where: {\n ...(criteria.userId ? { userId: criteria.userId } : {}),\n ...(criteria.origin ? { origin: criteria.origin } : {}),\n ...(criteria.deviceId ? { deviceId: criteria.deviceId } : {}),\n },\n });\n }\n}\n\nexport interface SessionManagerConfig {\n jwtSecret: string;\n accessTokenLifespan: number;\n maxRefreshTokenLifespan: number;\n idleRefreshTokenLifespan: number;\n maxSessionLifespan: number;\n idleSessionLifespan: number;\n /**\n * JWT signing/verification algorithm. Defaults to 'HS256' when not provided.\n */\n algorithm?: Algorithm;\n}\n\nclass SessionManager {\n private provider: SessionProvider;\n\n private config: SessionManagerConfig;\n\n // Run expired cleanup only every N calls to avoid extra queries\n private cleanupInvocationCounter: number = 0;\n\n private readonly cleanupEveryCalls: number = 50;\n\n constructor(provider: SessionProvider, config: SessionManagerConfig) {\n this.provider = provider;\n this.config = config;\n }\n\n generateSessionId(): string {\n return crypto.randomBytes(16).toString('hex');\n }\n\n private async maybeCleanupExpired(): Promise<void> {\n this.cleanupInvocationCounter += 1;\n if (this.cleanupInvocationCounter >= this.cleanupEveryCalls) {\n this.cleanupInvocationCounter = 0;\n\n await this.provider.deleteExpired();\n }\n }\n\n async generateRefreshToken(\n userId: string,\n deviceId: string,\n origin: string,\n options?: { familyType?: 'refresh' | 'session' }\n ): Promise<{ token: string; sessionId: string; absoluteExpiresAt: string; familyId: string }> {\n await this.maybeCleanupExpired();\n\n const sessionId = this.generateSessionId();\n const familyType = options?.familyType ?? 'refresh';\n const isRefresh = familyType === 'refresh';\n\n const idleLifespan = isRefresh\n ? this.config.idleRefreshTokenLifespan\n : this.config.idleSessionLifespan;\n\n const maxLifespan = isRefresh\n ? this.config.maxRefreshTokenLifespan\n : this.config.maxSessionLifespan;\n\n const now = Date.now();\n const expiresAt = new Date(now + idleLifespan * 1000);\n const absoluteExpiresAt = new Date(now + maxLifespan * 1000);\n\n // Create the root record first so createdAt can be used for signing.\n const record = await this.provider.create({\n userId,\n sessionId,\n deviceId,\n origin,\n parentId: null,\n childId: null,\n familyId: sessionId,\n type: familyType,\n status: 'active',\n expiresAt,\n absoluteExpiresAt,\n });\n\n const issuedAtSeconds = Math.floor(new Date(record.createdAt ?? new Date()).getTime() / 1000);\n const expiresAtSeconds = Math.floor(new Date(record.expiresAt).getTime() / 1000);\n\n const payload: RefreshTokenPayload = {\n userId,\n sessionId,\n type: 'refresh',\n iat: issuedAtSeconds,\n exp: expiresAtSeconds,\n };\n\n const token = jwt.sign(payload, this.config.jwtSecret, {\n algorithm: this.config.algorithm ?? DEFAULT_ALGORITHM,\n noTimestamp: true,\n });\n\n return {\n token,\n sessionId,\n absoluteExpiresAt: absoluteExpiresAt.toISOString(),\n familyId: record.familyId!,\n };\n }\n\n validateAccessToken(\n token: string\n ): { isValid: true; payload: AccessTokenPayload } | { isValid: false; payload: null } {\n try {\n const payload = jwt.verify(token, this.config.jwtSecret, {\n algorithms: [this.config.algorithm ?? DEFAULT_ALGORITHM],\n }) as TokenPayload;\n\n // Ensure this is an access token\n if (!payload || payload.type !== 'access') {\n return { isValid: false, payload: null };\n }\n\n return { isValid: true, payload };\n } catch (err) {\n return { isValid: false, payload: null };\n }\n }\n\n async validateRefreshToken(token: string): Promise<ValidateRefreshTokenResult> {\n try {\n const verifyOptions: VerifyOptions = {\n algorithms: [this.config.algorithm ?? DEFAULT_ALGORITHM],\n };\n\n const payload = jwt.verify(\n token,\n this.config.jwtSecret,\n verifyOptions\n ) as RefreshTokenPayload;\n\n if (payload.type !== 'refresh') {\n return { isValid: false };\n }\n\n const session = await this.provider.findBySessionId(payload.sessionId);\n if (!session) {\n return { isValid: false };\n }\n\n const now = new Date();\n if (new Date(session.expiresAt) <= now) {\n return { isValid: false };\n }\n\n // Absolute family expiry check\n if (session.absoluteExpiresAt && new Date(session.absoluteExpiresAt) <= now) {\n return { isValid: false };\n }\n\n // Only 'active' sessions are eligible to create access tokens.\n if (session.status !== 'active') {\n return { isValid: false };\n }\n\n if (session.userId !== payload.userId) {\n return { isValid: false };\n }\n\n return {\n isValid: true,\n userId: payload.userId,\n sessionId: payload.sessionId,\n };\n } catch (error: any) {\n if (error instanceof jwt.JsonWebTokenError) {\n return { isValid: false };\n }\n\n throw error;\n }\n }\n\n async invalidateRefreshToken(origin: string, userId: string, deviceId?: string): Promise<void> {\n await this.provider.deleteBy({ userId, origin, deviceId });\n }\n\n async generateAccessToken(refreshToken: string): Promise<{ token: string } | { error: string }> {\n const validation = await this.validateRefreshToken(refreshToken);\n\n if (!validation.isValid) {\n return { error: 'invalid_refresh_token' };\n }\n\n const payload: Omit<AccessTokenPayload, 'iat' | 'exp'> = {\n userId: String(validation.userId!),\n sessionId: validation.sessionId!,\n type: 'access',\n };\n\n const token = jwt.sign(payload, this.config.jwtSecret, {\n algorithm: this.config.algorithm ?? DEFAULT_ALGORITHM,\n expiresIn: this.config.accessTokenLifespan,\n });\n\n return { token };\n }\n\n async rotateRefreshToken(refreshToken: string): Promise<\n | {\n token: string;\n sessionId: string;\n absoluteExpiresAt: string;\n familyId: string;\n type: 'refresh' | 'session';\n }\n | { error: string }\n > {\n try {\n const payload = jwt.verify(refreshToken, this.config.jwtSecret, {\n algorithms: [this.config.algorithm ?? DEFAULT_ALGORITHM],\n }) as RefreshTokenPayload;\n\n if (!payload || payload.type !== 'refresh') {\n return { error: 'invalid_refresh_token' };\n }\n\n const current = await this.provider.findBySessionId(payload.sessionId);\n if (!current) {\n return { error: 'invalid_refresh_token' };\n }\n\n // If parent already has a child, return the same child token\n if (current.childId) {\n const child = await this.provider.findBySessionId(current.childId);\n\n if (child) {\n const childIat = Math.floor(new Date(child.createdAt ?? new Date()).getTime() / 1000);\n const childExp = Math.floor(new Date(child.expiresAt).getTime() / 1000);\n\n const childPayload: RefreshTokenPayload = {\n userId: child.userId,\n sessionId: child.sessionId,\n type: 'refresh',\n iat: childIat,\n exp: childExp,\n };\n\n const childToken = jwt.sign(childPayload, this.config.jwtSecret, {\n algorithm: this.config.algorithm ?? DEFAULT_ALGORITHM,\n noTimestamp: true,\n });\n\n let absoluteExpiresAt;\n if (child.absoluteExpiresAt) {\n absoluteExpiresAt =\n typeof child.absoluteExpiresAt === 'string'\n ? child.absoluteExpiresAt\n : child.absoluteExpiresAt.toISOString();\n } else {\n absoluteExpiresAt = new Date(0).toISOString();\n }\n\n return {\n token: childToken,\n sessionId: child.sessionId,\n absoluteExpiresAt,\n familyId: String(child.familyId ?? child.sessionId),\n type: child.type ?? 'refresh',\n };\n }\n }\n\n const now = Date.now();\n const familyType = current.type ?? 'refresh';\n const idleLifespan =\n familyType === 'refresh'\n ? this.config.idleRefreshTokenLifespan\n : this.config.idleSessionLifespan;\n\n // Enforce idle window since creation of the current token\n if (current.createdAt && now - new Date(current.createdAt).getTime() > idleLifespan * 1000) {\n return { error: 'idle_window_elapsed' };\n }\n\n // Enforce max family window using absoluteExpiresAt\n const absolute = current.absoluteExpiresAt\n ? new Date(current.absoluteExpiresAt).getTime()\n : now;\n if (absolute <= now) {\n return { error: 'max_window_elapsed' };\n }\n\n // Create child token\n const childSessionId = this.generateSessionId();\n const childExpiresAt = new Date(now + idleLifespan * 1000);\n\n const childRecord = await this.provider.create({\n userId: current.userId,\n sessionId: childSessionId,\n deviceId: current.deviceId,\n origin: current.origin,\n parentId: current.sessionId,\n childId: null,\n familyId: current.familyId ?? current.sessionId,\n type: familyType,\n status: 'active',\n expiresAt: childExpiresAt,\n absoluteExpiresAt: current.absoluteExpiresAt ?? new Date(absolute),\n });\n\n const childIat = Math.floor(new Date(childRecord.createdAt ?? new Date()).getTime() / 1000);\n const childExp = Math.floor(new Date(childRecord.expiresAt).getTime() / 1000);\n const payloadOut: RefreshTokenPayload = {\n userId: current.userId,\n sessionId: childSessionId,\n type: 'refresh',\n iat: childIat,\n exp: childExp,\n };\n const childToken = jwt.sign(payloadOut, this.config.jwtSecret, {\n algorithm: this.config.algorithm ?? DEFAULT_ALGORITHM,\n noTimestamp: true,\n });\n\n await this.provider.updateBySessionId(current.sessionId, {\n status: 'rotated',\n childId: childSessionId,\n });\n\n let absoluteExpiresAt;\n if (childRecord.absoluteExpiresAt) {\n absoluteExpiresAt =\n typeof childRecord.absoluteExpiresAt === 'string'\n ? childRecord.absoluteExpiresAt\n : childRecord.absoluteExpiresAt.toISOString();\n } else {\n absoluteExpiresAt = new Date(absolute).toISOString();\n }\n\n return {\n token: childToken,\n sessionId: childSessionId,\n absoluteExpiresAt,\n familyId: String(childRecord.familyId ?? childRecord.sessionId),\n type: familyType,\n };\n } catch {\n return { error: 'invalid_refresh_token' };\n }\n }\n\n /**\n * Returns true when a session exists and is not expired.\n * If the session exists but is expired, it will be deleted as part of this check.\n */\n async isSessionActive(sessionId: string): Promise<boolean> {\n const session = await this.provider.findBySessionId(sessionId);\n if (!session) {\n return false;\n }\n\n if (new Date(session.expiresAt) <= new Date()) {\n // Clean up expired session eagerly\n await this.provider.deleteBySessionId(sessionId);\n\n return false;\n }\n\n return true;\n }\n}\n\nconst createDatabaseProvider = (db: Database, contentType: string): SessionProvider => {\n return new DatabaseSessionProvider(db, contentType);\n};\n\nconst createSessionManager = ({ db, config }: { db: Database; config: SessionManagerConfig }) => {\n const provider = createDatabaseProvider(db, 'admin::session');\n return new SessionManager(provider, config);\n};\n\nexport { createSessionManager, createDatabaseProvider };\n"],"names":["DatabaseSessionProvider","create","session","result","db","query","contentType","data","findBySessionId","sessionId","findOne","where","updateBySessionId","update","deleteBySessionId","delete","deleteExpired","deleteMany","absoluteExpiresAt","$lt","Date","deleteBy","criteria","userId","origin","deviceId","constructor","SessionManager","generateSessionId","crypto","randomBytes","toString","maybeCleanupExpired","cleanupInvocationCounter","cleanupEveryCalls","provider","generateRefreshToken","options","familyType","isRefresh","idleLifespan","config","idleRefreshTokenLifespan","idleSessionLifespan","maxLifespan","maxRefreshTokenLifespan","maxSessionLifespan","now","expiresAt","record","parentId","childId","familyId","type","status","issuedAtSeconds","Math","floor","createdAt","getTime","expiresAtSeconds","payload","iat","exp","token","jwt","sign","jwtSecret","algorithm","DEFAULT_ALGORITHM","noTimestamp","toISOString","validateAccessToken","verify","algorithms","isValid","err","validateRefreshToken","verifyOptions","error","JsonWebTokenError","invalidateRefreshToken","generateAccessToken","refreshToken","validation","String","expiresIn","accessTokenLifespan","rotateRefreshToken","current","child","childIat","childExp","childPayload","childToken","absolute","childSessionId","childExpiresAt","childRecord","payloadOut","isSessionActive","createDatabaseProvider","createSessionManager"],"mappings":"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"}
1
+ {"version":3,"file":"session-manager.js","sources":["../../src/services/session-manager.ts"],"sourcesContent":["import crypto from 'crypto';\nimport jwt from 'jsonwebtoken';\nimport type { VerifyOptions, Algorithm } from 'jsonwebtoken';\nimport type { Database } from '@strapi/database';\nimport { DEFAULT_ALGORITHM } from '../constants';\n\nexport interface SessionProvider {\n create(session: SessionData): Promise<SessionData>;\n findBySessionId(sessionId: string): Promise<SessionData | null>;\n updateBySessionId(sessionId: string, data: Partial<SessionData>): Promise<void>;\n deleteBySessionId(sessionId: string): Promise<void>;\n deleteExpired(): Promise<void>;\n deleteBy(criteria: { userId?: string; origin?: string; deviceId?: string }): Promise<void>;\n}\n\nexport interface SessionData {\n id?: string;\n userId: string; // User ID stored as string (key-value store)\n sessionId: string;\n deviceId?: string; // Optional for origins that don't need device tracking\n origin: string;\n childId?: string | null;\n\n type?: 'refresh' | 'session';\n status?: 'active' | 'rotated' | 'revoked';\n expiresAt: Date;\n absoluteExpiresAt?: Date | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nexport interface RefreshTokenPayload {\n userId: string;\n sessionId: string;\n type: 'refresh';\n exp: number;\n iat: number;\n}\n\nexport interface AccessTokenPayload {\n userId: string;\n sessionId: string;\n type: 'access';\n exp: number;\n iat: number;\n}\n\nexport type TokenPayload = RefreshTokenPayload | AccessTokenPayload;\n\nexport interface ValidateRefreshTokenResult {\n isValid: boolean;\n userId?: string;\n sessionId?: string;\n error?:\n | 'invalid_token'\n | 'token_expired'\n | 'session_not_found'\n | 'session_expired'\n | 'wrong_token_type';\n}\n\nclass DatabaseSessionProvider implements SessionProvider {\n private db: Database;\n\n private contentType: string;\n\n constructor(db: Database, contentType: string) {\n this.db = db;\n this.contentType = contentType;\n }\n\n async create(session: SessionData): Promise<SessionData> {\n const result = await this.db.query(this.contentType).create({\n data: session,\n });\n return result as SessionData;\n }\n\n async findBySessionId(sessionId: string): Promise<SessionData | null> {\n const result = await this.db.query(this.contentType).findOne({\n where: { sessionId },\n });\n return result as SessionData | null;\n }\n\n async updateBySessionId(sessionId: string, data: Partial<SessionData>): Promise<void> {\n await this.db.query(this.contentType).update({ where: { sessionId }, data });\n }\n\n async deleteBySessionId(sessionId: string): Promise<void> {\n await this.db.query(this.contentType).delete({\n where: { sessionId },\n });\n }\n\n async deleteExpired(): Promise<void> {\n await this.db.query(this.contentType).deleteMany({\n where: { absoluteExpiresAt: { $lt: new Date() } },\n });\n }\n\n async deleteBy(criteria: { userId?: string; origin?: string; deviceId?: string }): Promise<void> {\n await this.db.query(this.contentType).deleteMany({\n where: {\n ...(criteria.userId ? { userId: criteria.userId } : {}),\n ...(criteria.origin ? { origin: criteria.origin } : {}),\n ...(criteria.deviceId ? { deviceId: criteria.deviceId } : {}),\n },\n });\n }\n}\n\nexport interface SessionManagerConfig {\n jwtSecret?: string;\n accessTokenLifespan: number;\n maxRefreshTokenLifespan: number;\n idleRefreshTokenLifespan: number;\n maxSessionLifespan: number;\n idleSessionLifespan: number;\n algorithm?: Algorithm;\n jwtOptions?: Record<string, unknown>;\n}\n\nclass OriginSessionManager {\n constructor(\n private sessionManager: SessionManager,\n private origin: string\n ) {}\n\n async generateRefreshToken(\n userId: string,\n deviceId: string | undefined,\n options?: { type?: 'refresh' | 'session' }\n ): Promise<{ token: string; sessionId: string; absoluteExpiresAt: string }> {\n return this.sessionManager.generateRefreshToken(userId, deviceId, this.origin, options);\n }\n\n async generateAccessToken(refreshToken: string): Promise<{ token: string } | { error: string }> {\n return this.sessionManager.generateAccessToken(refreshToken, this.origin);\n }\n\n async rotateRefreshToken(refreshToken: string): Promise<\n | {\n token: string;\n sessionId: string;\n absoluteExpiresAt: string;\n type: 'refresh' | 'session';\n }\n | { error: string }\n > {\n return this.sessionManager.rotateRefreshToken(refreshToken, this.origin);\n }\n\n validateAccessToken(\n token: string\n ): { isValid: true; payload: AccessTokenPayload } | { isValid: false; payload: null } {\n return this.sessionManager.validateAccessToken(token, this.origin);\n }\n\n async validateRefreshToken(token: string): Promise<ValidateRefreshTokenResult> {\n return this.sessionManager.validateRefreshToken(token, this.origin);\n }\n\n async invalidateRefreshToken(userId: string, deviceId?: string): Promise<void> {\n return this.sessionManager.invalidateRefreshToken(this.origin, userId, deviceId);\n }\n\n /**\n * Returns true when a session exists and is not expired for this origin.\n * If the session exists but is expired, it will be deleted as part of this check.\n */\n async isSessionActive(sessionId: string): Promise<boolean> {\n return this.sessionManager.isSessionActive(sessionId, this.origin);\n }\n}\n\nclass SessionManager {\n private provider: SessionProvider;\n\n // Store origin-specific configurations\n private originConfigs: Map<string, SessionManagerConfig> = new Map();\n\n // Run expired cleanup only every N calls to avoid extra queries\n private cleanupInvocationCounter: number = 0;\n\n private readonly cleanupEveryCalls: number = 50;\n\n constructor(provider: SessionProvider) {\n this.provider = provider;\n }\n\n /**\n * Define configuration for a specific origin\n */\n defineOrigin(origin: string, config: SessionManagerConfig): void {\n this.originConfigs.set(origin, config);\n }\n\n /**\n * Check if an origin is defined\n */\n hasOrigin(origin: string): boolean {\n return this.originConfigs.has(origin);\n }\n\n /**\n * Get configuration for a specific origin, throw error if not defined\n */\n private getConfigForOrigin(origin: string): SessionManagerConfig {\n const originConfig = this.originConfigs.get(origin);\n if (originConfig) {\n return originConfig;\n }\n throw new Error(\n `SessionManager: Origin '${origin}' is not defined. Please define it using defineOrigin('${origin}', config).`\n );\n }\n\n /**\n * Get the appropriate JWT key based on the algorithm\n */\n private getJwtKey(\n config: SessionManagerConfig,\n algorithm: Algorithm,\n operation: 'sign' | 'verify'\n ): string {\n const isAsymmetric =\n algorithm.startsWith('RS') || algorithm.startsWith('ES') || algorithm.startsWith('PS');\n\n if (isAsymmetric) {\n // For asymmetric algorithms, check if user has provided proper key configuration\n if (operation === 'sign') {\n const privateKey = config.jwtOptions?.privateKey as string;\n if (privateKey) {\n return privateKey;\n }\n throw new Error(\n `SessionManager: Private key is required for asymmetric algorithm ${algorithm}. Please configure admin.auth.options.privateKey.`\n );\n } else {\n const publicKey = config.jwtOptions?.publicKey as string;\n if (publicKey) {\n return publicKey;\n }\n throw new Error(\n `SessionManager: Public key is required for asymmetric algorithm ${algorithm}. Please configure admin.auth.options.publicKey.`\n );\n }\n } else {\n if (!config.jwtSecret) {\n throw new Error(\n `SessionManager: Secret key is required for symmetric algorithm ${algorithm}`\n );\n }\n return config.jwtSecret;\n }\n }\n\n generateSessionId(): string {\n return crypto.randomBytes(16).toString('hex');\n }\n\n private async maybeCleanupExpired(): Promise<void> {\n this.cleanupInvocationCounter += 1;\n if (this.cleanupInvocationCounter >= this.cleanupEveryCalls) {\n this.cleanupInvocationCounter = 0;\n\n await this.provider.deleteExpired();\n }\n }\n\n /**\n * Get the cleanup every calls threshold\n */\n get cleanupThreshold(): number {\n return this.cleanupEveryCalls;\n }\n\n async generateRefreshToken(\n userId: string,\n deviceId: string | undefined,\n origin: string,\n options?: { type?: 'refresh' | 'session' }\n ): Promise<{ token: string; sessionId: string; absoluteExpiresAt: string }> {\n if (!origin || typeof origin !== 'string') {\n throw new Error(\n 'SessionManager: Origin parameter is required and must be a non-empty string'\n );\n }\n\n await this.maybeCleanupExpired();\n\n const config = this.getConfigForOrigin(origin);\n const algorithm = config.algorithm || DEFAULT_ALGORITHM;\n const jwtKey = this.getJwtKey(config, algorithm, 'sign');\n const sessionId = this.generateSessionId();\n const tokenType = options?.type ?? 'refresh';\n const isRefresh = tokenType === 'refresh';\n\n const idleLifespan = isRefresh ? config.idleRefreshTokenLifespan : config.idleSessionLifespan;\n\n const maxLifespan = isRefresh ? config.maxRefreshTokenLifespan : config.maxSessionLifespan;\n\n const now = Date.now();\n const expiresAt = new Date(now + idleLifespan * 1000);\n const absoluteExpiresAt = new Date(now + maxLifespan * 1000);\n\n // Create the root record first so createdAt can be used for signing.\n const record = await this.provider.create({\n userId,\n sessionId,\n ...(deviceId && { deviceId }),\n origin,\n childId: null,\n type: tokenType,\n status: 'active',\n expiresAt,\n absoluteExpiresAt,\n });\n\n const issuedAtSeconds = Math.floor(new Date(record.createdAt ?? new Date()).getTime() / 1000);\n const expiresAtSeconds = Math.floor(new Date(record.expiresAt).getTime() / 1000);\n\n const payload: RefreshTokenPayload = {\n userId,\n sessionId,\n type: 'refresh',\n iat: issuedAtSeconds,\n exp: expiresAtSeconds,\n };\n\n // Filter out conflicting options that are already handled by the payload or used for key selection\n const jwtOptions = config.jwtOptions || {};\n const { expiresIn, privateKey, publicKey, ...jwtSignOptions } = jwtOptions;\n\n const token = jwt.sign(payload, jwtKey, {\n algorithm,\n noTimestamp: true,\n ...jwtSignOptions,\n });\n\n return {\n token,\n sessionId,\n absoluteExpiresAt: absoluteExpiresAt.toISOString(),\n };\n }\n\n validateAccessToken(\n token: string,\n origin: string\n ): { isValid: true; payload: AccessTokenPayload } | { isValid: false; payload: null } {\n if (!origin || typeof origin !== 'string') {\n throw new Error(\n 'SessionManager: Origin parameter is required and must be a non-empty string'\n );\n }\n\n try {\n const config = this.getConfigForOrigin(origin);\n const algorithm = config.algorithm || DEFAULT_ALGORITHM;\n const jwtKey = this.getJwtKey(config, algorithm, 'verify');\n const payload = jwt.verify(token, jwtKey, {\n algorithms: [algorithm],\n ...config.jwtOptions,\n }) as TokenPayload;\n\n // Ensure this is an access token\n if (!payload || payload.type !== 'access') {\n return { isValid: false, payload: null };\n }\n\n return { isValid: true, payload };\n } catch (err) {\n return { isValid: false, payload: null };\n }\n }\n\n async validateRefreshToken(token: string, origin: string): Promise<ValidateRefreshTokenResult> {\n if (!origin || typeof origin !== 'string') {\n throw new Error(\n 'SessionManager: Origin parameter is required and must be a non-empty string'\n );\n }\n\n try {\n const config = this.getConfigForOrigin(origin);\n const algorithm = config.algorithm || DEFAULT_ALGORITHM;\n const jwtKey = this.getJwtKey(config, algorithm, 'verify');\n const verifyOptions: VerifyOptions = {\n algorithms: [algorithm],\n ...config.jwtOptions,\n };\n\n const payload = jwt.verify(token, jwtKey, verifyOptions) as RefreshTokenPayload;\n\n if (payload.type !== 'refresh') {\n return { isValid: false };\n }\n\n const session = await this.provider.findBySessionId(payload.sessionId);\n if (!session) {\n return { isValid: false };\n }\n\n const now = new Date();\n if (new Date(session.expiresAt) <= now) {\n return { isValid: false };\n }\n\n // Absolute family expiry check\n if (session.absoluteExpiresAt && new Date(session.absoluteExpiresAt) <= now) {\n return { isValid: false };\n }\n\n // Only 'active' sessions are eligible to create access tokens.\n if (session.status !== 'active') {\n return { isValid: false };\n }\n\n if (session.userId !== payload.userId) {\n return { isValid: false };\n }\n\n return {\n isValid: true,\n userId: payload.userId,\n sessionId: payload.sessionId,\n };\n } catch (error: any) {\n if (error instanceof jwt.JsonWebTokenError) {\n return { isValid: false };\n }\n\n throw error;\n }\n }\n\n async invalidateRefreshToken(origin: string, userId: string, deviceId?: string): Promise<void> {\n await this.provider.deleteBy({ userId, origin, deviceId });\n }\n\n async generateAccessToken(\n refreshToken: string,\n origin: string\n ): Promise<{ token: string } | { error: string }> {\n if (!origin || typeof origin !== 'string') {\n throw new Error(\n 'SessionManager: Origin parameter is required and must be a non-empty string'\n );\n }\n\n const validation = await this.validateRefreshToken(refreshToken, origin);\n\n if (!validation.isValid) {\n return { error: 'invalid_refresh_token' };\n }\n\n const payload: Omit<AccessTokenPayload, 'iat' | 'exp'> = {\n userId: String(validation.userId!),\n sessionId: validation.sessionId!,\n type: 'access',\n };\n\n const config = this.getConfigForOrigin(origin);\n const algorithm = config.algorithm || DEFAULT_ALGORITHM;\n const jwtKey = this.getJwtKey(config, algorithm, 'sign');\n // Filter out conflicting options that are already handled by the payload or used for key selection\n const jwtOptions = config.jwtOptions || {};\n const { expiresIn, privateKey, publicKey, ...jwtSignOptions } = jwtOptions;\n\n const token = jwt.sign(payload, jwtKey, {\n algorithm,\n expiresIn: config.accessTokenLifespan,\n ...jwtSignOptions,\n });\n\n return { token };\n }\n\n async rotateRefreshToken(\n refreshToken: string,\n origin: string\n ): Promise<\n | {\n token: string;\n sessionId: string;\n absoluteExpiresAt: string;\n type: 'refresh' | 'session';\n }\n | { error: string }\n > {\n if (!origin || typeof origin !== 'string') {\n throw new Error(\n 'SessionManager: Origin parameter is required and must be a non-empty string'\n );\n }\n\n try {\n const config = this.getConfigForOrigin(origin);\n const algorithm = config.algorithm || DEFAULT_ALGORITHM;\n const jwtKey = this.getJwtKey(config, algorithm, 'verify');\n const payload = jwt.verify(refreshToken, jwtKey, {\n algorithms: [algorithm],\n ...config.jwtOptions,\n }) as RefreshTokenPayload;\n\n if (!payload || payload.type !== 'refresh') {\n return { error: 'invalid_refresh_token' };\n }\n\n const current = await this.provider.findBySessionId(payload.sessionId);\n if (!current) {\n return { error: 'invalid_refresh_token' };\n }\n\n // If parent already has a child, return the same child token\n if (current.childId) {\n const child = await this.provider.findBySessionId(current.childId);\n\n if (child) {\n const childIat = Math.floor(new Date(child.createdAt ?? new Date()).getTime() / 1000);\n const childExp = Math.floor(new Date(child.expiresAt).getTime() / 1000);\n\n const childPayload: RefreshTokenPayload = {\n userId: child.userId,\n sessionId: child.sessionId,\n type: 'refresh',\n iat: childIat,\n exp: childExp,\n };\n\n // Filter out conflicting options that are already handled by the payload\n const { expiresIn, ...jwtSignOptions } = config.jwtOptions || {};\n\n const childToken = jwt.sign(childPayload, jwtKey, {\n algorithm,\n noTimestamp: true,\n ...jwtSignOptions,\n });\n\n let absoluteExpiresAt;\n if (child.absoluteExpiresAt) {\n absoluteExpiresAt =\n typeof child.absoluteExpiresAt === 'string'\n ? child.absoluteExpiresAt\n : child.absoluteExpiresAt.toISOString();\n } else {\n absoluteExpiresAt = new Date(0).toISOString();\n }\n\n return {\n token: childToken,\n sessionId: child.sessionId,\n absoluteExpiresAt,\n type: child.type ?? 'refresh',\n };\n }\n }\n\n const now = Date.now();\n const tokenType = current.type ?? 'refresh';\n const idleLifespan =\n tokenType === 'refresh' ? config.idleRefreshTokenLifespan : config.idleSessionLifespan;\n\n // Enforce idle window since creation of the current token\n if (current.createdAt && now - new Date(current.createdAt).getTime() > idleLifespan * 1000) {\n return { error: 'idle_window_elapsed' };\n }\n\n // Enforce max family window using absoluteExpiresAt\n const absolute = current.absoluteExpiresAt\n ? new Date(current.absoluteExpiresAt).getTime()\n : now;\n if (absolute <= now) {\n return { error: 'max_window_elapsed' };\n }\n\n // Create child token\n const childSessionId = this.generateSessionId();\n const childExpiresAt = new Date(now + idleLifespan * 1000);\n\n const childRecord = await this.provider.create({\n userId: current.userId,\n sessionId: childSessionId,\n ...(current.deviceId && { deviceId: current.deviceId }),\n origin: current.origin,\n childId: null,\n type: tokenType,\n status: 'active',\n expiresAt: childExpiresAt,\n absoluteExpiresAt: current.absoluteExpiresAt ?? new Date(absolute),\n });\n\n const childIat = Math.floor(new Date(childRecord.createdAt ?? new Date()).getTime() / 1000);\n const childExp = Math.floor(new Date(childRecord.expiresAt).getTime() / 1000);\n const payloadOut: RefreshTokenPayload = {\n userId: current.userId,\n sessionId: childSessionId,\n type: 'refresh',\n iat: childIat,\n exp: childExp,\n };\n // Filter out conflicting options that are already handled by the payload\n const { expiresIn, ...jwtSignOptions } = config.jwtOptions || {};\n\n const childToken = jwt.sign(payloadOut, jwtKey, {\n algorithm,\n noTimestamp: true,\n ...jwtSignOptions,\n });\n\n await this.provider.updateBySessionId(current.sessionId, {\n status: 'rotated',\n childId: childSessionId,\n });\n\n let absoluteExpiresAt;\n if (childRecord.absoluteExpiresAt) {\n absoluteExpiresAt =\n typeof childRecord.absoluteExpiresAt === 'string'\n ? childRecord.absoluteExpiresAt\n : childRecord.absoluteExpiresAt.toISOString();\n } else {\n absoluteExpiresAt = new Date(absolute).toISOString();\n }\n\n return {\n token: childToken,\n sessionId: childSessionId,\n absoluteExpiresAt,\n type: tokenType,\n };\n } catch {\n return { error: 'invalid_refresh_token' };\n }\n }\n\n /**\n * Returns true when a session exists and is not expired.\n * If the session exists but is expired, it will be deleted as part of this check.\n */\n async isSessionActive(sessionId: string, origin: string): Promise<boolean> {\n const session = await this.provider.findBySessionId(sessionId);\n if (!session) {\n return false;\n }\n\n if (session.origin !== origin) {\n return false;\n }\n\n if (new Date(session.expiresAt) <= new Date()) {\n // Clean up expired session eagerly\n await this.provider.deleteBySessionId(sessionId);\n\n return false;\n }\n\n return true;\n }\n}\n\nconst createDatabaseProvider = (db: Database, contentType: string): SessionProvider => {\n return new DatabaseSessionProvider(db, contentType);\n};\n\nconst createSessionManager = ({\n db,\n}: {\n db: Database;\n}): SessionManager & ((origin: string) => OriginSessionManager) => {\n const provider = createDatabaseProvider(db, 'admin::session');\n const sessionManager = new SessionManager(provider);\n\n // Add callable functionality\n const fluentApi = (origin: string): OriginSessionManager => {\n if (!origin || typeof origin !== 'string') {\n throw new Error(\n 'SessionManager: Origin parameter is required and must be a non-empty string'\n );\n }\n return new OriginSessionManager(sessionManager, origin);\n };\n\n // Attach only the public SessionManagerService API to the callable\n const api = fluentApi as unknown as any;\n api.generateSessionId = sessionManager.generateSessionId.bind(sessionManager);\n api.defineOrigin = sessionManager.defineOrigin.bind(sessionManager);\n api.hasOrigin = sessionManager.hasOrigin.bind(sessionManager);\n // Note: isSessionActive is origin-scoped and exposed on OriginSessionManager only\n\n // Forward the cleanupThreshold getter (used in tests)\n Object.defineProperty(api, 'cleanupThreshold', {\n get() {\n return sessionManager.cleanupThreshold;\n },\n enumerable: true,\n });\n\n return api as SessionManager & ((origin: string) => OriginSessionManager);\n};\n\nexport { createSessionManager, createDatabaseProvider };\n"],"names":["DatabaseSessionProvider","create","session","result","db","query","contentType","data","findBySessionId","sessionId","findOne","where","updateBySessionId","update","deleteBySessionId","delete","deleteExpired","deleteMany","absoluteExpiresAt","$lt","Date","deleteBy","criteria","userId","origin","deviceId","constructor","OriginSessionManager","generateRefreshToken","options","sessionManager","generateAccessToken","refreshToken","rotateRefreshToken","validateAccessToken","token","validateRefreshToken","invalidateRefreshToken","isSessionActive","SessionManager","defineOrigin","config","originConfigs","set","hasOrigin","has","getConfigForOrigin","originConfig","get","Error","algorithm","operation","isAsymmetric","startsWith","privateKey","jwtOptions","publicKey","jwtSecret","generateSessionId","crypto","randomBytes","toString","maybeCleanupExpired","cleanupInvocationCounter","cleanupEveryCalls","provider","cleanupThreshold","DEFAULT_ALGORITHM","jwtKey","getJwtKey","tokenType","type","isRefresh","idleLifespan","idleRefreshTokenLifespan","idleSessionLifespan","maxLifespan","maxRefreshTokenLifespan","maxSessionLifespan","now","expiresAt","record","childId","status","issuedAtSeconds","Math","floor","createdAt","getTime","expiresAtSeconds","payload","iat","exp","expiresIn","jwtSignOptions","jwt","sign","noTimestamp","toISOString","verify","algorithms","isValid","err","verifyOptions","error","JsonWebTokenError","validation","String","accessTokenLifespan","current","child","childIat","childExp","childPayload","childToken","absolute","childSessionId","childExpiresAt","childRecord","payloadOut","Map","createDatabaseProvider","createSessionManager","fluentApi","api","bind","Object","defineProperty","enumerable"],"mappings":"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"}