strapi-plugin-magic-sessionmanager 3.2.1 → 3.3.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 CHANGED
@@ -349,7 +349,7 @@ Log: "Cleaned up X inactive sessions"
349
349
  2. Inactivity timeout triggers cleanup
350
350
  3. Admin terminates the session
351
351
 
352
- #### Refresh Tokens ⚠️ **Critical Limitation**
352
+ #### Refresh Tokens **SOLVED!**
353
353
 
354
354
  **What are Refresh Tokens?**
355
355
  Refresh tokens allow users to get new Access Tokens (JWTs) without re-entering credentials. This enables longer sessions:
@@ -367,78 +367,100 @@ Strapi issues new JWT
367
367
  User continues without re-login
368
368
  ```
369
369
 
370
- **The Problem:**
371
- - **Stored:** NO - Refresh tokens are NOT tracked by this plugin
372
- - **Reason:** Strapi v5 handles refresh tokens in `users-permissions` plugin
373
- - **Impact:** User can bypass session termination! ⚠️
370
+ **The Solution (v3.2+):**
371
+ - **Stored:** YES - Refresh tokens are encrypted and stored with sessions ✅
372
+ - **Tracked:** YES - Middleware intercepts `/api/auth/refresh-token` requests ✅
373
+ - **Validated:** YES - Checks if session is still active before issuing new tokens ✅
374
+
375
+ **How It Works:**
374
376
 
375
- **Scenario:**
376
377
  ```
377
- Admin terminates user's session
378
+ Login: User gets JWT + Refresh Token
379
+
380
+ Both tokens encrypted and stored in session
381
+
382
+ Admin terminates session
383
+
384
+ Session: isActive = false ❌
385
+
386
+ User tries to refresh token:
387
+ POST /api/auth/refresh-token
388
+ { refreshToken: "..." }
378
389
 
379
- Session Manager: isActive = false ❌
390
+ [Refresh Token Middleware]
380
391
 
381
- User's JWT still valid OR
382
- User has refresh token
392
+ Decrypt all active session refresh tokens
383
393
 
384
- User gets new JWT via refresh token
394
+ Find matching session
385
395
 
386
- Plugin creates NEW session automatically! ⚠️
396
+ Session found but isActive = false?
397
+ → BLOCK! Return 401 Unauthorized ❌
398
+ → Message: "Session terminated. Please login again."
387
399
 
388
- User is "logged in" again despite termination
400
+ Session found and isActive = true?
401
+ → ALLOW! ✅
402
+ → Strapi issues new tokens
403
+ → Session updated with new encrypted tokens
389
404
  ```
390
405
 
391
- **Current Limitation:**
392
- This plugin **cannot prevent** users with valid refresh tokens from getting new JWTs. The session termination only affects the current JWT token.
406
+ **Security Benefits:**
393
407
 
394
- **Workarounds:**
408
+ **Session termination is FINAL** - User cannot get new tokens
409
+ ✅ **Refresh tokens tracked** - Encrypted & stored securely
410
+ ✅ **Token rotation** - New tokens automatically updated in session
411
+ ✅ **Admin control** - Force logout works even with refresh tokens
412
+
413
+ **Configuration:**
414
+
415
+ Enable refresh tokens in Strapi:
395
416
 
396
- **Option 1: Disable Refresh Tokens (Strict Control)**
397
417
  ```typescript
398
418
  // src/config/plugins.ts
399
419
  export default () => ({
400
420
  'users-permissions': {
401
421
  config: {
402
- jwt: {
403
- expiresIn: '30m',
404
- // Don't issue refresh tokens
422
+ jwtManagement: 'refresh', // Enable refresh tokens
423
+ sessions: {
424
+ accessTokenLifespan: 3600, // 1 hour (in seconds)
425
+ maxRefreshTokenLifespan: 2592000, // 30 days
426
+ idleRefreshTokenLifespan: 604800, // 7 days idle
405
427
  },
406
428
  },
407
429
  },
408
430
  'magic-sessionmanager': {
431
+ enabled: true,
409
432
  config: {
410
- inactivityTimeout: 15 * 60 * 1000, // < JWT expiration
433
+ inactivityTimeout: 15 * 60 * 1000, // 15 minutes
411
434
  },
412
435
  },
413
436
  });
414
437
  ```
415
438
 
416
- **Option 2: Short Refresh Token Expiry**
417
- ```typescript
418
- 'users-permissions': {
419
- config: {
420
- jwt: {
421
- expiresIn: '15m', // Short Access Token
422
- refreshExpiresIn: '30m', // Short Refresh Token
423
- },
424
- },
425
- }
426
- ```
439
+ **Testing Refresh Token Blocking:**
427
440
 
428
- **Option 3: Accept the Limitation**
429
- Understand that:
430
- - Session termination stops the **current** JWT
431
- - Users with refresh tokens can get **new** JWTs
432
- - New JWTs create **new** sessions
433
- - Use session analytics to detect unusual patterns
441
+ ```bash
442
+ # 1. Login and get tokens
443
+ curl -X POST http://localhost:1337/api/auth/local \
444
+ -H "Content-Type: application/json" \
445
+ -d '{"identifier":"user@example.com","password":"pass"}'
434
446
 
435
- **Future Enhancement:**
436
- To fully block users, the plugin would need to:
437
- 1. Track refresh tokens (complex)
438
- 2. Hook into Strapi's token refresh endpoint
439
- 3. Validate against active sessions before issuing new JWTs
447
+ # Save both tokens:
448
+ ACCESS_TOKEN="eyJhbGci..."
449
+ REFRESH_TOKEN="abc123..."
450
+
451
+ # 2. Admin terminates session
452
+ # Go to Admin → Sessions → Find session → Terminate
453
+
454
+ # 3. Try to refresh token
455
+ curl -X POST http://localhost:1337/api/auth/refresh-token \
456
+ -H "Content-Type: application/json" \
457
+ -d "{\"refreshToken\":\"$REFRESH_TOKEN\"}"
458
+
459
+ # Expected: 401 Unauthorized
460
+ # "Session terminated. Please login again."
461
+ ```
440
462
 
441
- This is planned for a future version.
463
+ **This completely solves the refresh token security gap!** 🔒
442
464
 
443
465
  ### Multi-Login Behavior
444
466
 
@@ -350,8 +350,10 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
350
350
  userId: user.id,
351
351
  ip,
352
352
  userAgent,
353
- token: ctx.body.jwt
354
- // Store JWT token reference
353
+ token: ctx.body.jwt,
354
+ // Store Access Token (encrypted)
355
+ refreshToken: ctx.body.refreshToken
356
+ // Store Refresh Token (encrypted) if exists
355
357
  });
356
358
  strapi2.log.info(`[magic-sessionmanager] ✅ Session created for user ${user.id} (IP: ${ip})`);
357
359
  if (geoData && (config2.enableEmailAlerts || config2.enableWebhooks)) {
@@ -396,6 +398,84 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
396
398
  }
397
399
  });
398
400
  strapi2.log.info("[magic-sessionmanager] ✅ Login/Logout interceptor middleware mounted");
401
+ strapi2.server.use(async (ctx, next) => {
402
+ const isRefreshToken = ctx.path === "/api/auth/refresh-token" && ctx.method === "POST";
403
+ if (isRefreshToken) {
404
+ try {
405
+ const refreshToken = ctx.request.body?.refreshToken;
406
+ if (refreshToken) {
407
+ const allSessions = await strapi2.entityService.findMany("plugin::magic-sessionmanager.session", {
408
+ filters: {
409
+ isActive: true
410
+ }
411
+ });
412
+ const matchingSession = allSessions.find((session2) => {
413
+ if (!session2.refreshToken) return false;
414
+ try {
415
+ const decrypted = decryptToken$2(session2.refreshToken);
416
+ return decrypted === refreshToken;
417
+ } catch (err) {
418
+ return false;
419
+ }
420
+ });
421
+ if (!matchingSession) {
422
+ strapi2.log.warn("[magic-sessionmanager] 🚫 Blocked refresh token request - no active session");
423
+ ctx.status = 401;
424
+ ctx.body = {
425
+ error: {
426
+ status: 401,
427
+ message: "Session terminated. Please login again.",
428
+ name: "UnauthorizedError"
429
+ }
430
+ };
431
+ return;
432
+ }
433
+ strapi2.log.info(`[magic-sessionmanager] ✅ Refresh token allowed for session ${matchingSession.id}`);
434
+ }
435
+ } catch (err) {
436
+ strapi2.log.error("[magic-sessionmanager] Error checking refresh token:", err);
437
+ }
438
+ }
439
+ await next();
440
+ if (isRefreshToken && ctx.status === 200 && ctx.body && ctx.body.jwt) {
441
+ try {
442
+ const oldRefreshToken = ctx.request.body?.refreshToken;
443
+ const newAccessToken = ctx.body.jwt;
444
+ const newRefreshToken = ctx.body.refreshToken;
445
+ if (oldRefreshToken) {
446
+ const allSessions = await strapi2.entityService.findMany("plugin::magic-sessionmanager.session", {
447
+ filters: {
448
+ isActive: true
449
+ }
450
+ });
451
+ const matchingSession = allSessions.find((session2) => {
452
+ if (!session2.refreshToken) return false;
453
+ try {
454
+ const decrypted = decryptToken$2(session2.refreshToken);
455
+ return decrypted === oldRefreshToken;
456
+ } catch (err) {
457
+ return false;
458
+ }
459
+ });
460
+ if (matchingSession) {
461
+ const encryptedToken = newAccessToken ? encryptToken$1(newAccessToken) : matchingSession.token;
462
+ const encryptedRefreshToken = newRefreshToken ? encryptToken$1(newRefreshToken) : matchingSession.refreshToken;
463
+ await strapi2.entityService.update("plugin::magic-sessionmanager.session", matchingSession.id, {
464
+ data: {
465
+ token: encryptedToken,
466
+ refreshToken: encryptedRefreshToken,
467
+ lastActive: /* @__PURE__ */ new Date()
468
+ }
469
+ });
470
+ strapi2.log.info(`[magic-sessionmanager] 🔄 Tokens refreshed for session ${matchingSession.id}`);
471
+ }
472
+ }
473
+ } catch (err) {
474
+ strapi2.log.error("[magic-sessionmanager] Error updating refreshed tokens:", err);
475
+ }
476
+ }
477
+ });
478
+ strapi2.log.info("[magic-sessionmanager] ✅ Refresh Token interceptor middleware mounted");
399
479
  strapi2.server.use(
400
480
  lastSeen({ strapi: strapi2, sessionService })
401
481
  );
@@ -480,6 +560,10 @@ const attributes = {
480
560
  type: "text",
481
561
  "private": true
482
562
  },
563
+ refreshToken: {
564
+ type: "text",
565
+ "private": true
566
+ },
483
567
  loginTime: {
484
568
  type: "datetime",
485
569
  required: true
@@ -1317,14 +1401,15 @@ const { encryptToken, decryptToken, generateSessionId } = encryption;
1317
1401
  var session$1 = ({ strapi: strapi2 }) => ({
1318
1402
  /**
1319
1403
  * Create a new session record
1320
- * @param {Object} params - { userId, ip, userAgent, token }
1404
+ * @param {Object} params - { userId, ip, userAgent, token, refreshToken }
1321
1405
  * @returns {Promise<Object>} Created session
1322
1406
  */
1323
- async createSession({ userId, ip = "unknown", userAgent = "unknown", token }) {
1407
+ async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken }) {
1324
1408
  try {
1325
1409
  const now = /* @__PURE__ */ new Date();
1326
1410
  const sessionId = generateSessionId(userId);
1327
1411
  const encryptedToken = token ? encryptToken(token) : null;
1412
+ const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
1328
1413
  const session2 = await strapi2.entityService.create("plugin::magic-sessionmanager.session", {
1329
1414
  data: {
1330
1415
  user: userId,
@@ -1334,7 +1419,9 @@ var session$1 = ({ strapi: strapi2 }) => ({
1334
1419
  lastActive: now,
1335
1420
  isActive: true,
1336
1421
  token: encryptedToken,
1337
- // ✅ Encrypted JWT for security
1422
+ // ✅ Encrypted Access Token
1423
+ refreshToken: encryptedRefreshToken,
1424
+ // ✅ Encrypted Refresh Token
1338
1425
  sessionId
1339
1426
  // ✅ Unique identifier
1340
1427
  }
@@ -346,8 +346,10 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
346
346
  userId: user.id,
347
347
  ip,
348
348
  userAgent,
349
- token: ctx.body.jwt
350
- // Store JWT token reference
349
+ token: ctx.body.jwt,
350
+ // Store Access Token (encrypted)
351
+ refreshToken: ctx.body.refreshToken
352
+ // Store Refresh Token (encrypted) if exists
351
353
  });
352
354
  strapi2.log.info(`[magic-sessionmanager] ✅ Session created for user ${user.id} (IP: ${ip})`);
353
355
  if (geoData && (config2.enableEmailAlerts || config2.enableWebhooks)) {
@@ -392,6 +394,84 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
392
394
  }
393
395
  });
394
396
  strapi2.log.info("[magic-sessionmanager] ✅ Login/Logout interceptor middleware mounted");
397
+ strapi2.server.use(async (ctx, next) => {
398
+ const isRefreshToken = ctx.path === "/api/auth/refresh-token" && ctx.method === "POST";
399
+ if (isRefreshToken) {
400
+ try {
401
+ const refreshToken = ctx.request.body?.refreshToken;
402
+ if (refreshToken) {
403
+ const allSessions = await strapi2.entityService.findMany("plugin::magic-sessionmanager.session", {
404
+ filters: {
405
+ isActive: true
406
+ }
407
+ });
408
+ const matchingSession = allSessions.find((session2) => {
409
+ if (!session2.refreshToken) return false;
410
+ try {
411
+ const decrypted = decryptToken$2(session2.refreshToken);
412
+ return decrypted === refreshToken;
413
+ } catch (err) {
414
+ return false;
415
+ }
416
+ });
417
+ if (!matchingSession) {
418
+ strapi2.log.warn("[magic-sessionmanager] 🚫 Blocked refresh token request - no active session");
419
+ ctx.status = 401;
420
+ ctx.body = {
421
+ error: {
422
+ status: 401,
423
+ message: "Session terminated. Please login again.",
424
+ name: "UnauthorizedError"
425
+ }
426
+ };
427
+ return;
428
+ }
429
+ strapi2.log.info(`[magic-sessionmanager] ✅ Refresh token allowed for session ${matchingSession.id}`);
430
+ }
431
+ } catch (err) {
432
+ strapi2.log.error("[magic-sessionmanager] Error checking refresh token:", err);
433
+ }
434
+ }
435
+ await next();
436
+ if (isRefreshToken && ctx.status === 200 && ctx.body && ctx.body.jwt) {
437
+ try {
438
+ const oldRefreshToken = ctx.request.body?.refreshToken;
439
+ const newAccessToken = ctx.body.jwt;
440
+ const newRefreshToken = ctx.body.refreshToken;
441
+ if (oldRefreshToken) {
442
+ const allSessions = await strapi2.entityService.findMany("plugin::magic-sessionmanager.session", {
443
+ filters: {
444
+ isActive: true
445
+ }
446
+ });
447
+ const matchingSession = allSessions.find((session2) => {
448
+ if (!session2.refreshToken) return false;
449
+ try {
450
+ const decrypted = decryptToken$2(session2.refreshToken);
451
+ return decrypted === oldRefreshToken;
452
+ } catch (err) {
453
+ return false;
454
+ }
455
+ });
456
+ if (matchingSession) {
457
+ const encryptedToken = newAccessToken ? encryptToken$1(newAccessToken) : matchingSession.token;
458
+ const encryptedRefreshToken = newRefreshToken ? encryptToken$1(newRefreshToken) : matchingSession.refreshToken;
459
+ await strapi2.entityService.update("plugin::magic-sessionmanager.session", matchingSession.id, {
460
+ data: {
461
+ token: encryptedToken,
462
+ refreshToken: encryptedRefreshToken,
463
+ lastActive: /* @__PURE__ */ new Date()
464
+ }
465
+ });
466
+ strapi2.log.info(`[magic-sessionmanager] 🔄 Tokens refreshed for session ${matchingSession.id}`);
467
+ }
468
+ }
469
+ } catch (err) {
470
+ strapi2.log.error("[magic-sessionmanager] Error updating refreshed tokens:", err);
471
+ }
472
+ }
473
+ });
474
+ strapi2.log.info("[magic-sessionmanager] ✅ Refresh Token interceptor middleware mounted");
395
475
  strapi2.server.use(
396
476
  lastSeen({ strapi: strapi2, sessionService })
397
477
  );
@@ -476,6 +556,10 @@ const attributes = {
476
556
  type: "text",
477
557
  "private": true
478
558
  },
559
+ refreshToken: {
560
+ type: "text",
561
+ "private": true
562
+ },
479
563
  loginTime: {
480
564
  type: "datetime",
481
565
  required: true
@@ -1313,14 +1397,15 @@ const { encryptToken, decryptToken, generateSessionId } = encryption;
1313
1397
  var session$1 = ({ strapi: strapi2 }) => ({
1314
1398
  /**
1315
1399
  * Create a new session record
1316
- * @param {Object} params - { userId, ip, userAgent, token }
1400
+ * @param {Object} params - { userId, ip, userAgent, token, refreshToken }
1317
1401
  * @returns {Promise<Object>} Created session
1318
1402
  */
1319
- async createSession({ userId, ip = "unknown", userAgent = "unknown", token }) {
1403
+ async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken }) {
1320
1404
  try {
1321
1405
  const now = /* @__PURE__ */ new Date();
1322
1406
  const sessionId = generateSessionId(userId);
1323
1407
  const encryptedToken = token ? encryptToken(token) : null;
1408
+ const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
1324
1409
  const session2 = await strapi2.entityService.create("plugin::magic-sessionmanager.session", {
1325
1410
  data: {
1326
1411
  user: userId,
@@ -1330,7 +1415,9 @@ var session$1 = ({ strapi: strapi2 }) => ({
1330
1415
  lastActive: now,
1331
1416
  isActive: true,
1332
1417
  token: encryptedToken,
1333
- // ✅ Encrypted JWT for security
1418
+ // ✅ Encrypted Access Token
1419
+ refreshToken: encryptedRefreshToken,
1420
+ // ✅ Encrypted Refresh Token
1334
1421
  sessionId
1335
1422
  // ✅ Unique identifier
1336
1423
  }
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.2.1",
2
+ "version": "3.3.0",
3
3
  "keywords": [
4
4
  "strapi",
5
5
  "strapi-plugin",
@@ -235,7 +235,8 @@ module.exports = async ({ strapi }) => {
235
235
  userId: user.id,
236
236
  ip,
237
237
  userAgent,
238
- token: ctx.body.jwt, // Store JWT token reference
238
+ token: ctx.body.jwt, // Store Access Token (encrypted)
239
+ refreshToken: ctx.body.refreshToken, // Store Refresh Token (encrypted) if exists
239
240
  });
240
241
 
241
242
  strapi.log.info(`[magic-sessionmanager] ✅ Session created for user ${user.id} (IP: ${ip})`);
@@ -293,6 +294,107 @@ module.exports = async ({ strapi }) => {
293
294
 
294
295
  strapi.log.info('[magic-sessionmanager] ✅ Login/Logout interceptor middleware mounted');
295
296
 
297
+ // Middleware to block refresh token requests for terminated sessions
298
+ strapi.server.use(async (ctx, next) => {
299
+ // Check if this is a refresh token request
300
+ const isRefreshToken = ctx.path === '/api/auth/refresh-token' && ctx.method === 'POST';
301
+
302
+ if (isRefreshToken) {
303
+ try {
304
+ const refreshToken = ctx.request.body?.refreshToken;
305
+
306
+ if (refreshToken) {
307
+ // Find session with this refresh token
308
+ const allSessions = await strapi.entityService.findMany('plugin::magic-sessionmanager.session', {
309
+ filters: {
310
+ isActive: true,
311
+ },
312
+ });
313
+
314
+ // Find matching session by decrypting and comparing refresh tokens
315
+ const matchingSession = allSessions.find(session => {
316
+ if (!session.refreshToken) return false;
317
+ try {
318
+ const decrypted = decryptToken(session.refreshToken);
319
+ return decrypted === refreshToken;
320
+ } catch (err) {
321
+ return false;
322
+ }
323
+ });
324
+
325
+ if (!matchingSession) {
326
+ // No active session with this refresh token → Block!
327
+ strapi.log.warn('[magic-sessionmanager] 🚫 Blocked refresh token request - no active session');
328
+ ctx.status = 401;
329
+ ctx.body = {
330
+ error: {
331
+ status: 401,
332
+ message: 'Session terminated. Please login again.',
333
+ name: 'UnauthorizedError'
334
+ }
335
+ };
336
+ return; // Don't continue
337
+ }
338
+
339
+ strapi.log.info(`[magic-sessionmanager] ✅ Refresh token allowed for session ${matchingSession.id}`);
340
+ }
341
+ } catch (err) {
342
+ strapi.log.error('[magic-sessionmanager] Error checking refresh token:', err);
343
+ // On error, allow request to continue (fail-open for availability)
344
+ }
345
+ }
346
+
347
+ // Continue with request
348
+ await next();
349
+
350
+ // AFTER: If refresh token response was successful, update session with new tokens
351
+ if (isRefreshToken && ctx.status === 200 && ctx.body && ctx.body.jwt) {
352
+ try {
353
+ const oldRefreshToken = ctx.request.body?.refreshToken;
354
+ const newAccessToken = ctx.body.jwt;
355
+ const newRefreshToken = ctx.body.refreshToken;
356
+
357
+ if (oldRefreshToken) {
358
+ // Find session and update with new tokens
359
+ const allSessions = await strapi.entityService.findMany('plugin::magic-sessionmanager.session', {
360
+ filters: {
361
+ isActive: true,
362
+ },
363
+ });
364
+
365
+ const matchingSession = allSessions.find(session => {
366
+ if (!session.refreshToken) return false;
367
+ try {
368
+ const decrypted = decryptToken(session.refreshToken);
369
+ return decrypted === oldRefreshToken;
370
+ } catch (err) {
371
+ return false;
372
+ }
373
+ });
374
+
375
+ if (matchingSession) {
376
+ const encryptedToken = newAccessToken ? encryptToken(newAccessToken) : matchingSession.token;
377
+ const encryptedRefreshToken = newRefreshToken ? encryptToken(newRefreshToken) : matchingSession.refreshToken;
378
+
379
+ await strapi.entityService.update('plugin::magic-sessionmanager.session', matchingSession.id, {
380
+ data: {
381
+ token: encryptedToken,
382
+ refreshToken: encryptedRefreshToken,
383
+ lastActive: new Date(),
384
+ },
385
+ });
386
+
387
+ strapi.log.info(`[magic-sessionmanager] 🔄 Tokens refreshed for session ${matchingSession.id}`);
388
+ }
389
+ }
390
+ } catch (err) {
391
+ strapi.log.error('[magic-sessionmanager] Error updating refreshed tokens:', err);
392
+ }
393
+ }
394
+ });
395
+
396
+ strapi.log.info('[magic-sessionmanager] ✅ Refresh Token interceptor middleware mounted');
397
+
296
398
  // Mount lastSeen update middleware
297
399
  strapi.server.use(
298
400
  require('./middlewares/last-seen')({ strapi, sessionService })
@@ -43,6 +43,10 @@
43
43
  "type": "text",
44
44
  "private": true
45
45
  },
46
+ "refreshToken": {
47
+ "type": "text",
48
+ "private": true
49
+ },
46
50
  "loginTime": {
47
51
  "type": "datetime",
48
52
  "required": true
@@ -17,18 +17,19 @@ const { encryptToken, decryptToken, generateSessionId } = require('../utils/encr
17
17
  module.exports = ({ strapi }) => ({
18
18
  /**
19
19
  * Create a new session record
20
- * @param {Object} params - { userId, ip, userAgent, token }
20
+ * @param {Object} params - { userId, ip, userAgent, token, refreshToken }
21
21
  * @returns {Promise<Object>} Created session
22
22
  */
23
- async createSession({ userId, ip = 'unknown', userAgent = 'unknown', token }) {
23
+ async createSession({ userId, ip = 'unknown', userAgent = 'unknown', token, refreshToken }) {
24
24
  try {
25
25
  const now = new Date();
26
26
 
27
27
  // Generate unique session ID
28
28
  const sessionId = generateSessionId(userId);
29
29
 
30
- // Encrypt JWT token before storing
30
+ // Encrypt JWT tokens before storing (both access and refresh)
31
31
  const encryptedToken = token ? encryptToken(token) : null;
32
+ const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
32
33
 
33
34
  const session = await strapi.entityService.create('plugin::magic-sessionmanager.session', {
34
35
  data: {
@@ -38,8 +39,9 @@ module.exports = ({ strapi }) => ({
38
39
  loginTime: now,
39
40
  lastActive: now,
40
41
  isActive: true,
41
- token: encryptedToken, // ✅ Encrypted JWT for security
42
- sessionId: sessionId, // ✅ Unique identifier
42
+ token: encryptedToken, // ✅ Encrypted Access Token
43
+ refreshToken: encryptedRefreshToken, // ✅ Encrypted Refresh Token
44
+ sessionId: sessionId, // ✅ Unique identifier
43
45
  },
44
46
  });
45
47