strapi-plugin-magic-sessionmanager 4.3.4 → 4.4.1

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.
@@ -329,21 +329,37 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
329
329
  limit: 1
330
330
  });
331
331
  if (!activeSessions || activeSessions.length === 0) {
332
- const allSessions = await strapi2.documents(SESSION_UID$4).findMany({
333
- filters: { user: { documentId: userDocId2 } },
334
- limit: 1,
335
- fields: ["isActive"]
332
+ const inactiveSessions = await strapi2.documents(SESSION_UID$4).findMany({
333
+ filters: {
334
+ user: { documentId: userDocId2 },
335
+ isActive: false
336
+ },
337
+ limit: 5,
338
+ fields: ["documentId", "terminatedManually", "lastActive"],
339
+ sort: [{ lastActive: "desc" }]
336
340
  });
337
- const hasInactiveSessions = allSessions?.some((s3) => s3.isActive === false);
338
- if (hasInactiveSessions) {
339
- strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Session terminated (user: ${userDocId2.substring(0, 8)}...)`);
340
- return ctx.unauthorized("Session has been terminated. Please login again.");
341
- }
342
- if (strictMode) {
343
- strapi2.log.info(`[magic-sessionmanager] [BLOCKED] No session exists (user: ${userDocId2.substring(0, 8)}..., strictMode)`);
344
- return ctx.unauthorized("No valid session. Please login again.");
341
+ if (inactiveSessions && inactiveSessions.length > 0) {
342
+ const manuallyTerminated = inactiveSessions.find((s3) => s3.terminatedManually === true);
343
+ if (manuallyTerminated) {
344
+ strapi2.log.info(`[magic-sessionmanager] [BLOCKED] User ${userDocId2.substring(0, 8)}... was manually logged out`);
345
+ return ctx.unauthorized("Session has been terminated. Please login again.");
346
+ }
347
+ const sessionToReactivate = inactiveSessions[0];
348
+ await strapi2.documents(SESSION_UID$4).update({
349
+ documentId: sessionToReactivate.documentId,
350
+ data: {
351
+ isActive: true,
352
+ lastActive: /* @__PURE__ */ new Date()
353
+ }
354
+ });
355
+ strapi2.log.info(`[magic-sessionmanager] [REACTIVATED] Session reactivated for user ${userDocId2.substring(0, 8)}...`);
356
+ } else {
357
+ if (strictMode) {
358
+ strapi2.log.info(`[magic-sessionmanager] [BLOCKED] No session exists (user: ${userDocId2.substring(0, 8)}..., strictMode)`);
359
+ return ctx.unauthorized("No valid session. Please login again.");
360
+ }
361
+ strapi2.log.warn(`[magic-sessionmanager] [WARN] No session for user ${userDocId2.substring(0, 8)}... (allowing)`);
345
362
  }
346
- strapi2.log.warn(`[magic-sessionmanager] [WARN] No session for user ${userDocId2.substring(0, 8)}... (allowing)`);
347
363
  }
348
364
  ctx.state.userDocumentId = userDocId2;
349
365
  }
@@ -793,6 +809,7 @@ async function registerSessionAwareAuthStrategy(strapi2, log) {
793
809
  const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
794
810
  const strictMode = config2.strictSessionEnforcement === true;
795
811
  try {
812
+ const tokenHashValue = hashToken$2(token);
796
813
  let userDocId = null;
797
814
  const user = await strapi2.entityService.findOne(
798
815
  "plugin::users-permissions.user",
@@ -804,43 +821,69 @@ async function registerSessionAwareAuthStrategy(strapi2, log) {
804
821
  strapi2.log.debug("[magic-sessionmanager] [JWT] No documentId found, allowing through");
805
822
  return decoded;
806
823
  }
807
- const activeSessions = await strapi2.documents(SESSION_UID$3).findMany({
824
+ const thisSession = await strapi2.documents(SESSION_UID$3).findFirst({
825
+ filters: {
826
+ user: { documentId: userDocId },
827
+ tokenHash: tokenHashValue
828
+ },
829
+ fields: ["documentId", "isActive", "terminatedManually", "lastActive"]
830
+ });
831
+ if (thisSession) {
832
+ if (thisSession.terminatedManually === true) {
833
+ strapi2.log.info(
834
+ `[magic-sessionmanager] [JWT-BLOCKED] Session was manually terminated (user: ${userDocId.substring(0, 8)}...)`
835
+ );
836
+ return null;
837
+ }
838
+ if (thisSession.isActive) {
839
+ return decoded;
840
+ }
841
+ await strapi2.documents(SESSION_UID$3).update({
842
+ documentId: thisSession.documentId,
843
+ data: {
844
+ isActive: true,
845
+ lastActive: /* @__PURE__ */ new Date()
846
+ }
847
+ });
848
+ strapi2.log.info(
849
+ `[magic-sessionmanager] [JWT-REACTIVATED] Session reactivated for user ${userDocId.substring(0, 8)}...`
850
+ );
851
+ return decoded;
852
+ }
853
+ const anyActiveSessions = await strapi2.documents(SESSION_UID$3).findMany({
808
854
  filters: {
809
855
  user: { documentId: userDocId },
810
856
  isActive: true
811
857
  },
812
858
  limit: 1
813
859
  });
814
- if (activeSessions && activeSessions.length > 0) {
860
+ if (anyActiveSessions && anyActiveSessions.length > 0) {
861
+ strapi2.log.debug(
862
+ `[magic-sessionmanager] [JWT] No session for token but user has other active sessions (allowing)`
863
+ );
815
864
  return decoded;
816
865
  }
817
- const allSessions = await strapi2.documents(SESSION_UID$3).findMany({
818
- filters: { user: { documentId: userDocId } },
819
- limit: 5,
820
- fields: ["isActive", "lastActive"]
866
+ const terminatedSessions = await strapi2.documents(SESSION_UID$3).findMany({
867
+ filters: {
868
+ user: { documentId: userDocId },
869
+ terminatedManually: true
870
+ },
871
+ limit: 1
821
872
  });
822
- const totalSessions = allSessions?.length || 0;
823
- const hasInactiveSessions = allSessions?.some((s3) => s3.isActive === false);
824
- if (hasInactiveSessions) {
873
+ if (terminatedSessions && terminatedSessions.length > 0) {
825
874
  strapi2.log.info(
826
- `[magic-sessionmanager] [JWT-BLOCKED] User ${userDocId.substring(0, 8)}... was logged out (${totalSessions} inactive sessions)`
875
+ `[magic-sessionmanager] [JWT-BLOCKED] User ${userDocId.substring(0, 8)}... has terminated sessions`
827
876
  );
828
877
  return null;
829
878
  }
830
- if (totalSessions === 0) {
831
- if (strictMode) {
832
- strapi2.log.info(
833
- `[magic-sessionmanager] [JWT-BLOCKED] No sessions exist for user ${userDocId.substring(0, 8)}... (strictMode enabled)`
834
- );
835
- return null;
836
- }
837
- strapi2.log.warn(
838
- `[magic-sessionmanager] [JWT-WARN] No session found for user ${userDocId.substring(0, 8)}... (allowing - session may not have been created)`
879
+ if (strictMode) {
880
+ strapi2.log.info(
881
+ `[magic-sessionmanager] [JWT-BLOCKED] No sessions exist for user ${userDocId.substring(0, 8)}... (strictMode)`
839
882
  );
840
- return decoded;
883
+ return null;
841
884
  }
842
885
  strapi2.log.warn(
843
- `[magic-sessionmanager] [JWT-ALLOW] Unexpected session state for user ${userDocId.substring(0, 8)}... (allowing)`
886
+ `[magic-sessionmanager] [JWT-WARN] No session for user ${userDocId.substring(0, 8)}... (allowing)`
844
887
  );
845
888
  return decoded;
846
889
  } catch (err) {
@@ -960,6 +1003,11 @@ const attributes = {
960
1003
  "default": true,
961
1004
  required: true
962
1005
  },
1006
+ terminatedManually: {
1007
+ type: "boolean",
1008
+ "default": false,
1009
+ required: false
1010
+ },
963
1011
  geoLocation: {
964
1012
  type: "json"
965
1013
  },
@@ -1099,6 +1147,15 @@ var admin$1 = {
1099
1147
  description: "Terminate a specific session (admin)"
1100
1148
  }
1101
1149
  },
1150
+ {
1151
+ method: "POST",
1152
+ path: "/sessions/:sessionId/simulate-timeout",
1153
+ handler: "session.simulateTimeout",
1154
+ config: {
1155
+ policies: ["admin::isAuthenticatedAdmin"],
1156
+ description: "Simulate session timeout for testing (sets isActive: false, terminatedManually: false)"
1157
+ }
1158
+ },
1102
1159
  {
1103
1160
  method: "DELETE",
1104
1161
  path: "/sessions/:sessionId",
@@ -1680,6 +1737,40 @@ var session$3 = {
1680
1737
  ctx.throw(500, "Error terminating session");
1681
1738
  }
1682
1739
  },
1740
+ /**
1741
+ * Simulate session timeout for testing (Admin action)
1742
+ * POST /magic-sessionmanager/sessions/:sessionId/simulate-timeout
1743
+ * Sets isActive: false, terminatedManually: false (as if cleanup job ran)
1744
+ * This allows testing session reactivation behavior
1745
+ */
1746
+ async simulateTimeout(ctx) {
1747
+ try {
1748
+ const { sessionId } = ctx.params;
1749
+ const session2 = await strapi.documents(SESSION_UID$2).findOne({
1750
+ documentId: sessionId
1751
+ });
1752
+ if (!session2) {
1753
+ return ctx.notFound("Session not found");
1754
+ }
1755
+ await strapi.documents(SESSION_UID$2).update({
1756
+ documentId: sessionId,
1757
+ data: {
1758
+ isActive: false,
1759
+ terminatedManually: false
1760
+ // This allows reactivation!
1761
+ }
1762
+ });
1763
+ strapi.log.info(`[magic-sessionmanager] [TEST] Session ${sessionId} simulated timeout (terminatedManually: false)`);
1764
+ ctx.body = {
1765
+ message: `Session ${sessionId} marked as timed out (reactivatable)`,
1766
+ success: true,
1767
+ terminatedManually: false
1768
+ };
1769
+ } catch (err) {
1770
+ strapi.log.error("[magic-sessionmanager] Error simulating timeout:", err);
1771
+ ctx.throw(500, "Error simulating session timeout");
1772
+ }
1773
+ },
1683
1774
  /**
1684
1775
  * Terminate a single session (Admin action)
1685
1776
  * POST /magic-sessionmanager/sessions/:sessionId/terminate
@@ -2221,10 +2312,11 @@ var session$1 = ({ strapi: strapi2 }) => {
2221
2312
  documentId: sessionId,
2222
2313
  data: {
2223
2314
  isActive: false,
2315
+ terminatedManually: true,
2224
2316
  logoutTime: now
2225
2317
  }
2226
2318
  });
2227
- log.info(`Session ${sessionId} terminated`);
2319
+ log.info(`Session ${sessionId} terminated (manual)`);
2228
2320
  } else if (userId) {
2229
2321
  let userDocumentId = userId;
2230
2322
  if (!isNaN(userId)) {
@@ -2245,11 +2337,12 @@ var session$1 = ({ strapi: strapi2 }) => {
2245
2337
  documentId: session2.documentId,
2246
2338
  data: {
2247
2339
  isActive: false,
2340
+ terminatedManually: true,
2248
2341
  logoutTime: now
2249
2342
  }
2250
2343
  });
2251
2344
  }
2252
- log.info(`All sessions terminated for user ${userDocumentId}`);
2345
+ log.info(`All sessions terminated (manual) for user ${userDocumentId}`);
2253
2346
  }
2254
2347
  } catch (err) {
2255
2348
  log.error("Error terminating session:", err);
@@ -2590,7 +2683,11 @@ var session$1 = ({ strapi: strapi2 }) => {
2590
2683
  if (lastActiveTime < cutoffTime) {
2591
2684
  await strapi2.documents(SESSION_UID$1).update({
2592
2685
  documentId: session2.documentId,
2593
- data: { isActive: false }
2686
+ data: {
2687
+ isActive: false,
2688
+ terminatedManually: false
2689
+ // Timeout, not manual - can be reactivated
2690
+ }
2594
2691
  });
2595
2692
  deactivatedCount++;
2596
2693
  }
@@ -2643,7 +2740,7 @@ var session$1 = ({ strapi: strapi2 }) => {
2643
2740
  }
2644
2741
  };
2645
2742
  };
2646
- const version$1 = "4.3.3";
2743
+ const version$1 = "4.4.0";
2647
2744
  const require$$2 = {
2648
2745
  version: version$1
2649
2746
  };
@@ -38979,17 +39076,35 @@ var sessionRequired$1 = async (policyContext, config2, { strapi: strapi2 }) => {
38979
39076
  if (activeSessions && activeSessions.length > 0) {
38980
39077
  return true;
38981
39078
  }
38982
- const allSessions = await strapi2.documents(SESSION_UID).findMany({
38983
- filters: { user: { documentId: userDocId } },
38984
- limit: 1,
38985
- fields: ["isActive"]
39079
+ const inactiveSessions = await strapi2.documents(SESSION_UID).findMany({
39080
+ filters: {
39081
+ user: { documentId: userDocId },
39082
+ isActive: false
39083
+ },
39084
+ limit: 5,
39085
+ fields: ["documentId", "terminatedManually", "lastActive"],
39086
+ sort: [{ lastActive: "desc" }]
38986
39087
  });
38987
- const hasInactiveSessions = allSessions?.some((s3) => s3.isActive === false);
38988
- if (hasInactiveSessions) {
39088
+ if (inactiveSessions && inactiveSessions.length > 0) {
39089
+ const manuallyTerminated = inactiveSessions.find((s3) => s3.terminatedManually === true);
39090
+ if (manuallyTerminated) {
39091
+ strapi2.log.info(
39092
+ `[magic-sessionmanager] [POLICY-BLOCKED] User ${userDocId.substring(0, 8)}... was manually logged out`
39093
+ );
39094
+ throw new errors.UnauthorizedError("Session terminated. Please login again.");
39095
+ }
39096
+ const sessionToReactivate = inactiveSessions[0];
39097
+ await strapi2.documents(SESSION_UID).update({
39098
+ documentId: sessionToReactivate.documentId,
39099
+ data: {
39100
+ isActive: true,
39101
+ lastActive: /* @__PURE__ */ new Date()
39102
+ }
39103
+ });
38989
39104
  strapi2.log.info(
38990
- `[magic-sessionmanager] [POLICY-BLOCKED] Session terminated (user: ${userDocId.substring(0, 8)}...)`
39105
+ `[magic-sessionmanager] [POLICY-REACTIVATED] Session reactivated for user ${userDocId.substring(0, 8)}...`
38991
39106
  );
38992
- throw new errors.UnauthorizedError("Session terminated. Please login again.");
39107
+ return true;
38993
39108
  }
38994
39109
  if (strictMode) {
38995
39110
  strapi2.log.info(
@@ -316,21 +316,37 @@ var lastSeen = ({ strapi: strapi2, sessionService }) => {
316
316
  limit: 1
317
317
  });
318
318
  if (!activeSessions || activeSessions.length === 0) {
319
- const allSessions = await strapi2.documents(SESSION_UID$4).findMany({
320
- filters: { user: { documentId: userDocId2 } },
321
- limit: 1,
322
- fields: ["isActive"]
319
+ const inactiveSessions = await strapi2.documents(SESSION_UID$4).findMany({
320
+ filters: {
321
+ user: { documentId: userDocId2 },
322
+ isActive: false
323
+ },
324
+ limit: 5,
325
+ fields: ["documentId", "terminatedManually", "lastActive"],
326
+ sort: [{ lastActive: "desc" }]
323
327
  });
324
- const hasInactiveSessions = allSessions?.some((s3) => s3.isActive === false);
325
- if (hasInactiveSessions) {
326
- strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Session terminated (user: ${userDocId2.substring(0, 8)}...)`);
327
- return ctx.unauthorized("Session has been terminated. Please login again.");
328
- }
329
- if (strictMode) {
330
- strapi2.log.info(`[magic-sessionmanager] [BLOCKED] No session exists (user: ${userDocId2.substring(0, 8)}..., strictMode)`);
331
- return ctx.unauthorized("No valid session. Please login again.");
328
+ if (inactiveSessions && inactiveSessions.length > 0) {
329
+ const manuallyTerminated = inactiveSessions.find((s3) => s3.terminatedManually === true);
330
+ if (manuallyTerminated) {
331
+ strapi2.log.info(`[magic-sessionmanager] [BLOCKED] User ${userDocId2.substring(0, 8)}... was manually logged out`);
332
+ return ctx.unauthorized("Session has been terminated. Please login again.");
333
+ }
334
+ const sessionToReactivate = inactiveSessions[0];
335
+ await strapi2.documents(SESSION_UID$4).update({
336
+ documentId: sessionToReactivate.documentId,
337
+ data: {
338
+ isActive: true,
339
+ lastActive: /* @__PURE__ */ new Date()
340
+ }
341
+ });
342
+ strapi2.log.info(`[magic-sessionmanager] [REACTIVATED] Session reactivated for user ${userDocId2.substring(0, 8)}...`);
343
+ } else {
344
+ if (strictMode) {
345
+ strapi2.log.info(`[magic-sessionmanager] [BLOCKED] No session exists (user: ${userDocId2.substring(0, 8)}..., strictMode)`);
346
+ return ctx.unauthorized("No valid session. Please login again.");
347
+ }
348
+ strapi2.log.warn(`[magic-sessionmanager] [WARN] No session for user ${userDocId2.substring(0, 8)}... (allowing)`);
332
349
  }
333
- strapi2.log.warn(`[magic-sessionmanager] [WARN] No session for user ${userDocId2.substring(0, 8)}... (allowing)`);
334
350
  }
335
351
  ctx.state.userDocumentId = userDocId2;
336
352
  }
@@ -780,6 +796,7 @@ async function registerSessionAwareAuthStrategy(strapi2, log) {
780
796
  const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
781
797
  const strictMode = config2.strictSessionEnforcement === true;
782
798
  try {
799
+ const tokenHashValue = hashToken$2(token);
783
800
  let userDocId = null;
784
801
  const user = await strapi2.entityService.findOne(
785
802
  "plugin::users-permissions.user",
@@ -791,43 +808,69 @@ async function registerSessionAwareAuthStrategy(strapi2, log) {
791
808
  strapi2.log.debug("[magic-sessionmanager] [JWT] No documentId found, allowing through");
792
809
  return decoded;
793
810
  }
794
- const activeSessions = await strapi2.documents(SESSION_UID$3).findMany({
811
+ const thisSession = await strapi2.documents(SESSION_UID$3).findFirst({
812
+ filters: {
813
+ user: { documentId: userDocId },
814
+ tokenHash: tokenHashValue
815
+ },
816
+ fields: ["documentId", "isActive", "terminatedManually", "lastActive"]
817
+ });
818
+ if (thisSession) {
819
+ if (thisSession.terminatedManually === true) {
820
+ strapi2.log.info(
821
+ `[magic-sessionmanager] [JWT-BLOCKED] Session was manually terminated (user: ${userDocId.substring(0, 8)}...)`
822
+ );
823
+ return null;
824
+ }
825
+ if (thisSession.isActive) {
826
+ return decoded;
827
+ }
828
+ await strapi2.documents(SESSION_UID$3).update({
829
+ documentId: thisSession.documentId,
830
+ data: {
831
+ isActive: true,
832
+ lastActive: /* @__PURE__ */ new Date()
833
+ }
834
+ });
835
+ strapi2.log.info(
836
+ `[magic-sessionmanager] [JWT-REACTIVATED] Session reactivated for user ${userDocId.substring(0, 8)}...`
837
+ );
838
+ return decoded;
839
+ }
840
+ const anyActiveSessions = await strapi2.documents(SESSION_UID$3).findMany({
795
841
  filters: {
796
842
  user: { documentId: userDocId },
797
843
  isActive: true
798
844
  },
799
845
  limit: 1
800
846
  });
801
- if (activeSessions && activeSessions.length > 0) {
847
+ if (anyActiveSessions && anyActiveSessions.length > 0) {
848
+ strapi2.log.debug(
849
+ `[magic-sessionmanager] [JWT] No session for token but user has other active sessions (allowing)`
850
+ );
802
851
  return decoded;
803
852
  }
804
- const allSessions = await strapi2.documents(SESSION_UID$3).findMany({
805
- filters: { user: { documentId: userDocId } },
806
- limit: 5,
807
- fields: ["isActive", "lastActive"]
853
+ const terminatedSessions = await strapi2.documents(SESSION_UID$3).findMany({
854
+ filters: {
855
+ user: { documentId: userDocId },
856
+ terminatedManually: true
857
+ },
858
+ limit: 1
808
859
  });
809
- const totalSessions = allSessions?.length || 0;
810
- const hasInactiveSessions = allSessions?.some((s3) => s3.isActive === false);
811
- if (hasInactiveSessions) {
860
+ if (terminatedSessions && terminatedSessions.length > 0) {
812
861
  strapi2.log.info(
813
- `[magic-sessionmanager] [JWT-BLOCKED] User ${userDocId.substring(0, 8)}... was logged out (${totalSessions} inactive sessions)`
862
+ `[magic-sessionmanager] [JWT-BLOCKED] User ${userDocId.substring(0, 8)}... has terminated sessions`
814
863
  );
815
864
  return null;
816
865
  }
817
- if (totalSessions === 0) {
818
- if (strictMode) {
819
- strapi2.log.info(
820
- `[magic-sessionmanager] [JWT-BLOCKED] No sessions exist for user ${userDocId.substring(0, 8)}... (strictMode enabled)`
821
- );
822
- return null;
823
- }
824
- strapi2.log.warn(
825
- `[magic-sessionmanager] [JWT-WARN] No session found for user ${userDocId.substring(0, 8)}... (allowing - session may not have been created)`
866
+ if (strictMode) {
867
+ strapi2.log.info(
868
+ `[magic-sessionmanager] [JWT-BLOCKED] No sessions exist for user ${userDocId.substring(0, 8)}... (strictMode)`
826
869
  );
827
- return decoded;
870
+ return null;
828
871
  }
829
872
  strapi2.log.warn(
830
- `[magic-sessionmanager] [JWT-ALLOW] Unexpected session state for user ${userDocId.substring(0, 8)}... (allowing)`
873
+ `[magic-sessionmanager] [JWT-WARN] No session for user ${userDocId.substring(0, 8)}... (allowing)`
831
874
  );
832
875
  return decoded;
833
876
  } catch (err) {
@@ -947,6 +990,11 @@ const attributes = {
947
990
  "default": true,
948
991
  required: true
949
992
  },
993
+ terminatedManually: {
994
+ type: "boolean",
995
+ "default": false,
996
+ required: false
997
+ },
950
998
  geoLocation: {
951
999
  type: "json"
952
1000
  },
@@ -1086,6 +1134,15 @@ var admin$1 = {
1086
1134
  description: "Terminate a specific session (admin)"
1087
1135
  }
1088
1136
  },
1137
+ {
1138
+ method: "POST",
1139
+ path: "/sessions/:sessionId/simulate-timeout",
1140
+ handler: "session.simulateTimeout",
1141
+ config: {
1142
+ policies: ["admin::isAuthenticatedAdmin"],
1143
+ description: "Simulate session timeout for testing (sets isActive: false, terminatedManually: false)"
1144
+ }
1145
+ },
1089
1146
  {
1090
1147
  method: "DELETE",
1091
1148
  path: "/sessions/:sessionId",
@@ -1667,6 +1724,40 @@ var session$3 = {
1667
1724
  ctx.throw(500, "Error terminating session");
1668
1725
  }
1669
1726
  },
1727
+ /**
1728
+ * Simulate session timeout for testing (Admin action)
1729
+ * POST /magic-sessionmanager/sessions/:sessionId/simulate-timeout
1730
+ * Sets isActive: false, terminatedManually: false (as if cleanup job ran)
1731
+ * This allows testing session reactivation behavior
1732
+ */
1733
+ async simulateTimeout(ctx) {
1734
+ try {
1735
+ const { sessionId } = ctx.params;
1736
+ const session2 = await strapi.documents(SESSION_UID$2).findOne({
1737
+ documentId: sessionId
1738
+ });
1739
+ if (!session2) {
1740
+ return ctx.notFound("Session not found");
1741
+ }
1742
+ await strapi.documents(SESSION_UID$2).update({
1743
+ documentId: sessionId,
1744
+ data: {
1745
+ isActive: false,
1746
+ terminatedManually: false
1747
+ // This allows reactivation!
1748
+ }
1749
+ });
1750
+ strapi.log.info(`[magic-sessionmanager] [TEST] Session ${sessionId} simulated timeout (terminatedManually: false)`);
1751
+ ctx.body = {
1752
+ message: `Session ${sessionId} marked as timed out (reactivatable)`,
1753
+ success: true,
1754
+ terminatedManually: false
1755
+ };
1756
+ } catch (err) {
1757
+ strapi.log.error("[magic-sessionmanager] Error simulating timeout:", err);
1758
+ ctx.throw(500, "Error simulating session timeout");
1759
+ }
1760
+ },
1670
1761
  /**
1671
1762
  * Terminate a single session (Admin action)
1672
1763
  * POST /magic-sessionmanager/sessions/:sessionId/terminate
@@ -2208,10 +2299,11 @@ var session$1 = ({ strapi: strapi2 }) => {
2208
2299
  documentId: sessionId,
2209
2300
  data: {
2210
2301
  isActive: false,
2302
+ terminatedManually: true,
2211
2303
  logoutTime: now
2212
2304
  }
2213
2305
  });
2214
- log.info(`Session ${sessionId} terminated`);
2306
+ log.info(`Session ${sessionId} terminated (manual)`);
2215
2307
  } else if (userId) {
2216
2308
  let userDocumentId = userId;
2217
2309
  if (!isNaN(userId)) {
@@ -2232,11 +2324,12 @@ var session$1 = ({ strapi: strapi2 }) => {
2232
2324
  documentId: session2.documentId,
2233
2325
  data: {
2234
2326
  isActive: false,
2327
+ terminatedManually: true,
2235
2328
  logoutTime: now
2236
2329
  }
2237
2330
  });
2238
2331
  }
2239
- log.info(`All sessions terminated for user ${userDocumentId}`);
2332
+ log.info(`All sessions terminated (manual) for user ${userDocumentId}`);
2240
2333
  }
2241
2334
  } catch (err) {
2242
2335
  log.error("Error terminating session:", err);
@@ -2577,7 +2670,11 @@ var session$1 = ({ strapi: strapi2 }) => {
2577
2670
  if (lastActiveTime < cutoffTime) {
2578
2671
  await strapi2.documents(SESSION_UID$1).update({
2579
2672
  documentId: session2.documentId,
2580
- data: { isActive: false }
2673
+ data: {
2674
+ isActive: false,
2675
+ terminatedManually: false
2676
+ // Timeout, not manual - can be reactivated
2677
+ }
2581
2678
  });
2582
2679
  deactivatedCount++;
2583
2680
  }
@@ -2630,7 +2727,7 @@ var session$1 = ({ strapi: strapi2 }) => {
2630
2727
  }
2631
2728
  };
2632
2729
  };
2633
- const version$1 = "4.3.3";
2730
+ const version$1 = "4.4.0";
2634
2731
  const require$$2 = {
2635
2732
  version: version$1
2636
2733
  };
@@ -38966,17 +39063,35 @@ var sessionRequired$1 = async (policyContext, config2, { strapi: strapi2 }) => {
38966
39063
  if (activeSessions && activeSessions.length > 0) {
38967
39064
  return true;
38968
39065
  }
38969
- const allSessions = await strapi2.documents(SESSION_UID).findMany({
38970
- filters: { user: { documentId: userDocId } },
38971
- limit: 1,
38972
- fields: ["isActive"]
39066
+ const inactiveSessions = await strapi2.documents(SESSION_UID).findMany({
39067
+ filters: {
39068
+ user: { documentId: userDocId },
39069
+ isActive: false
39070
+ },
39071
+ limit: 5,
39072
+ fields: ["documentId", "terminatedManually", "lastActive"],
39073
+ sort: [{ lastActive: "desc" }]
38973
39074
  });
38974
- const hasInactiveSessions = allSessions?.some((s3) => s3.isActive === false);
38975
- if (hasInactiveSessions) {
39075
+ if (inactiveSessions && inactiveSessions.length > 0) {
39076
+ const manuallyTerminated = inactiveSessions.find((s3) => s3.terminatedManually === true);
39077
+ if (manuallyTerminated) {
39078
+ strapi2.log.info(
39079
+ `[magic-sessionmanager] [POLICY-BLOCKED] User ${userDocId.substring(0, 8)}... was manually logged out`
39080
+ );
39081
+ throw new errors.UnauthorizedError("Session terminated. Please login again.");
39082
+ }
39083
+ const sessionToReactivate = inactiveSessions[0];
39084
+ await strapi2.documents(SESSION_UID).update({
39085
+ documentId: sessionToReactivate.documentId,
39086
+ data: {
39087
+ isActive: true,
39088
+ lastActive: /* @__PURE__ */ new Date()
39089
+ }
39090
+ });
38976
39091
  strapi2.log.info(
38977
- `[magic-sessionmanager] [POLICY-BLOCKED] Session terminated (user: ${userDocId.substring(0, 8)}...)`
39092
+ `[magic-sessionmanager] [POLICY-REACTIVATED] Session reactivated for user ${userDocId.substring(0, 8)}...`
38978
39093
  );
38979
- throw new errors.UnauthorizedError("Session terminated. Please login again.");
39094
+ return true;
38980
39095
  }
38981
39096
  if (strictMode) {
38982
39097
  strapi2.log.info(
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.3.4",
2
+ "version": "4.4.1",
3
3
  "keywords": [
4
4
  "strapi",
5
5
  "strapi-plugin",