strapi-plugin-magic-sessionmanager 3.6.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -0
- package/admin/src/hooks/useLicense.js +1 -1
- package/admin/src/index.js +5 -2
- package/admin/src/pages/Settings.jsx +0 -1
- package/admin/src/translations/es.json +21 -0
- package/admin/src/translations/fr.json +21 -0
- package/admin/src/translations/pt.json +21 -0
- package/admin/src/utils/parseUserAgent.js +1 -1
- package/dist/_chunks/{Analytics-DRzCKaDF.js → Analytics-ioaeEh-E.js} +2 -2
- package/dist/_chunks/{Analytics-CwyLwdOZ.mjs → Analytics-mYu_uGwU.mjs} +2 -2
- package/dist/_chunks/{App-Zhs_vt59.mjs → App-BXpIS12l.mjs} +2 -2
- package/dist/_chunks/{App-nGu2Eb87.js → App-DdnUYWbC.js} +2 -2
- package/dist/_chunks/{License-CPI0p_W8.mjs → License-C03C2j9P.mjs} +1 -1
- package/dist/_chunks/{License-k5vvhgKr.js → License-DZYrOgcx.js} +1 -1
- package/dist/_chunks/{Settings-CL2im8M3.mjs → Settings-0ocB3qHk.mjs} +2 -2
- package/dist/_chunks/{Settings-Lkmxisuv.js → Settings-C6_CqpCC.js} +2 -2
- package/dist/_chunks/es-CuLHazN1.js +23 -0
- package/dist/_chunks/es-Dkmjhy9c.mjs +23 -0
- package/dist/_chunks/fr-BAJp2yhI.js +23 -0
- package/dist/_chunks/fr-Bssg_3UF.mjs +23 -0
- package/dist/_chunks/{index-B-0VPfeF.mjs → index-DBRS3kt5.mjs} +11 -8
- package/dist/_chunks/{index-W_QbTAYU.js → index-DC8Y0qxx.js} +11 -8
- package/dist/_chunks/pt-BAP9cKs3.js +23 -0
- package/dist/_chunks/pt-BVNoNcuY.mjs +23 -0
- package/dist/_chunks/{useLicense-DUGjNbQ9.mjs → useLicense-DSLL9n3Y.mjs} +2 -2
- package/dist/_chunks/{useLicense-C_Rneohy.js → useLicense-qgGfMvse.js} +2 -2
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +117 -95
- package/dist/server/index.mjs +117 -95
- package/package.json +1 -1
- package/server/src/bootstrap.js +32 -26
- package/server/src/controllers/license.js +4 -4
- package/server/src/controllers/session.js +10 -6
- package/server/src/destroy.js +1 -1
- package/server/src/middlewares/last-seen.js +8 -3
- package/server/src/register.js +4 -4
- package/server/src/services/geolocation.js +4 -2
- package/server/src/services/license-guard.js +13 -10
- package/server/src/services/notifications.js +10 -10
- package/server/src/services/service.js +1 -1
- package/server/src/services/session.js +41 -31
- package/server/src/utils/encryption.js +1 -1
|
@@ -9,11 +9,17 @@ const { encryptToken, decryptToken, generateSessionId } = require('../utils/encr
|
|
|
9
9
|
*
|
|
10
10
|
* SECURITY: JWT tokens are encrypted before storing in database using AES-256-GCM
|
|
11
11
|
*
|
|
12
|
+
* [SUCCESS] Migrated to strapi.documents() API (Strapi v5 Best Practice)
|
|
13
|
+
*
|
|
12
14
|
* TODO: For production multi-instance deployments, use Redis for:
|
|
13
15
|
* - Session store instead of DB
|
|
14
16
|
* - Rate limiting locks
|
|
15
17
|
* - Distributed session state
|
|
16
18
|
*/
|
|
19
|
+
|
|
20
|
+
const SESSION_UID = 'plugin::magic-sessionmanager.session';
|
|
21
|
+
const USER_UID = 'plugin::users-permissions.user';
|
|
22
|
+
|
|
17
23
|
module.exports = ({ strapi }) => ({
|
|
18
24
|
/**
|
|
19
25
|
* Create a new session record
|
|
@@ -31,21 +37,22 @@ module.exports = ({ strapi }) => ({
|
|
|
31
37
|
const encryptedToken = token ? encryptToken(token) : null;
|
|
32
38
|
const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
// Using Document Service API (Strapi v5)
|
|
41
|
+
const session = await strapi.documents(SESSION_UID).create({
|
|
35
42
|
data: {
|
|
36
|
-
user: userId,
|
|
43
|
+
user: userId, // userId should be documentId (string)
|
|
37
44
|
ipAddress: ip.substring(0, 45),
|
|
38
45
|
userAgent: userAgent.substring(0, 500),
|
|
39
46
|
loginTime: now,
|
|
40
47
|
lastActive: now,
|
|
41
48
|
isActive: true,
|
|
42
|
-
token: encryptedToken, //
|
|
43
|
-
refreshToken: encryptedRefreshToken, //
|
|
44
|
-
sessionId: sessionId, //
|
|
49
|
+
token: encryptedToken, // [SUCCESS] Encrypted Access Token
|
|
50
|
+
refreshToken: encryptedRefreshToken, // [SUCCESS] Encrypted Refresh Token
|
|
51
|
+
sessionId: sessionId, // [SUCCESS] Unique identifier
|
|
45
52
|
},
|
|
46
53
|
});
|
|
47
54
|
|
|
48
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
55
|
+
strapi.log.info(`[magic-sessionmanager] [SUCCESS] Session ${session.documentId} (${sessionId}) created for user ${userId}`);
|
|
49
56
|
|
|
50
57
|
return session;
|
|
51
58
|
} catch (err) {
|
|
@@ -64,7 +71,9 @@ module.exports = ({ strapi }) => ({
|
|
|
64
71
|
const now = new Date();
|
|
65
72
|
|
|
66
73
|
if (sessionId) {
|
|
67
|
-
|
|
74
|
+
// Using Document Service API (Strapi v5)
|
|
75
|
+
await strapi.documents(SESSION_UID).update({
|
|
76
|
+
documentId: sessionId,
|
|
68
77
|
data: {
|
|
69
78
|
isActive: false,
|
|
70
79
|
logoutTime: now,
|
|
@@ -73,17 +82,18 @@ module.exports = ({ strapi }) => ({
|
|
|
73
82
|
|
|
74
83
|
strapi.log.info(`[magic-sessionmanager] Session ${sessionId} terminated`);
|
|
75
84
|
} else if (userId) {
|
|
76
|
-
// Find all active sessions for user
|
|
77
|
-
const activeSessions = await strapi.
|
|
85
|
+
// Find all active sessions for user - use Deep Filtering (Strapi v5)
|
|
86
|
+
const activeSessions = await strapi.documents(SESSION_UID).findMany({
|
|
78
87
|
filters: {
|
|
79
|
-
user: {
|
|
88
|
+
user: { documentId: userId }, // Deep filtering syntax
|
|
80
89
|
isActive: true,
|
|
81
90
|
},
|
|
82
91
|
});
|
|
83
92
|
|
|
84
93
|
// Terminate all active sessions
|
|
85
94
|
for (const session of activeSessions) {
|
|
86
|
-
await strapi.
|
|
95
|
+
await strapi.documents(SESSION_UID).update({
|
|
96
|
+
documentId: session.documentId,
|
|
87
97
|
data: {
|
|
88
98
|
isActive: false,
|
|
89
99
|
logoutTime: now,
|
|
@@ -105,7 +115,7 @@ module.exports = ({ strapi }) => ({
|
|
|
105
115
|
*/
|
|
106
116
|
async getAllSessions() {
|
|
107
117
|
try {
|
|
108
|
-
const sessions = await strapi.
|
|
118
|
+
const sessions = await strapi.documents(SESSION_UID).findMany( {
|
|
109
119
|
populate: { user: { fields: ['id', 'email', 'username'] } },
|
|
110
120
|
sort: { loginTime: 'desc' },
|
|
111
121
|
limit: 1000, // Reasonable limit
|
|
@@ -147,7 +157,7 @@ module.exports = ({ strapi }) => ({
|
|
|
147
157
|
*/
|
|
148
158
|
async getActiveSessions() {
|
|
149
159
|
try {
|
|
150
|
-
const sessions = await strapi.
|
|
160
|
+
const sessions = await strapi.documents(SESSION_UID).findMany( {
|
|
151
161
|
filters: { isActive: true },
|
|
152
162
|
populate: { user: { fields: ['id', 'email', 'username'] } },
|
|
153
163
|
sort: { loginTime: 'desc' },
|
|
@@ -191,8 +201,8 @@ module.exports = ({ strapi }) => ({
|
|
|
191
201
|
*/
|
|
192
202
|
async getUserSessions(userId) {
|
|
193
203
|
try {
|
|
194
|
-
const sessions = await strapi.
|
|
195
|
-
filters: { user: {
|
|
204
|
+
const sessions = await strapi.documents(SESSION_UID).findMany( {
|
|
205
|
+
filters: { user: { documentId: userId } },
|
|
196
206
|
sort: { loginTime: 'desc' },
|
|
197
207
|
});
|
|
198
208
|
|
|
@@ -241,20 +251,20 @@ module.exports = ({ strapi }) => ({
|
|
|
241
251
|
|
|
242
252
|
// Update session lastActive only
|
|
243
253
|
if (sessionId) {
|
|
244
|
-
const session = await strapi.
|
|
254
|
+
const session = await strapi.documents(SESSION_UID).findOne({ documentId: sessionId });
|
|
245
255
|
|
|
246
256
|
if (session && session.lastActive) {
|
|
247
257
|
const lastActiveTime = new Date(session.lastActive).getTime();
|
|
248
258
|
const currentTime = now.getTime();
|
|
249
259
|
|
|
250
260
|
if (currentTime - lastActiveTime > rateLimit) {
|
|
251
|
-
await strapi.
|
|
261
|
+
await strapi.documents(SESSION_UID).update({ documentId: sessionId,
|
|
252
262
|
data: { lastActive: now },
|
|
253
263
|
});
|
|
254
264
|
}
|
|
255
265
|
} else if (session) {
|
|
256
266
|
// First time or null
|
|
257
|
-
await strapi.
|
|
267
|
+
await strapi.documents(SESSION_UID).update({ documentId: sessionId,
|
|
258
268
|
data: { lastActive: now },
|
|
259
269
|
});
|
|
260
270
|
}
|
|
@@ -279,12 +289,12 @@ module.exports = ({ strapi }) => ({
|
|
|
279
289
|
const now = new Date();
|
|
280
290
|
const cutoffTime = new Date(now.getTime() - inactivityTimeout);
|
|
281
291
|
|
|
282
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
292
|
+
strapi.log.info(`[magic-sessionmanager] [CLEANUP] Cleaning up sessions inactive since before ${cutoffTime.toISOString()}`);
|
|
283
293
|
|
|
284
294
|
// Find all active sessions
|
|
285
|
-
const activeSessions = await strapi.
|
|
295
|
+
const activeSessions = await strapi.documents(SESSION_UID).findMany({
|
|
286
296
|
filters: { isActive: true },
|
|
287
|
-
fields: ['
|
|
297
|
+
fields: ['lastActive', 'loginTime'],
|
|
288
298
|
});
|
|
289
299
|
|
|
290
300
|
// Deactivate old sessions
|
|
@@ -293,14 +303,15 @@ module.exports = ({ strapi }) => ({
|
|
|
293
303
|
const lastActiveTime = session.lastActive ? new Date(session.lastActive) : new Date(session.loginTime);
|
|
294
304
|
|
|
295
305
|
if (lastActiveTime < cutoffTime) {
|
|
296
|
-
await strapi.
|
|
306
|
+
await strapi.documents(SESSION_UID).update({
|
|
307
|
+
documentId: session.documentId,
|
|
297
308
|
data: { isActive: false },
|
|
298
309
|
});
|
|
299
310
|
deactivatedCount++;
|
|
300
311
|
}
|
|
301
312
|
}
|
|
302
313
|
|
|
303
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
314
|
+
strapi.log.info(`[magic-sessionmanager] [SUCCESS] Cleanup complete: ${deactivatedCount} sessions deactivated`);
|
|
304
315
|
return deactivatedCount;
|
|
305
316
|
} catch (err) {
|
|
306
317
|
strapi.log.error('[magic-sessionmanager] Error cleaning up inactive sessions:', err);
|
|
@@ -316,8 +327,8 @@ module.exports = ({ strapi }) => ({
|
|
|
316
327
|
*/
|
|
317
328
|
async deleteSession(sessionId) {
|
|
318
329
|
try {
|
|
319
|
-
await strapi.
|
|
320
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
330
|
+
await strapi.documents(SESSION_UID).delete({ documentId: sessionId });
|
|
331
|
+
strapi.log.info(`[magic-sessionmanager] [DELETE] Session ${sessionId} permanently deleted`);
|
|
321
332
|
return true;
|
|
322
333
|
} catch (err) {
|
|
323
334
|
strapi.log.error('[magic-sessionmanager] Error deleting session:', err);
|
|
@@ -332,23 +343,22 @@ module.exports = ({ strapi }) => ({
|
|
|
332
343
|
*/
|
|
333
344
|
async deleteInactiveSessions() {
|
|
334
345
|
try {
|
|
335
|
-
strapi.log.info('[magic-sessionmanager]
|
|
346
|
+
strapi.log.info('[magic-sessionmanager] [DELETE] Deleting all inactive sessions...');
|
|
336
347
|
|
|
337
|
-
// Find all inactive sessions
|
|
338
|
-
const inactiveSessions = await strapi.
|
|
348
|
+
// Find all inactive sessions (documentId is always included automatically)
|
|
349
|
+
const inactiveSessions = await strapi.documents(SESSION_UID).findMany({
|
|
339
350
|
filters: { isActive: false },
|
|
340
|
-
fields: ['id'],
|
|
341
351
|
});
|
|
342
352
|
|
|
343
353
|
let deletedCount = 0;
|
|
344
354
|
|
|
345
355
|
// Delete each inactive session
|
|
346
356
|
for (const session of inactiveSessions) {
|
|
347
|
-
await strapi.
|
|
357
|
+
await strapi.documents(SESSION_UID).delete({ documentId: session.documentId });
|
|
348
358
|
deletedCount++;
|
|
349
359
|
}
|
|
350
360
|
|
|
351
|
-
strapi.log.info(`[magic-sessionmanager]
|
|
361
|
+
strapi.log.info(`[magic-sessionmanager] [SUCCESS] Deleted ${deletedCount} inactive sessions`);
|
|
352
362
|
return deletedCount;
|
|
353
363
|
} catch (err) {
|
|
354
364
|
strapi.log.error('[magic-sessionmanager] Error deleting inactive sessions:', err);
|
|
@@ -31,7 +31,7 @@ function getEncryptionKey() {
|
|
|
31
31
|
const strapiKeys = process.env.APP_KEYS || process.env.API_TOKEN_SALT || 'default-insecure-key';
|
|
32
32
|
const key = crypto.createHash('sha256').update(strapiKeys).digest();
|
|
33
33
|
|
|
34
|
-
console.warn('[magic-sessionmanager/encryption]
|
|
34
|
+
console.warn('[magic-sessionmanager/encryption] [WARNING] No SESSION_ENCRYPTION_KEY found. Using fallback (not recommended for production).');
|
|
35
35
|
console.warn('[magic-sessionmanager/encryption] Set SESSION_ENCRYPTION_KEY in .env for better security.');
|
|
36
36
|
|
|
37
37
|
return key;
|