strapi-plugin-magic-sessionmanager 4.2.8 → 4.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -179,7 +179,7 @@ function encryptToken$2(token) {
179
179
  throw new Error("Failed to encrypt token");
180
180
  }
181
181
  }
182
- function decryptToken$4(encryptedToken) {
182
+ function decryptToken$3(encryptedToken) {
183
183
  if (!encryptedToken) return null;
184
184
  try {
185
185
  const key = getEncryptionKey();
@@ -206,15 +206,20 @@ function generateSessionId$1(userId) {
206
206
  const userHash = crypto$1.createHash("sha256").update(userId.toString()).digest("hex").substring(0, 8);
207
207
  return `sess_${timestamp}_${userHash}_${randomBytes}`;
208
208
  }
209
+ function hashToken$3(token) {
210
+ if (!token) return null;
211
+ return crypto$1.createHash("sha256").update(token).digest("hex");
212
+ }
209
213
  var encryption = {
210
214
  encryptToken: encryptToken$2,
211
- decryptToken: decryptToken$4,
212
- generateSessionId: generateSessionId$1
215
+ decryptToken: decryptToken$3,
216
+ generateSessionId: generateSessionId$1,
217
+ hashToken: hashToken$3
213
218
  };
214
219
  const SESSION_UID$3 = "plugin::magic-sessionmanager.session";
215
- const { decryptToken: decryptToken$3 } = encryption;
220
+ const { hashToken: hashToken$2 } = encryption;
216
221
  const lastTouchCache = /* @__PURE__ */ new Map();
217
- var lastSeen = ({ strapi: strapi2, sessionService }) => {
222
+ var lastSeen = ({ strapi: strapi2 }) => {
218
223
  return async (ctx, next) => {
219
224
  const currentToken = ctx.request.headers.authorization?.replace("Bearer ", "");
220
225
  if (!currentToken) {
@@ -227,58 +232,26 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
227
232
  return;
228
233
  }
229
234
  let matchingSession = null;
230
- let userId = null;
231
235
  try {
232
- if (ctx.state.user && ctx.state.user.documentId) {
233
- userId = ctx.state.user.documentId;
234
- const activeSessions = await strapi2.documents(SESSION_UID$3).findMany({
235
- filters: {
236
- user: { documentId: userId },
237
- isActive: true
238
- }
239
- });
240
- if (!activeSessions || activeSessions.length === 0) {
241
- strapi2.log.info(`[magic-sessionmanager] [BLOCKED] User ${userId} has no active sessions`);
242
- return ctx.unauthorized("All sessions have been terminated. Please login again.");
243
- }
244
- for (const session2 of activeSessions) {
245
- if (!session2.token) continue;
246
- try {
247
- const decrypted = decryptToken$3(session2.token);
248
- if (decrypted === currentToken) {
249
- matchingSession = session2;
250
- break;
251
- }
252
- } catch (err) {
253
- }
254
- }
255
- if (!matchingSession) {
256
- strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Session for user ${userId} has been terminated`);
257
- return ctx.unauthorized("This session has been terminated. Please login again.");
258
- }
259
- } else {
260
- const allActiveSessions = await strapi2.documents(SESSION_UID$3).findMany({
261
- filters: { isActive: true },
262
- populate: { user: { fields: ["documentId"] } },
263
- limit: 500
264
- // Reasonable limit for performance
265
- });
266
- for (const session2 of allActiveSessions) {
267
- if (!session2.token) continue;
268
- try {
269
- const decrypted = decryptToken$3(session2.token);
270
- if (decrypted === currentToken) {
271
- matchingSession = session2;
272
- userId = session2.user?.documentId;
273
- break;
274
- }
275
- } catch (err) {
276
- }
277
- }
278
- }
236
+ const currentTokenHash = hashToken$2(currentToken);
237
+ matchingSession = await strapi2.documents(SESSION_UID$3).findFirst({
238
+ filters: {
239
+ tokenHash: currentTokenHash,
240
+ isActive: true
241
+ },
242
+ populate: { user: { fields: ["documentId"] } }
243
+ });
279
244
  if (matchingSession) {
280
245
  ctx.state.sessionId = matchingSession.documentId;
281
246
  ctx.state.currentSession = matchingSession;
247
+ if (matchingSession.user?.documentId) {
248
+ ctx.state.sessionUserId = matchingSession.user.documentId;
249
+ }
250
+ } else {
251
+ if (ctx.state.user && ctx.state.user.documentId) {
252
+ strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Session terminated for user ${ctx.state.user.documentId}`);
253
+ return ctx.unauthorized("This session has been terminated. Please login again.");
254
+ }
282
255
  }
283
256
  } catch (err) {
284
257
  strapi2.log.debug("[magic-sessionmanager] Error checking session:", err.message);
@@ -305,7 +278,7 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
305
278
  };
306
279
  };
307
280
  const getClientIp = getClientIp_1;
308
- const { encryptToken: encryptToken$1, decryptToken: decryptToken$2 } = encryption;
281
+ const { encryptToken: encryptToken$1, decryptToken: decryptToken$2, hashToken: hashToken$1 } = encryption;
309
282
  const { createLogger: createLogger$3 } = logger;
310
283
  const SESSION_UID$2 = "plugin::magic-sessionmanager.session";
311
284
  const USER_UID$2 = "plugin::users-permissions.user";
@@ -313,6 +286,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
313
286
  const log = createLogger$3(strapi2);
314
287
  log.info("[START] Bootstrap starting...");
315
288
  try {
289
+ await ensureTokenHashIndex(strapi2, log);
316
290
  const licenseGuardService = strapi2.plugin("magic-sessionmanager").service("license-guard");
317
291
  setTimeout(async () => {
318
292
  const licenseStatus = await licenseGuardService.initialize();
@@ -589,11 +563,15 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
589
563
  if (matchingSession) {
590
564
  const encryptedToken = newAccessToken ? encryptToken$1(newAccessToken) : matchingSession.token;
591
565
  const encryptedRefreshToken = newRefreshToken ? encryptToken$1(newRefreshToken) : matchingSession.refreshToken;
566
+ const newTokenHash = newAccessToken ? hashToken$1(newAccessToken) : matchingSession.tokenHash;
567
+ const newRefreshTokenHash = newRefreshToken ? hashToken$1(newRefreshToken) : matchingSession.refreshTokenHash;
592
568
  await strapi2.documents(SESSION_UID$2).update({
593
569
  documentId: matchingSession.documentId,
594
570
  data: {
595
571
  token: encryptedToken,
572
+ tokenHash: newTokenHash,
596
573
  refreshToken: encryptedRefreshToken,
574
+ refreshTokenHash: newRefreshTokenHash,
597
575
  lastActive: /* @__PURE__ */ new Date()
598
576
  }
599
577
  });
@@ -607,7 +585,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
607
585
  });
608
586
  log.info("[SUCCESS] Refresh Token interceptor middleware mounted");
609
587
  strapi2.server.use(
610
- lastSeen({ strapi: strapi2, sessionService })
588
+ lastSeen({ strapi: strapi2 })
611
589
  );
612
590
  log.info("[SUCCESS] LastSeen middleware mounted");
613
591
  await ensureContentApiPermissions(strapi2, log);
@@ -665,6 +643,46 @@ async function ensureContentApiPermissions(strapi2, log) {
665
643
  log.warn("Please manually enable plugin permissions in Settings > Users & Permissions > Roles > Authenticated");
666
644
  }
667
645
  }
646
+ async function ensureTokenHashIndex(strapi2, log) {
647
+ try {
648
+ const knex = strapi2.db.connection;
649
+ const tableName = "magic_sessions";
650
+ const indexName = "idx_magic_sessions_token_hash";
651
+ const hasIndex = await knex.schema.hasTable(tableName).then(async (exists) => {
652
+ if (!exists) return false;
653
+ const dialect = strapi2.db.dialect.client;
654
+ if (dialect === "postgres") {
655
+ const result = await knex.raw(`
656
+ SELECT indexname FROM pg_indexes
657
+ WHERE tablename = ? AND indexname = ?
658
+ `, [tableName, indexName]);
659
+ return result.rows.length > 0;
660
+ } else if (dialect === "mysql" || dialect === "mysql2") {
661
+ const result = await knex.raw(`
662
+ SHOW INDEX FROM ${tableName} WHERE Key_name = ?
663
+ `, [indexName]);
664
+ return result[0].length > 0;
665
+ } else if (dialect === "sqlite" || dialect === "better-sqlite3") {
666
+ const result = await knex.raw(`
667
+ SELECT name FROM sqlite_master
668
+ WHERE type='index' AND name = ?
669
+ `, [indexName]);
670
+ return result.length > 0;
671
+ }
672
+ return false;
673
+ });
674
+ if (hasIndex) {
675
+ log.debug("[INDEX] tokenHash index already exists");
676
+ return;
677
+ }
678
+ await knex.schema.alterTable(tableName, (table) => {
679
+ table.index(["token_hash", "is_active"], indexName);
680
+ });
681
+ log.info("[INDEX] Created tokenHash index for O(1) session lookup");
682
+ } catch (err) {
683
+ log.debug("[INDEX] Could not create tokenHash index (will retry on next startup):", err.message);
684
+ }
685
+ }
668
686
  const { createLogger: createLogger$2 } = logger;
669
687
  var destroy$1 = async ({ strapi: strapi2 }) => {
670
688
  const log = createLogger$2(strapi2);
@@ -743,10 +761,19 @@ const attributes = {
743
761
  type: "text",
744
762
  "private": true
745
763
  },
764
+ tokenHash: {
765
+ type: "string",
766
+ configurable: false,
767
+ unique: false
768
+ },
746
769
  refreshToken: {
747
770
  type: "text",
748
771
  "private": true
749
772
  },
773
+ refreshTokenHash: {
774
+ type: "string",
775
+ configurable: false
776
+ },
750
777
  loginTime: {
751
778
  type: "datetime",
752
779
  required: true
@@ -1778,7 +1805,7 @@ var controllers$1 = {
1778
1805
  license,
1779
1806
  settings
1780
1807
  };
1781
- const { encryptToken, decryptToken, generateSessionId } = encryption;
1808
+ const { encryptToken, decryptToken, generateSessionId, hashToken } = encryption;
1782
1809
  const { createLogger: createLogger$1 } = logger;
1783
1810
  const SESSION_UID = "plugin::magic-sessionmanager.session";
1784
1811
  const USER_UID = "plugin::users-permissions.user";
@@ -1796,6 +1823,8 @@ var session$1 = ({ strapi: strapi2 }) => {
1796
1823
  const sessionId = generateSessionId(userId);
1797
1824
  const encryptedToken = token ? encryptToken(token) : null;
1798
1825
  const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
1826
+ const tokenHashValue = token ? hashToken(token) : null;
1827
+ const refreshTokenHashValue = refreshToken ? hashToken(refreshToken) : null;
1799
1828
  const session2 = await strapi2.documents(SESSION_UID).create({
1800
1829
  data: {
1801
1830
  user: userId,
@@ -1806,11 +1835,15 @@ var session$1 = ({ strapi: strapi2 }) => {
1806
1835
  lastActive: now,
1807
1836
  isActive: true,
1808
1837
  token: encryptedToken,
1809
- // [SUCCESS] Encrypted Access Token
1838
+ // Encrypted Access Token
1839
+ tokenHash: tokenHashValue,
1840
+ // SHA-256 hash for fast lookup
1810
1841
  refreshToken: encryptedRefreshToken,
1811
- // [SUCCESS] Encrypted Refresh Token
1842
+ // Encrypted Refresh Token
1843
+ refreshTokenHash: refreshTokenHashValue,
1844
+ // SHA-256 hash for fast lookup
1812
1845
  sessionId
1813
- // [SUCCESS] Unique identifier
1846
+ // Unique identifier
1814
1847
  }
1815
1848
  });
1816
1849
  log.info(`[SUCCESS] Session ${session2.documentId} (${sessionId}) created for user ${userId}`);
@@ -1888,9 +1921,9 @@ var session$1 = ({ strapi: strapi2 }) => {
1888
1921
  const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
1889
1922
  const timeSinceActive = now - lastActiveTime;
1890
1923
  const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
1891
- const { token, ...sessionWithoutToken } = session2;
1924
+ const { token, tokenHash, refreshToken, refreshTokenHash, ...safeSession } = session2;
1892
1925
  return {
1893
- ...sessionWithoutToken,
1926
+ ...safeSession,
1894
1927
  isTrulyActive,
1895
1928
  minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
1896
1929
  };
@@ -1919,9 +1952,9 @@ var session$1 = ({ strapi: strapi2 }) => {
1919
1952
  const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
1920
1953
  const timeSinceActive = now - lastActiveTime;
1921
1954
  const isTrulyActive = timeSinceActive < inactivityTimeout;
1922
- const { token, ...sessionWithoutToken } = session2;
1955
+ const { token, tokenHash, refreshToken, refreshTokenHash, ...safeSession } = session2;
1923
1956
  return {
1924
- ...sessionWithoutToken,
1957
+ ...safeSession,
1925
1958
  isTrulyActive,
1926
1959
  minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
1927
1960
  };
@@ -1958,9 +1991,9 @@ var session$1 = ({ strapi: strapi2 }) => {
1958
1991
  const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
1959
1992
  const timeSinceActive = now - lastActiveTime;
1960
1993
  const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
1961
- const { token, ...sessionWithoutToken } = session2;
1994
+ const { token, tokenHash, refreshToken, refreshTokenHash, ...safeSession } = session2;
1962
1995
  return {
1963
- ...sessionWithoutToken,
1996
+ ...safeSession,
1964
1997
  isTrulyActive,
1965
1998
  minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
1966
1999
  };
@@ -2077,7 +2110,7 @@ var session$1 = ({ strapi: strapi2 }) => {
2077
2110
  }
2078
2111
  };
2079
2112
  };
2080
- const version = "4.2.7";
2113
+ const version = "4.2.9";
2081
2114
  const require$$2 = {
2082
2115
  version
2083
2116
  };
@@ -175,7 +175,7 @@ function encryptToken$2(token) {
175
175
  throw new Error("Failed to encrypt token");
176
176
  }
177
177
  }
178
- function decryptToken$4(encryptedToken) {
178
+ function decryptToken$3(encryptedToken) {
179
179
  if (!encryptedToken) return null;
180
180
  try {
181
181
  const key = getEncryptionKey();
@@ -202,15 +202,20 @@ function generateSessionId$1(userId) {
202
202
  const userHash = crypto$1.createHash("sha256").update(userId.toString()).digest("hex").substring(0, 8);
203
203
  return `sess_${timestamp}_${userHash}_${randomBytes}`;
204
204
  }
205
+ function hashToken$3(token) {
206
+ if (!token) return null;
207
+ return crypto$1.createHash("sha256").update(token).digest("hex");
208
+ }
205
209
  var encryption = {
206
210
  encryptToken: encryptToken$2,
207
- decryptToken: decryptToken$4,
208
- generateSessionId: generateSessionId$1
211
+ decryptToken: decryptToken$3,
212
+ generateSessionId: generateSessionId$1,
213
+ hashToken: hashToken$3
209
214
  };
210
215
  const SESSION_UID$3 = "plugin::magic-sessionmanager.session";
211
- const { decryptToken: decryptToken$3 } = encryption;
216
+ const { hashToken: hashToken$2 } = encryption;
212
217
  const lastTouchCache = /* @__PURE__ */ new Map();
213
- var lastSeen = ({ strapi: strapi2, sessionService }) => {
218
+ var lastSeen = ({ strapi: strapi2 }) => {
214
219
  return async (ctx, next) => {
215
220
  const currentToken = ctx.request.headers.authorization?.replace("Bearer ", "");
216
221
  if (!currentToken) {
@@ -223,58 +228,26 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
223
228
  return;
224
229
  }
225
230
  let matchingSession = null;
226
- let userId = null;
227
231
  try {
228
- if (ctx.state.user && ctx.state.user.documentId) {
229
- userId = ctx.state.user.documentId;
230
- const activeSessions = await strapi2.documents(SESSION_UID$3).findMany({
231
- filters: {
232
- user: { documentId: userId },
233
- isActive: true
234
- }
235
- });
236
- if (!activeSessions || activeSessions.length === 0) {
237
- strapi2.log.info(`[magic-sessionmanager] [BLOCKED] User ${userId} has no active sessions`);
238
- return ctx.unauthorized("All sessions have been terminated. Please login again.");
239
- }
240
- for (const session2 of activeSessions) {
241
- if (!session2.token) continue;
242
- try {
243
- const decrypted = decryptToken$3(session2.token);
244
- if (decrypted === currentToken) {
245
- matchingSession = session2;
246
- break;
247
- }
248
- } catch (err) {
249
- }
250
- }
251
- if (!matchingSession) {
252
- strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Session for user ${userId} has been terminated`);
253
- return ctx.unauthorized("This session has been terminated. Please login again.");
254
- }
255
- } else {
256
- const allActiveSessions = await strapi2.documents(SESSION_UID$3).findMany({
257
- filters: { isActive: true },
258
- populate: { user: { fields: ["documentId"] } },
259
- limit: 500
260
- // Reasonable limit for performance
261
- });
262
- for (const session2 of allActiveSessions) {
263
- if (!session2.token) continue;
264
- try {
265
- const decrypted = decryptToken$3(session2.token);
266
- if (decrypted === currentToken) {
267
- matchingSession = session2;
268
- userId = session2.user?.documentId;
269
- break;
270
- }
271
- } catch (err) {
272
- }
273
- }
274
- }
232
+ const currentTokenHash = hashToken$2(currentToken);
233
+ matchingSession = await strapi2.documents(SESSION_UID$3).findFirst({
234
+ filters: {
235
+ tokenHash: currentTokenHash,
236
+ isActive: true
237
+ },
238
+ populate: { user: { fields: ["documentId"] } }
239
+ });
275
240
  if (matchingSession) {
276
241
  ctx.state.sessionId = matchingSession.documentId;
277
242
  ctx.state.currentSession = matchingSession;
243
+ if (matchingSession.user?.documentId) {
244
+ ctx.state.sessionUserId = matchingSession.user.documentId;
245
+ }
246
+ } else {
247
+ if (ctx.state.user && ctx.state.user.documentId) {
248
+ strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Session terminated for user ${ctx.state.user.documentId}`);
249
+ return ctx.unauthorized("This session has been terminated. Please login again.");
250
+ }
278
251
  }
279
252
  } catch (err) {
280
253
  strapi2.log.debug("[magic-sessionmanager] Error checking session:", err.message);
@@ -301,7 +274,7 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
301
274
  };
302
275
  };
303
276
  const getClientIp = getClientIp_1;
304
- const { encryptToken: encryptToken$1, decryptToken: decryptToken$2 } = encryption;
277
+ const { encryptToken: encryptToken$1, decryptToken: decryptToken$2, hashToken: hashToken$1 } = encryption;
305
278
  const { createLogger: createLogger$3 } = logger;
306
279
  const SESSION_UID$2 = "plugin::magic-sessionmanager.session";
307
280
  const USER_UID$2 = "plugin::users-permissions.user";
@@ -309,6 +282,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
309
282
  const log = createLogger$3(strapi2);
310
283
  log.info("[START] Bootstrap starting...");
311
284
  try {
285
+ await ensureTokenHashIndex(strapi2, log);
312
286
  const licenseGuardService = strapi2.plugin("magic-sessionmanager").service("license-guard");
313
287
  setTimeout(async () => {
314
288
  const licenseStatus = await licenseGuardService.initialize();
@@ -585,11 +559,15 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
585
559
  if (matchingSession) {
586
560
  const encryptedToken = newAccessToken ? encryptToken$1(newAccessToken) : matchingSession.token;
587
561
  const encryptedRefreshToken = newRefreshToken ? encryptToken$1(newRefreshToken) : matchingSession.refreshToken;
562
+ const newTokenHash = newAccessToken ? hashToken$1(newAccessToken) : matchingSession.tokenHash;
563
+ const newRefreshTokenHash = newRefreshToken ? hashToken$1(newRefreshToken) : matchingSession.refreshTokenHash;
588
564
  await strapi2.documents(SESSION_UID$2).update({
589
565
  documentId: matchingSession.documentId,
590
566
  data: {
591
567
  token: encryptedToken,
568
+ tokenHash: newTokenHash,
592
569
  refreshToken: encryptedRefreshToken,
570
+ refreshTokenHash: newRefreshTokenHash,
593
571
  lastActive: /* @__PURE__ */ new Date()
594
572
  }
595
573
  });
@@ -603,7 +581,7 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
603
581
  });
604
582
  log.info("[SUCCESS] Refresh Token interceptor middleware mounted");
605
583
  strapi2.server.use(
606
- lastSeen({ strapi: strapi2, sessionService })
584
+ lastSeen({ strapi: strapi2 })
607
585
  );
608
586
  log.info("[SUCCESS] LastSeen middleware mounted");
609
587
  await ensureContentApiPermissions(strapi2, log);
@@ -661,6 +639,46 @@ async function ensureContentApiPermissions(strapi2, log) {
661
639
  log.warn("Please manually enable plugin permissions in Settings > Users & Permissions > Roles > Authenticated");
662
640
  }
663
641
  }
642
+ async function ensureTokenHashIndex(strapi2, log) {
643
+ try {
644
+ const knex = strapi2.db.connection;
645
+ const tableName = "magic_sessions";
646
+ const indexName = "idx_magic_sessions_token_hash";
647
+ const hasIndex = await knex.schema.hasTable(tableName).then(async (exists) => {
648
+ if (!exists) return false;
649
+ const dialect = strapi2.db.dialect.client;
650
+ if (dialect === "postgres") {
651
+ const result = await knex.raw(`
652
+ SELECT indexname FROM pg_indexes
653
+ WHERE tablename = ? AND indexname = ?
654
+ `, [tableName, indexName]);
655
+ return result.rows.length > 0;
656
+ } else if (dialect === "mysql" || dialect === "mysql2") {
657
+ const result = await knex.raw(`
658
+ SHOW INDEX FROM ${tableName} WHERE Key_name = ?
659
+ `, [indexName]);
660
+ return result[0].length > 0;
661
+ } else if (dialect === "sqlite" || dialect === "better-sqlite3") {
662
+ const result = await knex.raw(`
663
+ SELECT name FROM sqlite_master
664
+ WHERE type='index' AND name = ?
665
+ `, [indexName]);
666
+ return result.length > 0;
667
+ }
668
+ return false;
669
+ });
670
+ if (hasIndex) {
671
+ log.debug("[INDEX] tokenHash index already exists");
672
+ return;
673
+ }
674
+ await knex.schema.alterTable(tableName, (table) => {
675
+ table.index(["token_hash", "is_active"], indexName);
676
+ });
677
+ log.info("[INDEX] Created tokenHash index for O(1) session lookup");
678
+ } catch (err) {
679
+ log.debug("[INDEX] Could not create tokenHash index (will retry on next startup):", err.message);
680
+ }
681
+ }
664
682
  const { createLogger: createLogger$2 } = logger;
665
683
  var destroy$1 = async ({ strapi: strapi2 }) => {
666
684
  const log = createLogger$2(strapi2);
@@ -739,10 +757,19 @@ const attributes = {
739
757
  type: "text",
740
758
  "private": true
741
759
  },
760
+ tokenHash: {
761
+ type: "string",
762
+ configurable: false,
763
+ unique: false
764
+ },
742
765
  refreshToken: {
743
766
  type: "text",
744
767
  "private": true
745
768
  },
769
+ refreshTokenHash: {
770
+ type: "string",
771
+ configurable: false
772
+ },
746
773
  loginTime: {
747
774
  type: "datetime",
748
775
  required: true
@@ -1774,7 +1801,7 @@ var controllers$1 = {
1774
1801
  license,
1775
1802
  settings
1776
1803
  };
1777
- const { encryptToken, decryptToken, generateSessionId } = encryption;
1804
+ const { encryptToken, decryptToken, generateSessionId, hashToken } = encryption;
1778
1805
  const { createLogger: createLogger$1 } = logger;
1779
1806
  const SESSION_UID = "plugin::magic-sessionmanager.session";
1780
1807
  const USER_UID = "plugin::users-permissions.user";
@@ -1792,6 +1819,8 @@ var session$1 = ({ strapi: strapi2 }) => {
1792
1819
  const sessionId = generateSessionId(userId);
1793
1820
  const encryptedToken = token ? encryptToken(token) : null;
1794
1821
  const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
1822
+ const tokenHashValue = token ? hashToken(token) : null;
1823
+ const refreshTokenHashValue = refreshToken ? hashToken(refreshToken) : null;
1795
1824
  const session2 = await strapi2.documents(SESSION_UID).create({
1796
1825
  data: {
1797
1826
  user: userId,
@@ -1802,11 +1831,15 @@ var session$1 = ({ strapi: strapi2 }) => {
1802
1831
  lastActive: now,
1803
1832
  isActive: true,
1804
1833
  token: encryptedToken,
1805
- // [SUCCESS] Encrypted Access Token
1834
+ // Encrypted Access Token
1835
+ tokenHash: tokenHashValue,
1836
+ // SHA-256 hash for fast lookup
1806
1837
  refreshToken: encryptedRefreshToken,
1807
- // [SUCCESS] Encrypted Refresh Token
1838
+ // Encrypted Refresh Token
1839
+ refreshTokenHash: refreshTokenHashValue,
1840
+ // SHA-256 hash for fast lookup
1808
1841
  sessionId
1809
- // [SUCCESS] Unique identifier
1842
+ // Unique identifier
1810
1843
  }
1811
1844
  });
1812
1845
  log.info(`[SUCCESS] Session ${session2.documentId} (${sessionId}) created for user ${userId}`);
@@ -1884,9 +1917,9 @@ var session$1 = ({ strapi: strapi2 }) => {
1884
1917
  const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
1885
1918
  const timeSinceActive = now - lastActiveTime;
1886
1919
  const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
1887
- const { token, ...sessionWithoutToken } = session2;
1920
+ const { token, tokenHash, refreshToken, refreshTokenHash, ...safeSession } = session2;
1888
1921
  return {
1889
- ...sessionWithoutToken,
1922
+ ...safeSession,
1890
1923
  isTrulyActive,
1891
1924
  minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
1892
1925
  };
@@ -1915,9 +1948,9 @@ var session$1 = ({ strapi: strapi2 }) => {
1915
1948
  const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
1916
1949
  const timeSinceActive = now - lastActiveTime;
1917
1950
  const isTrulyActive = timeSinceActive < inactivityTimeout;
1918
- const { token, ...sessionWithoutToken } = session2;
1951
+ const { token, tokenHash, refreshToken, refreshTokenHash, ...safeSession } = session2;
1919
1952
  return {
1920
- ...sessionWithoutToken,
1953
+ ...safeSession,
1921
1954
  isTrulyActive,
1922
1955
  minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
1923
1956
  };
@@ -1954,9 +1987,9 @@ var session$1 = ({ strapi: strapi2 }) => {
1954
1987
  const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
1955
1988
  const timeSinceActive = now - lastActiveTime;
1956
1989
  const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
1957
- const { token, ...sessionWithoutToken } = session2;
1990
+ const { token, tokenHash, refreshToken, refreshTokenHash, ...safeSession } = session2;
1958
1991
  return {
1959
- ...sessionWithoutToken,
1992
+ ...safeSession,
1960
1993
  isTrulyActive,
1961
1994
  minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
1962
1995
  };
@@ -2073,7 +2106,7 @@ var session$1 = ({ strapi: strapi2 }) => {
2073
2106
  }
2074
2107
  };
2075
2108
  };
2076
- const version = "4.2.7";
2109
+ const version = "4.2.9";
2077
2110
  const require$$2 = {
2078
2111
  version
2079
2112
  };
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.2.8",
2
+ "version": "4.2.10",
3
3
  "keywords": [
4
4
  "strapi",
5
5
  "strapi-plugin",