strapi-plugin-magic-sessionmanager 4.2.14 → 4.2.16

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.
@@ -4,8 +4,8 @@ import { useFetchClient } from "@strapi/strapi/admin";
4
4
  import styled, { css, keyframes } from "styled-components";
5
5
  import { Loader, Typography, Box, Flex, Badge } from "@strapi/design-system";
6
6
  import { ChartBubble, Crown, User, Clock, Monitor } from "@strapi/icons";
7
- import { a as pluginId } from "./index-DQO9bNP7.mjs";
8
- import { u as useLicense } from "./useLicense-DxbD4Wf8.mjs";
7
+ import { a as pluginId } from "./index-CUSrDKCG.mjs";
8
+ import { u as useLicense } from "./useLicense-xjKLHcVq.mjs";
9
9
  const theme = {
10
10
  colors: {
11
11
  primary: { 100: "#E0F2FE", 500: "#0EA5E9", 600: "#0284C7" },
@@ -6,8 +6,8 @@ const admin = require("@strapi/strapi/admin");
6
6
  const styled = require("styled-components");
7
7
  const designSystem = require("@strapi/design-system");
8
8
  const icons = require("@strapi/icons");
9
- const index = require("./index-CWcvrfXc.js");
10
- const useLicense = require("./useLicense-DtvJOszr.js");
9
+ const index = require("./index-BuxWeACw.js");
10
+ const useLicense = require("./useLicense-D7FSpX8c.js");
11
11
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
12
12
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
13
13
  const theme = {
@@ -5,10 +5,10 @@ const react = require("react");
5
5
  const reactIntl = require("react-intl");
6
6
  const admin = require("@strapi/strapi/admin");
7
7
  const styled = require("styled-components");
8
- const index = require("./index-CWcvrfXc.js");
8
+ const index = require("./index-BuxWeACw.js");
9
9
  const designSystem = require("@strapi/design-system");
10
10
  const icons = require("@strapi/icons");
11
- const useLicense = require("./useLicense-DtvJOszr.js");
11
+ const useLicense = require("./useLicense-D7FSpX8c.js");
12
12
  const reactRouterDom = require("react-router-dom");
13
13
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
14
14
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
@@ -3,10 +3,10 @@ import { useState, useEffect } from "react";
3
3
  import { useIntl } from "react-intl";
4
4
  import { useFetchClient, useNotification } from "@strapi/strapi/admin";
5
5
  import styled, { css, keyframes } from "styled-components";
6
- import { p as parseUserAgent, a as pluginId, g as getTranslation } from "./index-DQO9bNP7.mjs";
6
+ import { p as parseUserAgent, a as pluginId, g as getTranslation } from "./index-CUSrDKCG.mjs";
7
7
  import { Modal, Flex, Box, Typography, Badge, Divider, Button, Loader, SingleSelect, SingleSelectOption, Thead, Tr, Th, Tbody, Td, Table, TextInput } from "@strapi/design-system";
8
8
  import { Check, Information, Monitor, Server, Clock, Cross, Earth, Shield, Crown, Phone, Download, User, Eye, Trash, Search, Key } from "@strapi/icons";
9
- import { u as useLicense } from "./useLicense-DxbD4Wf8.mjs";
9
+ import { u as useLicense } from "./useLicense-xjKLHcVq.mjs";
10
10
  import { useNavigate } from "react-router-dom";
11
11
  const theme = {
12
12
  colors: {
@@ -4,7 +4,7 @@ import { Loader, Box, Alert, Flex, Typography, Button, Badge, Accordion } from "
4
4
  import { useFetchClient, useNotification } from "@strapi/strapi/admin";
5
5
  import { ArrowClockwise, Duplicate, Download, User, Shield, Sparkle, ChartBubble } from "@strapi/icons";
6
6
  import styled, { css, keyframes } from "styled-components";
7
- import { a as pluginId } from "./index-DQO9bNP7.mjs";
7
+ import { a as pluginId } from "./index-CUSrDKCG.mjs";
8
8
  const theme = {
9
9
  borderRadius: { lg: "12px" }
10
10
  };
@@ -6,7 +6,7 @@ const designSystem = require("@strapi/design-system");
6
6
  const admin = require("@strapi/strapi/admin");
7
7
  const icons = require("@strapi/icons");
8
8
  const styled = require("styled-components");
9
- const index = require("./index-CWcvrfXc.js");
9
+ const index = require("./index-BuxWeACw.js");
10
10
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
11
11
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
12
12
  const theme = {
@@ -4,7 +4,7 @@ import { useIntl } from "react-intl";
4
4
  import { Box, Typography, Flex, Grid } from "@strapi/design-system";
5
5
  import { Check, Cross, Clock, User } from "@strapi/icons";
6
6
  import { useFetchClient } from "@strapi/strapi/admin";
7
- import { g as getTranslation } from "./index-DQO9bNP7.mjs";
7
+ import { g as getTranslation } from "./index-CUSrDKCG.mjs";
8
8
  const OnlineUsersWidget = () => {
9
9
  const { formatMessage } = useIntl();
10
10
  const { get } = useFetchClient();
@@ -6,7 +6,7 @@ const reactIntl = require("react-intl");
6
6
  const designSystem = require("@strapi/design-system");
7
7
  const icons = require("@strapi/icons");
8
8
  const admin = require("@strapi/strapi/admin");
9
- const index = require("./index-CWcvrfXc.js");
9
+ const index = require("./index-BuxWeACw.js");
10
10
  const OnlineUsersWidget = () => {
11
11
  const { formatMessage } = reactIntl.useIntl();
12
12
  const { get } = admin.useFetchClient();
@@ -5,8 +5,8 @@ import { Flex, Loader, Typography, Button, Box, Badge, Accordion, Grid, SingleSe
5
5
  import { useFetchClient, useNotification } from "@strapi/strapi/admin";
6
6
  import { Check, Information, Cog, Trash, Shield, Code, Duplicate, Mail } from "@strapi/icons";
7
7
  import styled, { css, keyframes } from "styled-components";
8
- import { a as pluginId, g as getTranslation } from "./index-DQO9bNP7.mjs";
9
- import { u as useLicense } from "./useLicense-DxbD4Wf8.mjs";
8
+ import { a as pluginId, g as getTranslation } from "./index-CUSrDKCG.mjs";
9
+ import { u as useLicense } from "./useLicense-xjKLHcVq.mjs";
10
10
  const theme = {
11
11
  colors: {
12
12
  primary: { 600: "#0284C7", 700: "#075985", 100: "#E0F2FE", 50: "#F0F9FF" },
@@ -7,8 +7,8 @@ const designSystem = require("@strapi/design-system");
7
7
  const admin = require("@strapi/strapi/admin");
8
8
  const icons = require("@strapi/icons");
9
9
  const styled = require("styled-components");
10
- const index = require("./index-CWcvrfXc.js");
11
- const useLicense = require("./useLicense-DtvJOszr.js");
10
+ const index = require("./index-BuxWeACw.js");
11
+ const useLicense = require("./useLicense-D7FSpX8c.js");
12
12
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
13
13
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
14
14
  const theme = {
@@ -6,7 +6,7 @@ const admin = require("@strapi/strapi/admin");
6
6
  const styled = require("styled-components");
7
7
  const designSystem = require("@strapi/design-system");
8
8
  const icons = require("@strapi/icons");
9
- const index = require("./index-CWcvrfXc.js");
9
+ const index = require("./index-BuxWeACw.js");
10
10
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
11
11
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
12
12
  const Container = styled__default.default(designSystem.Box)`
@@ -4,7 +4,7 @@ import { useFetchClient, useNotification } from "@strapi/strapi/admin";
4
4
  import styled from "styled-components";
5
5
  import { Flex, Typography, Box, Badge, Button } from "@strapi/design-system";
6
6
  import { Check, Cross, Sparkle, Lightning, Rocket } from "@strapi/icons";
7
- import { a as pluginId } from "./index-DQO9bNP7.mjs";
7
+ import { a as pluginId } from "./index-CUSrDKCG.mjs";
8
8
  const Container = styled(Box)`
9
9
  padding: 32px;
10
10
  max-width: 1400px;
@@ -391,19 +391,19 @@ const name = pluginPkg.strapi.name;
391
391
  const index = {
392
392
  register(app) {
393
393
  app.addMenuLink({
394
- to: `/plugins/${pluginId}`,
394
+ to: `plugins/${pluginId}`,
395
395
  icon: PluginIcon,
396
396
  intlLabel: {
397
397
  id: `${pluginId}.plugin.name`,
398
398
  defaultMessage: pluginPkg.strapi.displayName
399
399
  },
400
- Component: () => Promise.resolve().then(() => require("./App-DDKYCjKw.js"))
400
+ Component: () => Promise.resolve().then(() => require("./App-BkaaNOpt.js"))
401
401
  });
402
402
  app.createSettingSection(
403
403
  {
404
404
  intlLabel: { id: `${pluginId}.settings.section`, defaultMessage: "Sessions" },
405
405
  id: pluginId,
406
- to: `/settings/${pluginId}`
406
+ to: pluginId
407
407
  },
408
408
  [
409
409
  {
@@ -412,8 +412,8 @@ const index = {
412
412
  defaultMessage: "Upgrade"
413
413
  },
414
414
  id: "upgrade",
415
- to: `/settings/${pluginId}/upgrade`,
416
- Component: () => Promise.resolve().then(() => require("./UpgradePage-Dssk8A0Z.js"))
415
+ to: `${pluginId}/upgrade`,
416
+ Component: () => Promise.resolve().then(() => require("./UpgradePage-Bwy_1m6f.js"))
417
417
  },
418
418
  {
419
419
  intlLabel: {
@@ -421,8 +421,8 @@ const index = {
421
421
  defaultMessage: "General"
422
422
  },
423
423
  id: "general",
424
- to: `/settings/${pluginId}/general`,
425
- Component: () => Promise.resolve().then(() => require("./Settings-DyEAuTNQ.js"))
424
+ to: `${pluginId}/general`,
425
+ Component: () => Promise.resolve().then(() => require("./Settings-jtZRw_VP.js"))
426
426
  },
427
427
  {
428
428
  intlLabel: {
@@ -430,8 +430,8 @@ const index = {
430
430
  defaultMessage: "Analytics"
431
431
  },
432
432
  id: "analytics",
433
- to: `/settings/${pluginId}/analytics`,
434
- Component: () => Promise.resolve().then(() => require("./Analytics-lw_JaOVy.js"))
433
+ to: `${pluginId}/analytics`,
434
+ Component: () => Promise.resolve().then(() => require("./Analytics-D6RGeWO5.js"))
435
435
  },
436
436
  {
437
437
  intlLabel: {
@@ -439,8 +439,8 @@ const index = {
439
439
  defaultMessage: "License"
440
440
  },
441
441
  id: "license",
442
- to: `/settings/${pluginId}/license`,
443
- Component: () => Promise.resolve().then(() => require("./License-Tk-6UfPl.js"))
442
+ to: `${pluginId}/license`,
443
+ Component: () => Promise.resolve().then(() => require("./License-C8VnKtV1.js"))
444
444
  }
445
445
  ]
446
446
  );
@@ -458,7 +458,7 @@ const index = {
458
458
  defaultMessage: "Online Users"
459
459
  },
460
460
  component: async () => {
461
- const component = await Promise.resolve().then(() => require("./OnlineUsersWidget-C1qTpsws.js"));
461
+ const component = await Promise.resolve().then(() => require("./OnlineUsersWidget-uJ6DZB_N.js"));
462
462
  return component.default;
463
463
  },
464
464
  id: "online-users-widget",
@@ -390,19 +390,19 @@ const name = pluginPkg.strapi.name;
390
390
  const index = {
391
391
  register(app) {
392
392
  app.addMenuLink({
393
- to: `/plugins/${pluginId}`,
393
+ to: `plugins/${pluginId}`,
394
394
  icon: PluginIcon,
395
395
  intlLabel: {
396
396
  id: `${pluginId}.plugin.name`,
397
397
  defaultMessage: pluginPkg.strapi.displayName
398
398
  },
399
- Component: () => import("./App-DJW1ZNl5.mjs")
399
+ Component: () => import("./App-CahdcIEB.mjs")
400
400
  });
401
401
  app.createSettingSection(
402
402
  {
403
403
  intlLabel: { id: `${pluginId}.settings.section`, defaultMessage: "Sessions" },
404
404
  id: pluginId,
405
- to: `/settings/${pluginId}`
405
+ to: pluginId
406
406
  },
407
407
  [
408
408
  {
@@ -411,8 +411,8 @@ const index = {
411
411
  defaultMessage: "Upgrade"
412
412
  },
413
413
  id: "upgrade",
414
- to: `/settings/${pluginId}/upgrade`,
415
- Component: () => import("./UpgradePage-cINvE9zY.mjs")
414
+ to: `${pluginId}/upgrade`,
415
+ Component: () => import("./UpgradePage-mqr6dLVY.mjs")
416
416
  },
417
417
  {
418
418
  intlLabel: {
@@ -420,8 +420,8 @@ const index = {
420
420
  defaultMessage: "General"
421
421
  },
422
422
  id: "general",
423
- to: `/settings/${pluginId}/general`,
424
- Component: () => import("./Settings-C9xvckgq.mjs")
423
+ to: `${pluginId}/general`,
424
+ Component: () => import("./Settings-DOUUwwxB.mjs")
425
425
  },
426
426
  {
427
427
  intlLabel: {
@@ -429,8 +429,8 @@ const index = {
429
429
  defaultMessage: "Analytics"
430
430
  },
431
431
  id: "analytics",
432
- to: `/settings/${pluginId}/analytics`,
433
- Component: () => import("./Analytics-DTE_zmRV.mjs")
432
+ to: `${pluginId}/analytics`,
433
+ Component: () => import("./Analytics-BC4jdzBT.mjs")
434
434
  },
435
435
  {
436
436
  intlLabel: {
@@ -438,8 +438,8 @@ const index = {
438
438
  defaultMessage: "License"
439
439
  },
440
440
  id: "license",
441
- to: `/settings/${pluginId}/license`,
442
- Component: () => import("./License-DaOFuImm.mjs")
441
+ to: `${pluginId}/license`,
442
+ Component: () => import("./License-BFx721o7.mjs")
443
443
  }
444
444
  ]
445
445
  );
@@ -457,7 +457,7 @@ const index = {
457
457
  defaultMessage: "Online Users"
458
458
  },
459
459
  component: async () => {
460
- const component = await import("./OnlineUsersWidget-CADphbXG.mjs");
460
+ const component = await import("./OnlineUsersWidget-r2ZgSnok.mjs");
461
461
  return component.default;
462
462
  },
463
463
  id: "online-users-widget",
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  const react = require("react");
3
3
  const admin = require("@strapi/strapi/admin");
4
- const index = require("./index-CWcvrfXc.js");
4
+ const index = require("./index-BuxWeACw.js");
5
5
  const useLicense = () => {
6
6
  const { get } = admin.useFetchClient();
7
7
  const [isPremium, setIsPremium] = react.useState(false);
@@ -1,6 +1,6 @@
1
1
  import { useState, useEffect } from "react";
2
2
  import { useFetchClient } from "@strapi/strapi/admin";
3
- import { a as pluginId } from "./index-DQO9bNP7.mjs";
3
+ import { a as pluginId } from "./index-CUSrDKCG.mjs";
4
4
  const useLicense = () => {
5
5
  const { get } = useFetchClient();
6
6
  const [isPremium, setIsPremium] = useState(false);
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
- const index = require("../_chunks/index-CWcvrfXc.js");
2
+ const index = require("../_chunks/index-BuxWeACw.js");
3
3
  module.exports = index.index;
@@ -1,4 +1,4 @@
1
- import { i } from "../_chunks/index-DQO9bNP7.mjs";
1
+ import { i } from "../_chunks/index-CUSrDKCG.mjs";
2
2
  export {
3
3
  i as default
4
4
  };
@@ -206,7 +206,7 @@ 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$4(token) {
209
+ function hashToken$3(token) {
210
210
  if (!token) return null;
211
211
  return crypto$1.createHash("sha256").update(token).digest("hex");
212
212
  }
@@ -214,113 +214,63 @@ var encryption = {
214
214
  encryptToken: encryptToken$2,
215
215
  decryptToken: decryptToken$3,
216
216
  generateSessionId: generateSessionId$1,
217
- hashToken: hashToken$4
217
+ hashToken: hashToken$3
218
218
  };
219
219
  const SESSION_UID$3 = "plugin::magic-sessionmanager.session";
220
- const { hashToken: hashToken$3 } = encryption;
221
- const lastTouchCache = /* @__PURE__ */ new Map();
222
- const CACHE_MAX_SIZE = 1e4;
223
- const CACHE_CLEANUP_AGE = 60 * 60 * 1e3;
224
- function cleanupOldCacheEntries() {
225
- if (lastTouchCache.size < CACHE_MAX_SIZE) return;
226
- const now = Date.now();
227
- const cutoff = now - CACHE_CLEANUP_AGE;
228
- for (const [key, timestamp] of lastTouchCache.entries()) {
229
- if (timestamp < cutoff) {
230
- lastTouchCache.delete(key);
231
- }
232
- }
220
+ const AUTH_PATTERNS = [
221
+ "/auth/",
222
+ // All /api/auth/* endpoints (login, logout, refresh, etc.)
223
+ "/magic-link/",
224
+ // All Magic-Link endpoints
225
+ "/passwordless/",
226
+ // Legacy passwordless endpoints
227
+ "/otp/",
228
+ // OTP endpoints (any plugin)
229
+ "/login",
230
+ // Any login endpoint
231
+ "/register",
232
+ // Any register endpoint
233
+ "/forgot-password",
234
+ // Password reset
235
+ "/reset-password"
236
+ // Password reset
237
+ ];
238
+ function isAuthEndpoint(path) {
239
+ return AUTH_PATTERNS.some((pattern) => path.includes(pattern));
233
240
  }
234
- var lastSeen = ({ strapi: strapi2 }) => {
241
+ var lastSeen = ({ strapi: strapi2, sessionService }) => {
235
242
  return async (ctx, next) => {
236
- const currentToken = ctx.request.headers.authorization?.replace("Bearer ", "");
237
- if (!currentToken) {
238
- await next();
239
- return;
240
- }
241
- const skipPaths = [
242
- "/admin",
243
- // Admin panel UI
244
- "/content-manager",
245
- // Content Manager
246
- "/content-type-builder",
247
- // Content-Type Builder
248
- "/upload",
249
- // Media Library
250
- "/i18n",
251
- // Internationalization
252
- "/users-permissions",
253
- // Users & Permissions settings
254
- "/email",
255
- // Email plugin
256
- "/_health",
257
- // Health check
258
- "/favicon.ico",
259
- // Static assets
260
- "/api/auth/local",
261
- // Login endpoint
262
- "/api/auth/register",
263
- // Registration endpoint
264
- "/api/auth/forgot-password",
265
- // Password reset
266
- "/api/auth/reset-password",
267
- // Password reset
268
- "/api/auth/logout",
269
- // Logout endpoint (handled separately)
270
- "/api/auth/refresh",
271
- // Refresh token (has own validation in bootstrap.js)
272
- "/api/connect",
273
- // OAuth providers
274
- "/api/magic-link"
275
- // Magic link auth (if using magic-link plugin)
276
- ];
277
- if (skipPaths.some((p) => ctx.path.startsWith(p))) {
278
- await next();
279
- return;
280
- }
281
- if (!ctx.path.startsWith("/api/")) {
243
+ if (isAuthEndpoint(ctx.path)) {
282
244
  await next();
283
245
  return;
284
246
  }
285
- let matchingSession = null;
286
- try {
287
- const currentTokenHash = hashToken$3(currentToken);
288
- matchingSession = await strapi2.documents(SESSION_UID$3).findFirst({
289
- filters: {
290
- tokenHash: currentTokenHash,
291
- isActive: true
292
- },
293
- populate: { user: { fields: ["documentId"] } }
294
- });
295
- if (matchingSession) {
296
- ctx.state.sessionId = matchingSession.documentId;
297
- ctx.state.currentSession = matchingSession;
298
- if (matchingSession.user?.documentId) {
299
- ctx.state.sessionUserId = matchingSession.user.documentId;
247
+ if (ctx.state.user && ctx.state.user.documentId) {
248
+ try {
249
+ const userId = ctx.state.user.documentId;
250
+ const activeSessions = await strapi2.documents(SESSION_UID$3).findMany({
251
+ filters: {
252
+ user: { documentId: userId },
253
+ isActive: true
254
+ },
255
+ limit: 1
256
+ });
257
+ if (!activeSessions || activeSessions.length === 0) {
258
+ strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Request blocked - session terminated or invalid (user: ${userId.substring(0, 8)}...)`);
259
+ return ctx.unauthorized("All sessions have been terminated. Please login again.");
300
260
  }
301
- } else {
302
- strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Request blocked - session terminated or invalid (token hash: ${currentTokenHash.substring(0, 8)}...)`);
303
- return ctx.unauthorized("This session has been terminated. Please login again.");
261
+ } catch (err) {
262
+ strapi2.log.debug("[magic-sessionmanager] Error checking active sessions:", err.message);
304
263
  }
305
- } catch (err) {
306
- strapi2.log.debug("[magic-sessionmanager] Error checking session:", err.message);
307
264
  }
308
265
  await next();
309
- if (matchingSession) {
266
+ if (ctx.state.user && ctx.state.user.documentId) {
310
267
  try {
311
- const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
312
- const rateLimit = config2.lastSeenRateLimit || 3e4;
313
- const now = Date.now();
314
- const lastTouch = lastTouchCache.get(matchingSession.documentId) || 0;
315
- if (now - lastTouch > rateLimit) {
316
- lastTouchCache.set(matchingSession.documentId, now);
317
- cleanupOldCacheEntries();
318
- await strapi2.documents(SESSION_UID$3).update({
319
- documentId: matchingSession.documentId,
320
- data: { lastActive: /* @__PURE__ */ new Date() }
321
- });
322
- strapi2.log.debug(`[magic-sessionmanager] [TOUCH] Session ${matchingSession.documentId} activity updated`);
323
- }
268
+ const userId = ctx.state.user.documentId;
269
+ const sessionId = ctx.state.sessionId;
270
+ await sessionService.touch({
271
+ userId,
272
+ sessionId
273
+ });
324
274
  } catch (err) {
325
275
  strapi2.log.debug("[magic-sessionmanager] Error updating lastSeen:", err.message);
326
276
  }
@@ -2484,7 +2434,7 @@ var session$1 = ({ strapi: strapi2 }) => {
2484
2434
  }
2485
2435
  };
2486
2436
  };
2487
- const version = "4.2.13";
2437
+ const version = "4.2.15";
2488
2438
  const require$$2 = {
2489
2439
  version
2490
2440
  };
@@ -202,7 +202,7 @@ 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$4(token) {
205
+ function hashToken$3(token) {
206
206
  if (!token) return null;
207
207
  return crypto$1.createHash("sha256").update(token).digest("hex");
208
208
  }
@@ -210,113 +210,63 @@ var encryption = {
210
210
  encryptToken: encryptToken$2,
211
211
  decryptToken: decryptToken$3,
212
212
  generateSessionId: generateSessionId$1,
213
- hashToken: hashToken$4
213
+ hashToken: hashToken$3
214
214
  };
215
215
  const SESSION_UID$3 = "plugin::magic-sessionmanager.session";
216
- const { hashToken: hashToken$3 } = encryption;
217
- const lastTouchCache = /* @__PURE__ */ new Map();
218
- const CACHE_MAX_SIZE = 1e4;
219
- const CACHE_CLEANUP_AGE = 60 * 60 * 1e3;
220
- function cleanupOldCacheEntries() {
221
- if (lastTouchCache.size < CACHE_MAX_SIZE) return;
222
- const now = Date.now();
223
- const cutoff = now - CACHE_CLEANUP_AGE;
224
- for (const [key, timestamp] of lastTouchCache.entries()) {
225
- if (timestamp < cutoff) {
226
- lastTouchCache.delete(key);
227
- }
228
- }
216
+ const AUTH_PATTERNS = [
217
+ "/auth/",
218
+ // All /api/auth/* endpoints (login, logout, refresh, etc.)
219
+ "/magic-link/",
220
+ // All Magic-Link endpoints
221
+ "/passwordless/",
222
+ // Legacy passwordless endpoints
223
+ "/otp/",
224
+ // OTP endpoints (any plugin)
225
+ "/login",
226
+ // Any login endpoint
227
+ "/register",
228
+ // Any register endpoint
229
+ "/forgot-password",
230
+ // Password reset
231
+ "/reset-password"
232
+ // Password reset
233
+ ];
234
+ function isAuthEndpoint(path) {
235
+ return AUTH_PATTERNS.some((pattern) => path.includes(pattern));
229
236
  }
230
- var lastSeen = ({ strapi: strapi2 }) => {
237
+ var lastSeen = ({ strapi: strapi2, sessionService }) => {
231
238
  return async (ctx, next) => {
232
- const currentToken = ctx.request.headers.authorization?.replace("Bearer ", "");
233
- if (!currentToken) {
234
- await next();
235
- return;
236
- }
237
- const skipPaths = [
238
- "/admin",
239
- // Admin panel UI
240
- "/content-manager",
241
- // Content Manager
242
- "/content-type-builder",
243
- // Content-Type Builder
244
- "/upload",
245
- // Media Library
246
- "/i18n",
247
- // Internationalization
248
- "/users-permissions",
249
- // Users & Permissions settings
250
- "/email",
251
- // Email plugin
252
- "/_health",
253
- // Health check
254
- "/favicon.ico",
255
- // Static assets
256
- "/api/auth/local",
257
- // Login endpoint
258
- "/api/auth/register",
259
- // Registration endpoint
260
- "/api/auth/forgot-password",
261
- // Password reset
262
- "/api/auth/reset-password",
263
- // Password reset
264
- "/api/auth/logout",
265
- // Logout endpoint (handled separately)
266
- "/api/auth/refresh",
267
- // Refresh token (has own validation in bootstrap.js)
268
- "/api/connect",
269
- // OAuth providers
270
- "/api/magic-link"
271
- // Magic link auth (if using magic-link plugin)
272
- ];
273
- if (skipPaths.some((p) => ctx.path.startsWith(p))) {
274
- await next();
275
- return;
276
- }
277
- if (!ctx.path.startsWith("/api/")) {
239
+ if (isAuthEndpoint(ctx.path)) {
278
240
  await next();
279
241
  return;
280
242
  }
281
- let matchingSession = null;
282
- try {
283
- const currentTokenHash = hashToken$3(currentToken);
284
- matchingSession = await strapi2.documents(SESSION_UID$3).findFirst({
285
- filters: {
286
- tokenHash: currentTokenHash,
287
- isActive: true
288
- },
289
- populate: { user: { fields: ["documentId"] } }
290
- });
291
- if (matchingSession) {
292
- ctx.state.sessionId = matchingSession.documentId;
293
- ctx.state.currentSession = matchingSession;
294
- if (matchingSession.user?.documentId) {
295
- ctx.state.sessionUserId = matchingSession.user.documentId;
243
+ if (ctx.state.user && ctx.state.user.documentId) {
244
+ try {
245
+ const userId = ctx.state.user.documentId;
246
+ const activeSessions = await strapi2.documents(SESSION_UID$3).findMany({
247
+ filters: {
248
+ user: { documentId: userId },
249
+ isActive: true
250
+ },
251
+ limit: 1
252
+ });
253
+ if (!activeSessions || activeSessions.length === 0) {
254
+ strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Request blocked - session terminated or invalid (user: ${userId.substring(0, 8)}...)`);
255
+ return ctx.unauthorized("All sessions have been terminated. Please login again.");
296
256
  }
297
- } else {
298
- strapi2.log.info(`[magic-sessionmanager] [BLOCKED] Request blocked - session terminated or invalid (token hash: ${currentTokenHash.substring(0, 8)}...)`);
299
- return ctx.unauthorized("This session has been terminated. Please login again.");
257
+ } catch (err) {
258
+ strapi2.log.debug("[magic-sessionmanager] Error checking active sessions:", err.message);
300
259
  }
301
- } catch (err) {
302
- strapi2.log.debug("[magic-sessionmanager] Error checking session:", err.message);
303
260
  }
304
261
  await next();
305
- if (matchingSession) {
262
+ if (ctx.state.user && ctx.state.user.documentId) {
306
263
  try {
307
- const config2 = strapi2.config.get("plugin::magic-sessionmanager") || {};
308
- const rateLimit = config2.lastSeenRateLimit || 3e4;
309
- const now = Date.now();
310
- const lastTouch = lastTouchCache.get(matchingSession.documentId) || 0;
311
- if (now - lastTouch > rateLimit) {
312
- lastTouchCache.set(matchingSession.documentId, now);
313
- cleanupOldCacheEntries();
314
- await strapi2.documents(SESSION_UID$3).update({
315
- documentId: matchingSession.documentId,
316
- data: { lastActive: /* @__PURE__ */ new Date() }
317
- });
318
- strapi2.log.debug(`[magic-sessionmanager] [TOUCH] Session ${matchingSession.documentId} activity updated`);
319
- }
264
+ const userId = ctx.state.user.documentId;
265
+ const sessionId = ctx.state.sessionId;
266
+ await sessionService.touch({
267
+ userId,
268
+ sessionId
269
+ });
320
270
  } catch (err) {
321
271
  strapi2.log.debug("[magic-sessionmanager] Error updating lastSeen:", err.message);
322
272
  }
@@ -2480,7 +2430,7 @@ var session$1 = ({ strapi: strapi2 }) => {
2480
2430
  }
2481
2431
  };
2482
2432
  };
2483
- const version = "4.2.13";
2433
+ const version = "4.2.15";
2484
2434
  const require$$2 = {
2485
2435
  version
2486
2436
  };
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.2.14",
2
+ "version": "4.2.16",
3
3
  "keywords": [
4
4
  "strapi",
5
5
  "strapi-plugin",