strapi-plugin-magic-sessionmanager 4.2.10 → 4.2.12
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 +183 -9
- package/dist/server/index.js +261 -53
- package/dist/server/index.mjs +261 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -390,30 +390,204 @@ Trigger a suspicious login (e.g., use a VPN) and check if the email arrives!
|
|
|
390
390
|
|
|
391
391
|
---
|
|
392
392
|
|
|
393
|
-
## 📋
|
|
393
|
+
## 📋 Content-API Endpoints (For Frontend/Apps)
|
|
394
394
|
|
|
395
|
-
|
|
395
|
+
All Content-API endpoints require a valid JWT token in the `Authorization` header.
|
|
396
|
+
Users can only access their **own** sessions.
|
|
397
|
+
|
|
398
|
+
### Get My Sessions
|
|
399
|
+
|
|
400
|
+
Returns all sessions for the authenticated user.
|
|
401
|
+
|
|
402
|
+
```bash
|
|
403
|
+
GET /api/magic-sessionmanager/my-sessions
|
|
404
|
+
Authorization: Bearer <JWT>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Response:**
|
|
408
|
+
```json
|
|
409
|
+
{
|
|
410
|
+
"data": [
|
|
411
|
+
{
|
|
412
|
+
"id": 41,
|
|
413
|
+
"documentId": "abc123xyz",
|
|
414
|
+
"sessionId": "sess_m5k2h_8a3b1c2d_f9e8d7c6",
|
|
415
|
+
"ipAddress": "192.168.1.100",
|
|
416
|
+
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36...",
|
|
417
|
+
"loginTime": "2026-01-02T10:30:00.000Z",
|
|
418
|
+
"lastActive": "2026-01-02T13:45:00.000Z",
|
|
419
|
+
"logoutTime": null,
|
|
420
|
+
"isActive": true,
|
|
421
|
+
"deviceType": "desktop",
|
|
422
|
+
"browserName": "Chrome 143",
|
|
423
|
+
"osName": "macOS 10.15.7",
|
|
424
|
+
"geoLocation": null,
|
|
425
|
+
"securityScore": null,
|
|
426
|
+
"isCurrentSession": true,
|
|
427
|
+
"isTrulyActive": true,
|
|
428
|
+
"minutesSinceActive": 2
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
"id": 40,
|
|
432
|
+
"documentId": "def456uvw",
|
|
433
|
+
"sessionId": "sess_m5k1g_7b2a0c1d_e8d7c6b5",
|
|
434
|
+
"ipAddress": "10.0.0.50",
|
|
435
|
+
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)...",
|
|
436
|
+
"loginTime": "2026-01-01T08:15:00.000Z",
|
|
437
|
+
"lastActive": "2026-01-01T12:00:00.000Z",
|
|
438
|
+
"logoutTime": null,
|
|
439
|
+
"isActive": true,
|
|
440
|
+
"deviceType": "mobile",
|
|
441
|
+
"browserName": "Safari",
|
|
442
|
+
"osName": "iOS 17",
|
|
443
|
+
"geoLocation": null,
|
|
444
|
+
"securityScore": null,
|
|
445
|
+
"isCurrentSession": false,
|
|
446
|
+
"isTrulyActive": false,
|
|
447
|
+
"minutesSinceActive": 1545
|
|
448
|
+
}
|
|
449
|
+
],
|
|
450
|
+
"meta": {
|
|
451
|
+
"count": 2,
|
|
452
|
+
"active": 1
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### Get Current Session
|
|
458
|
+
|
|
459
|
+
Returns only the session associated with the current JWT token.
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
GET /api/magic-sessionmanager/current-session
|
|
463
|
+
Authorization: Bearer <JWT>
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
**Response:**
|
|
467
|
+
```json
|
|
468
|
+
{
|
|
469
|
+
"data": {
|
|
470
|
+
"id": 41,
|
|
471
|
+
"documentId": "abc123xyz",
|
|
472
|
+
"sessionId": "sess_m5k2h_8a3b1c2d_f9e8d7c6",
|
|
473
|
+
"ipAddress": "192.168.1.100",
|
|
474
|
+
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36...",
|
|
475
|
+
"loginTime": "2026-01-02T10:30:00.000Z",
|
|
476
|
+
"lastActive": "2026-01-02T13:45:00.000Z",
|
|
477
|
+
"logoutTime": null,
|
|
478
|
+
"isActive": true,
|
|
479
|
+
"deviceType": "desktop",
|
|
480
|
+
"browserName": "Chrome 143",
|
|
481
|
+
"osName": "macOS 10.15.7",
|
|
482
|
+
"geoLocation": null,
|
|
483
|
+
"securityScore": null,
|
|
484
|
+
"isCurrentSession": true,
|
|
485
|
+
"isTrulyActive": true,
|
|
486
|
+
"minutesSinceActive": 2
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Logout (Current Session)
|
|
492
|
+
|
|
493
|
+
Terminates only the current session.
|
|
494
|
+
|
|
495
|
+
```bash
|
|
496
|
+
POST /api/magic-sessionmanager/logout
|
|
497
|
+
Authorization: Bearer <JWT>
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
**Response:**
|
|
501
|
+
```json
|
|
502
|
+
{
|
|
503
|
+
"message": "Logged out successfully"
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Logout All Devices
|
|
508
|
+
|
|
509
|
+
Terminates ALL sessions for the authenticated user (logs out everywhere).
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
POST /api/magic-sessionmanager/logout-all
|
|
513
|
+
Authorization: Bearer <JWT>
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**Response:**
|
|
517
|
+
```json
|
|
518
|
+
{
|
|
519
|
+
"message": "Logged out from all devices successfully"
|
|
520
|
+
}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Terminate Specific Session
|
|
524
|
+
|
|
525
|
+
Terminates a specific session (not the current one). Useful for "Log out other devices".
|
|
526
|
+
|
|
527
|
+
```bash
|
|
528
|
+
DELETE /api/magic-sessionmanager/my-sessions/:sessionId
|
|
529
|
+
Authorization: Bearer <JWT>
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
**Response:**
|
|
533
|
+
```json
|
|
534
|
+
{
|
|
535
|
+
"message": "Session abc123xyz terminated successfully",
|
|
536
|
+
"success": true
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
**Error (trying to terminate current session):**
|
|
541
|
+
```json
|
|
542
|
+
{
|
|
543
|
+
"error": {
|
|
544
|
+
"status": 400,
|
|
545
|
+
"message": "Cannot terminate current session. Use /logout instead."
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## 📋 Admin-API Endpoints (For Admin Panel)
|
|
553
|
+
|
|
554
|
+
These endpoints require admin authentication.
|
|
555
|
+
|
|
556
|
+
### Get All Sessions
|
|
396
557
|
|
|
397
558
|
```bash
|
|
398
|
-
# Get all active sessions
|
|
399
559
|
GET /magic-sessionmanager/sessions
|
|
400
560
|
```
|
|
401
561
|
|
|
402
|
-
###
|
|
562
|
+
### Get Active Sessions Only
|
|
403
563
|
|
|
404
564
|
```bash
|
|
405
|
-
|
|
406
|
-
POST /api/auth/logout
|
|
565
|
+
GET /magic-sessionmanager/sessions/active
|
|
407
566
|
```
|
|
408
567
|
|
|
409
|
-
### Force
|
|
568
|
+
### Force Terminate Session
|
|
410
569
|
|
|
411
570
|
```bash
|
|
412
|
-
# Admin force-logout a session
|
|
413
571
|
POST /magic-sessionmanager/sessions/:sessionId/terminate
|
|
414
572
|
```
|
|
415
573
|
|
|
416
|
-
|
|
574
|
+
### Terminate All User Sessions
|
|
575
|
+
|
|
576
|
+
```bash
|
|
577
|
+
POST /magic-sessionmanager/user/:userId/terminate-all
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### Block/Unblock User
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
POST /magic-sessionmanager/user/:userId/toggle-block
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### Clean Inactive Sessions
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
POST /magic-sessionmanager/sessions/clean-inactive
|
|
590
|
+
```
|
|
417
591
|
|
|
418
592
|
---
|
|
419
593
|
|
package/dist/server/index.js
CHANGED
|
@@ -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$
|
|
209
|
+
function hashToken$4(token) {
|
|
210
210
|
if (!token) return null;
|
|
211
211
|
return crypto$1.createHash("sha256").update(token).digest("hex");
|
|
212
212
|
}
|
|
@@ -214,10 +214,10 @@ var encryption = {
|
|
|
214
214
|
encryptToken: encryptToken$2,
|
|
215
215
|
decryptToken: decryptToken$3,
|
|
216
216
|
generateSessionId: generateSessionId$1,
|
|
217
|
-
hashToken: hashToken$
|
|
217
|
+
hashToken: hashToken$4
|
|
218
218
|
};
|
|
219
219
|
const SESSION_UID$3 = "plugin::magic-sessionmanager.session";
|
|
220
|
-
const { hashToken: hashToken$
|
|
220
|
+
const { hashToken: hashToken$3 } = encryption;
|
|
221
221
|
const lastTouchCache = /* @__PURE__ */ new Map();
|
|
222
222
|
var lastSeen = ({ strapi: strapi2 }) => {
|
|
223
223
|
return async (ctx, next) => {
|
|
@@ -233,7 +233,7 @@ var lastSeen = ({ strapi: strapi2 }) => {
|
|
|
233
233
|
}
|
|
234
234
|
let matchingSession = null;
|
|
235
235
|
try {
|
|
236
|
-
const currentTokenHash = hashToken$
|
|
236
|
+
const currentTokenHash = hashToken$3(currentToken);
|
|
237
237
|
matchingSession = await strapi2.documents(SESSION_UID$3).findFirst({
|
|
238
238
|
filters: {
|
|
239
239
|
tokenHash: currentTokenHash,
|
|
@@ -278,7 +278,7 @@ var lastSeen = ({ strapi: strapi2 }) => {
|
|
|
278
278
|
};
|
|
279
279
|
};
|
|
280
280
|
const getClientIp = getClientIp_1;
|
|
281
|
-
const { encryptToken: encryptToken$1, decryptToken: decryptToken$2, hashToken: hashToken$
|
|
281
|
+
const { encryptToken: encryptToken$1, decryptToken: decryptToken$2, hashToken: hashToken$2 } = encryption;
|
|
282
282
|
const { createLogger: createLogger$3 } = logger;
|
|
283
283
|
const SESSION_UID$2 = "plugin::magic-sessionmanager.session";
|
|
284
284
|
const USER_UID$2 = "plugin::users-permissions.user";
|
|
@@ -455,8 +455,10 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
455
455
|
userAgent,
|
|
456
456
|
token: ctx.body.jwt,
|
|
457
457
|
// Store Access Token (encrypted)
|
|
458
|
-
refreshToken: ctx.body.refreshToken
|
|
458
|
+
refreshToken: ctx.body.refreshToken,
|
|
459
459
|
// Store Refresh Token (encrypted) if exists
|
|
460
|
+
geoData
|
|
461
|
+
// Store geolocation data if available
|
|
460
462
|
});
|
|
461
463
|
log.info(`[SUCCESS] Session created for user ${userDocId} (IP: ${ip})`);
|
|
462
464
|
if (geoData && (config2.enableEmailAlerts || config2.enableWebhooks)) {
|
|
@@ -563,8 +565,8 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
563
565
|
if (matchingSession) {
|
|
564
566
|
const encryptedToken = newAccessToken ? encryptToken$1(newAccessToken) : matchingSession.token;
|
|
565
567
|
const encryptedRefreshToken = newRefreshToken ? encryptToken$1(newRefreshToken) : matchingSession.refreshToken;
|
|
566
|
-
const newTokenHash = newAccessToken ? hashToken$
|
|
567
|
-
const newRefreshTokenHash = newRefreshToken ? hashToken$
|
|
568
|
+
const newTokenHash = newAccessToken ? hashToken$2(newAccessToken) : matchingSession.tokenHash;
|
|
569
|
+
const newRefreshTokenHash = newRefreshToken ? hashToken$2(newRefreshToken) : matchingSession.refreshTokenHash;
|
|
568
570
|
await strapi2.documents(SESSION_UID$2).update({
|
|
569
571
|
documentId: matchingSession.documentId,
|
|
570
572
|
data: {
|
|
@@ -1042,7 +1044,91 @@ var routes$1 = {
|
|
|
1042
1044
|
admin,
|
|
1043
1045
|
"content-api": contentApi
|
|
1044
1046
|
};
|
|
1045
|
-
|
|
1047
|
+
function parseUserAgent$2(userAgent) {
|
|
1048
|
+
if (!userAgent) {
|
|
1049
|
+
return {
|
|
1050
|
+
deviceType: "unknown",
|
|
1051
|
+
browserName: "unknown",
|
|
1052
|
+
browserVersion: null,
|
|
1053
|
+
osName: "unknown",
|
|
1054
|
+
osVersion: null
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
userAgent.toLowerCase();
|
|
1058
|
+
let deviceType = "desktop";
|
|
1059
|
+
if (/mobile|android.*mobile|iphone|ipod|blackberry|iemobile|opera mini|opera mobi/i.test(userAgent)) {
|
|
1060
|
+
deviceType = "mobile";
|
|
1061
|
+
} else if (/tablet|ipad|android(?!.*mobile)|kindle|silk/i.test(userAgent)) {
|
|
1062
|
+
deviceType = "tablet";
|
|
1063
|
+
} else if (/bot|crawl|spider|slurp|mediapartners/i.test(userAgent)) {
|
|
1064
|
+
deviceType = "bot";
|
|
1065
|
+
}
|
|
1066
|
+
let browserName = "unknown";
|
|
1067
|
+
let browserVersion = null;
|
|
1068
|
+
if (/edg\//i.test(userAgent)) {
|
|
1069
|
+
browserName = "Edge";
|
|
1070
|
+
browserVersion = extractVersion(userAgent, /edg\/(\d+[\.\d]*)/i);
|
|
1071
|
+
} else if (/opr\//i.test(userAgent) || /opera/i.test(userAgent)) {
|
|
1072
|
+
browserName = "Opera";
|
|
1073
|
+
browserVersion = extractVersion(userAgent, /(?:opr|opera)[\s\/](\d+[\.\d]*)/i);
|
|
1074
|
+
} else if (/chrome|crios/i.test(userAgent) && !/edg/i.test(userAgent)) {
|
|
1075
|
+
browserName = "Chrome";
|
|
1076
|
+
browserVersion = extractVersion(userAgent, /(?:chrome|crios)\/(\d+[\.\d]*)/i);
|
|
1077
|
+
} else if (/firefox|fxios/i.test(userAgent)) {
|
|
1078
|
+
browserName = "Firefox";
|
|
1079
|
+
browserVersion = extractVersion(userAgent, /(?:firefox|fxios)\/(\d+[\.\d]*)/i);
|
|
1080
|
+
} else if (/safari/i.test(userAgent) && !/chrome|chromium/i.test(userAgent)) {
|
|
1081
|
+
browserName = "Safari";
|
|
1082
|
+
browserVersion = extractVersion(userAgent, /version\/(\d+[\.\d]*)/i);
|
|
1083
|
+
} else if (/msie|trident/i.test(userAgent)) {
|
|
1084
|
+
browserName = "Internet Explorer";
|
|
1085
|
+
browserVersion = extractVersion(userAgent, /(?:msie |rv:)(\d+[\.\d]*)/i);
|
|
1086
|
+
}
|
|
1087
|
+
let osName = "unknown";
|
|
1088
|
+
let osVersion = null;
|
|
1089
|
+
if (/windows nt/i.test(userAgent)) {
|
|
1090
|
+
osName = "Windows";
|
|
1091
|
+
const winVersion = extractVersion(userAgent, /windows nt (\d+[\.\d]*)/i);
|
|
1092
|
+
const winVersionMap = {
|
|
1093
|
+
"10.0": "10/11",
|
|
1094
|
+
"6.3": "8.1",
|
|
1095
|
+
"6.2": "8",
|
|
1096
|
+
"6.1": "7",
|
|
1097
|
+
"6.0": "Vista",
|
|
1098
|
+
"5.1": "XP"
|
|
1099
|
+
};
|
|
1100
|
+
osVersion = winVersionMap[winVersion] || winVersion;
|
|
1101
|
+
} else if (/mac os x/i.test(userAgent)) {
|
|
1102
|
+
osName = "macOS";
|
|
1103
|
+
osVersion = extractVersion(userAgent, /mac os x (\d+[_\.\d]*)/i)?.replace(/_/g, ".");
|
|
1104
|
+
} else if (/iphone|ipad|ipod/i.test(userAgent)) {
|
|
1105
|
+
osName = "iOS";
|
|
1106
|
+
osVersion = extractVersion(userAgent, /os (\d+[_\.\d]*)/i)?.replace(/_/g, ".");
|
|
1107
|
+
} else if (/android/i.test(userAgent)) {
|
|
1108
|
+
osName = "Android";
|
|
1109
|
+
osVersion = extractVersion(userAgent, /android (\d+[\.\d]*)/i);
|
|
1110
|
+
} else if (/linux/i.test(userAgent)) {
|
|
1111
|
+
osName = "Linux";
|
|
1112
|
+
} else if (/cros/i.test(userAgent)) {
|
|
1113
|
+
osName = "Chrome OS";
|
|
1114
|
+
}
|
|
1115
|
+
return {
|
|
1116
|
+
deviceType,
|
|
1117
|
+
browserName,
|
|
1118
|
+
browserVersion,
|
|
1119
|
+
osName,
|
|
1120
|
+
osVersion
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
function extractVersion(userAgent, regex) {
|
|
1124
|
+
const match = userAgent.match(regex);
|
|
1125
|
+
return match ? match[1] : null;
|
|
1126
|
+
}
|
|
1127
|
+
var userAgentParser = {
|
|
1128
|
+
parseUserAgent: parseUserAgent$2
|
|
1129
|
+
};
|
|
1130
|
+
const { decryptToken: decryptToken$1, hashToken: hashToken$1 } = encryption;
|
|
1131
|
+
const { parseUserAgent: parseUserAgent$1 } = userAgentParser;
|
|
1046
1132
|
const SESSION_UID$1 = "plugin::magic-sessionmanager.session";
|
|
1047
1133
|
const USER_UID$1 = "plugin::users-permissions.user";
|
|
1048
1134
|
var session$3 = {
|
|
@@ -1088,12 +1174,13 @@ var session$3 = {
|
|
|
1088
1174
|
* Get own sessions (authenticated user)
|
|
1089
1175
|
* GET /api/magic-sessionmanager/my-sessions
|
|
1090
1176
|
* Automatically uses the authenticated user's documentId
|
|
1091
|
-
* Marks which session is the current one (based on JWT token)
|
|
1177
|
+
* Marks which session is the current one (based on JWT token hash)
|
|
1092
1178
|
*/
|
|
1093
1179
|
async getOwnSessions(ctx) {
|
|
1094
1180
|
try {
|
|
1095
1181
|
const userId = ctx.state.user?.documentId;
|
|
1096
1182
|
const currentToken = ctx.request.headers.authorization?.replace("Bearer ", "");
|
|
1183
|
+
const currentTokenHash = currentToken ? hashToken$1(currentToken) : null;
|
|
1097
1184
|
if (!userId) {
|
|
1098
1185
|
return ctx.throw(401, "Unauthorized");
|
|
1099
1186
|
}
|
|
@@ -1108,17 +1195,38 @@ var session$3 = {
|
|
|
1108
1195
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1109
1196
|
const timeSinceActive = now - lastActiveTime;
|
|
1110
1197
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
1111
|
-
|
|
1112
|
-
|
|
1198
|
+
const isCurrentSession = currentTokenHash && session2.tokenHash === currentTokenHash;
|
|
1199
|
+
const parsedUA = parseUserAgent$1(session2.userAgent);
|
|
1200
|
+
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
1201
|
+
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
1202
|
+
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
1203
|
+
let geoLocation = session2.geoLocation;
|
|
1204
|
+
if (typeof geoLocation === "string") {
|
|
1113
1205
|
try {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1206
|
+
geoLocation = JSON.parse(geoLocation);
|
|
1207
|
+
} catch (e) {
|
|
1208
|
+
geoLocation = null;
|
|
1117
1209
|
}
|
|
1118
1210
|
}
|
|
1119
|
-
const {
|
|
1211
|
+
const {
|
|
1212
|
+
token,
|
|
1213
|
+
tokenHash,
|
|
1214
|
+
refreshToken,
|
|
1215
|
+
refreshTokenHash,
|
|
1216
|
+
locale,
|
|
1217
|
+
publishedAt,
|
|
1218
|
+
// Remove Strapi internal fields
|
|
1219
|
+
geoLocation: _geo,
|
|
1220
|
+
// Remove raw geoLocation
|
|
1221
|
+
...sessionWithoutTokens
|
|
1222
|
+
} = session2;
|
|
1120
1223
|
return {
|
|
1121
1224
|
...sessionWithoutTokens,
|
|
1225
|
+
deviceType,
|
|
1226
|
+
browserName,
|
|
1227
|
+
osName,
|
|
1228
|
+
geoLocation,
|
|
1229
|
+
// Parsed object or null
|
|
1122
1230
|
isCurrentSession,
|
|
1123
1231
|
isTrulyActive,
|
|
1124
1232
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
@@ -1180,21 +1288,14 @@ var session$3 = {
|
|
|
1180
1288
|
return ctx.throw(401, "Unauthorized");
|
|
1181
1289
|
}
|
|
1182
1290
|
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
1183
|
-
const
|
|
1291
|
+
const currentTokenHash = hashToken$1(token);
|
|
1292
|
+
const matchingSession = await strapi.documents(SESSION_UID$1).findFirst({
|
|
1184
1293
|
filters: {
|
|
1185
1294
|
user: { documentId: userId },
|
|
1295
|
+
tokenHash: currentTokenHash,
|
|
1186
1296
|
isActive: true
|
|
1187
1297
|
}
|
|
1188
1298
|
});
|
|
1189
|
-
const matchingSession = sessions.find((session2) => {
|
|
1190
|
-
if (!session2.token) return false;
|
|
1191
|
-
try {
|
|
1192
|
-
const decrypted = decryptToken$1(session2.token);
|
|
1193
|
-
return decrypted === token;
|
|
1194
|
-
} catch (err) {
|
|
1195
|
-
return false;
|
|
1196
|
-
}
|
|
1197
|
-
});
|
|
1198
1299
|
if (matchingSession) {
|
|
1199
1300
|
await sessionService.terminateSession({ sessionId: matchingSession.documentId });
|
|
1200
1301
|
strapi.log.info(`[magic-sessionmanager] User ${userId} logged out (session ${matchingSession.documentId})`);
|
|
@@ -1229,7 +1330,7 @@ var session$3 = {
|
|
|
1229
1330
|
}
|
|
1230
1331
|
},
|
|
1231
1332
|
/**
|
|
1232
|
-
* Get current session info based on JWT token
|
|
1333
|
+
* Get current session info based on JWT token hash
|
|
1233
1334
|
* GET /api/magic-sessionmanager/current-session
|
|
1234
1335
|
* Returns the session associated with the current JWT token
|
|
1235
1336
|
*/
|
|
@@ -1240,21 +1341,14 @@ var session$3 = {
|
|
|
1240
1341
|
if (!userId || !token) {
|
|
1241
1342
|
return ctx.throw(401, "Unauthorized");
|
|
1242
1343
|
}
|
|
1243
|
-
const
|
|
1344
|
+
const currentTokenHash = hashToken$1(token);
|
|
1345
|
+
const currentSession = await strapi.documents(SESSION_UID$1).findFirst({
|
|
1244
1346
|
filters: {
|
|
1245
1347
|
user: { documentId: userId },
|
|
1348
|
+
tokenHash: currentTokenHash,
|
|
1246
1349
|
isActive: true
|
|
1247
1350
|
}
|
|
1248
1351
|
});
|
|
1249
|
-
const currentSession = sessions.find((session2) => {
|
|
1250
|
-
if (!session2.token) return false;
|
|
1251
|
-
try {
|
|
1252
|
-
const decrypted = decryptToken$1(session2.token);
|
|
1253
|
-
return decrypted === token;
|
|
1254
|
-
} catch (err) {
|
|
1255
|
-
return false;
|
|
1256
|
-
}
|
|
1257
|
-
});
|
|
1258
1352
|
if (!currentSession) {
|
|
1259
1353
|
return ctx.notFound("Current session not found");
|
|
1260
1354
|
}
|
|
@@ -1263,10 +1357,36 @@ var session$3 = {
|
|
|
1263
1357
|
const now = /* @__PURE__ */ new Date();
|
|
1264
1358
|
const lastActiveTime = currentSession.lastActive ? new Date(currentSession.lastActive) : new Date(currentSession.loginTime);
|
|
1265
1359
|
const timeSinceActive = now - lastActiveTime;
|
|
1266
|
-
const
|
|
1360
|
+
const parsedUA = parseUserAgent$1(currentSession.userAgent);
|
|
1361
|
+
const deviceType = currentSession.deviceType || parsedUA.deviceType;
|
|
1362
|
+
const browserName = currentSession.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
1363
|
+
const osName = currentSession.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
1364
|
+
let geoLocation = currentSession.geoLocation;
|
|
1365
|
+
if (typeof geoLocation === "string") {
|
|
1366
|
+
try {
|
|
1367
|
+
geoLocation = JSON.parse(geoLocation);
|
|
1368
|
+
} catch (e) {
|
|
1369
|
+
geoLocation = null;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
const {
|
|
1373
|
+
token: _,
|
|
1374
|
+
tokenHash: _th,
|
|
1375
|
+
refreshToken: __,
|
|
1376
|
+
refreshTokenHash: _rth,
|
|
1377
|
+
locale: _l,
|
|
1378
|
+
publishedAt: _p,
|
|
1379
|
+
geoLocation: _geo,
|
|
1380
|
+
...sessionWithoutTokens
|
|
1381
|
+
} = currentSession;
|
|
1267
1382
|
ctx.body = {
|
|
1268
1383
|
data: {
|
|
1269
1384
|
...sessionWithoutTokens,
|
|
1385
|
+
deviceType,
|
|
1386
|
+
browserName,
|
|
1387
|
+
osName,
|
|
1388
|
+
geoLocation,
|
|
1389
|
+
// Parsed object or null
|
|
1270
1390
|
isCurrentSession: true,
|
|
1271
1391
|
isTrulyActive: timeSinceActive < inactivityTimeout,
|
|
1272
1392
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
@@ -1287,6 +1407,7 @@ var session$3 = {
|
|
|
1287
1407
|
const userId = ctx.state.user?.documentId;
|
|
1288
1408
|
const { sessionId } = ctx.params;
|
|
1289
1409
|
const currentToken = ctx.request.headers.authorization?.replace("Bearer ", "");
|
|
1410
|
+
const currentTokenHash = currentToken ? hashToken$1(currentToken) : null;
|
|
1290
1411
|
if (!userId) {
|
|
1291
1412
|
return ctx.throw(401, "Unauthorized");
|
|
1292
1413
|
}
|
|
@@ -1305,14 +1426,8 @@ var session$3 = {
|
|
|
1305
1426
|
strapi.log.warn(`[magic-sessionmanager] Security: User ${userId} tried to terminate session ${sessionId} of user ${sessionUserId}`);
|
|
1306
1427
|
return ctx.forbidden("You can only terminate your own sessions");
|
|
1307
1428
|
}
|
|
1308
|
-
if (sessionToTerminate.
|
|
1309
|
-
|
|
1310
|
-
const decrypted = decryptToken$1(sessionToTerminate.token);
|
|
1311
|
-
if (decrypted === currentToken) {
|
|
1312
|
-
return ctx.badRequest("Cannot terminate current session. Use /logout instead.");
|
|
1313
|
-
}
|
|
1314
|
-
} catch (err) {
|
|
1315
|
-
}
|
|
1429
|
+
if (currentTokenHash && sessionToTerminate.tokenHash === currentTokenHash) {
|
|
1430
|
+
return ctx.badRequest("Cannot terminate current session. Use /logout instead.");
|
|
1316
1431
|
}
|
|
1317
1432
|
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
1318
1433
|
await sessionService.terminateSession({ sessionId });
|
|
@@ -1807,6 +1922,7 @@ var controllers$1 = {
|
|
|
1807
1922
|
};
|
|
1808
1923
|
const { encryptToken, decryptToken, generateSessionId, hashToken } = encryption;
|
|
1809
1924
|
const { createLogger: createLogger$1 } = logger;
|
|
1925
|
+
const { parseUserAgent } = userAgentParser;
|
|
1810
1926
|
const SESSION_UID = "plugin::magic-sessionmanager.session";
|
|
1811
1927
|
const USER_UID = "plugin::users-permissions.user";
|
|
1812
1928
|
var session$1 = ({ strapi: strapi2 }) => {
|
|
@@ -1814,10 +1930,10 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1814
1930
|
return {
|
|
1815
1931
|
/**
|
|
1816
1932
|
* Create a new session record
|
|
1817
|
-
* @param {Object} params - { userId, ip, userAgent, token, refreshToken }
|
|
1933
|
+
* @param {Object} params - { userId, ip, userAgent, token, refreshToken, geoData }
|
|
1818
1934
|
* @returns {Promise<Object>} Created session
|
|
1819
1935
|
*/
|
|
1820
|
-
async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken }) {
|
|
1936
|
+
async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken, geoData }) {
|
|
1821
1937
|
try {
|
|
1822
1938
|
const now = /* @__PURE__ */ new Date();
|
|
1823
1939
|
const sessionId = generateSessionId(userId);
|
|
@@ -1825,6 +1941,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1825
1941
|
const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
|
|
1826
1942
|
const tokenHashValue = token ? hashToken(token) : null;
|
|
1827
1943
|
const refreshTokenHashValue = refreshToken ? hashToken(refreshToken) : null;
|
|
1944
|
+
const parsedUA = parseUserAgent(userAgent);
|
|
1828
1945
|
const session2 = await strapi2.documents(SESSION_UID).create({
|
|
1829
1946
|
data: {
|
|
1830
1947
|
user: userId,
|
|
@@ -1842,8 +1959,22 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1842
1959
|
// Encrypted Refresh Token
|
|
1843
1960
|
refreshTokenHash: refreshTokenHashValue,
|
|
1844
1961
|
// SHA-256 hash for fast lookup
|
|
1845
|
-
sessionId
|
|
1962
|
+
sessionId,
|
|
1846
1963
|
// Unique identifier
|
|
1964
|
+
// Device info from User-Agent
|
|
1965
|
+
deviceType: parsedUA.deviceType,
|
|
1966
|
+
browserName: parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName,
|
|
1967
|
+
osName: parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName,
|
|
1968
|
+
// Geolocation data (if available from Premium features)
|
|
1969
|
+
geoLocation: geoData ? JSON.stringify({
|
|
1970
|
+
country: geoData.country,
|
|
1971
|
+
country_code: geoData.country_code,
|
|
1972
|
+
country_flag: geoData.country_flag,
|
|
1973
|
+
city: geoData.city,
|
|
1974
|
+
region: geoData.region,
|
|
1975
|
+
timezone: geoData.timezone
|
|
1976
|
+
}) : null,
|
|
1977
|
+
securityScore: geoData?.securityScore || null
|
|
1847
1978
|
}
|
|
1848
1979
|
});
|
|
1849
1980
|
log.info(`[SUCCESS] Session ${session2.documentId} (${sessionId}) created for user ${userId}`);
|
|
@@ -1921,9 +2052,36 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1921
2052
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1922
2053
|
const timeSinceActive = now - lastActiveTime;
|
|
1923
2054
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
1924
|
-
const
|
|
2055
|
+
const parsedUA = parseUserAgent(session2.userAgent);
|
|
2056
|
+
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2057
|
+
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2058
|
+
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2059
|
+
let geoLocation = session2.geoLocation;
|
|
2060
|
+
if (typeof geoLocation === "string") {
|
|
2061
|
+
try {
|
|
2062
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2063
|
+
} catch (e) {
|
|
2064
|
+
geoLocation = null;
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
const {
|
|
2068
|
+
token,
|
|
2069
|
+
tokenHash,
|
|
2070
|
+
refreshToken,
|
|
2071
|
+
refreshTokenHash,
|
|
2072
|
+
locale,
|
|
2073
|
+
publishedAt,
|
|
2074
|
+
geoLocation: _geo,
|
|
2075
|
+
// Remove raw geoLocation, we use parsed version
|
|
2076
|
+
...safeSession
|
|
2077
|
+
} = session2;
|
|
1925
2078
|
return {
|
|
1926
2079
|
...safeSession,
|
|
2080
|
+
deviceType,
|
|
2081
|
+
browserName,
|
|
2082
|
+
osName,
|
|
2083
|
+
geoLocation,
|
|
2084
|
+
// Parsed object or null
|
|
1927
2085
|
isTrulyActive,
|
|
1928
2086
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1929
2087
|
};
|
|
@@ -1952,9 +2110,34 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1952
2110
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1953
2111
|
const timeSinceActive = now - lastActiveTime;
|
|
1954
2112
|
const isTrulyActive = timeSinceActive < inactivityTimeout;
|
|
1955
|
-
const
|
|
2113
|
+
const parsedUA = parseUserAgent(session2.userAgent);
|
|
2114
|
+
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2115
|
+
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2116
|
+
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2117
|
+
let geoLocation = session2.geoLocation;
|
|
2118
|
+
if (typeof geoLocation === "string") {
|
|
2119
|
+
try {
|
|
2120
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2121
|
+
} catch (e) {
|
|
2122
|
+
geoLocation = null;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
const {
|
|
2126
|
+
token,
|
|
2127
|
+
tokenHash,
|
|
2128
|
+
refreshToken,
|
|
2129
|
+
refreshTokenHash,
|
|
2130
|
+
locale,
|
|
2131
|
+
publishedAt,
|
|
2132
|
+
geoLocation: _geo,
|
|
2133
|
+
...safeSession
|
|
2134
|
+
} = session2;
|
|
1956
2135
|
return {
|
|
1957
2136
|
...safeSession,
|
|
2137
|
+
deviceType,
|
|
2138
|
+
browserName,
|
|
2139
|
+
osName,
|
|
2140
|
+
geoLocation,
|
|
1958
2141
|
isTrulyActive,
|
|
1959
2142
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1960
2143
|
};
|
|
@@ -1991,9 +2174,34 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1991
2174
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1992
2175
|
const timeSinceActive = now - lastActiveTime;
|
|
1993
2176
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
1994
|
-
const
|
|
2177
|
+
const parsedUA = parseUserAgent(session2.userAgent);
|
|
2178
|
+
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2179
|
+
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2180
|
+
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2181
|
+
let geoLocation = session2.geoLocation;
|
|
2182
|
+
if (typeof geoLocation === "string") {
|
|
2183
|
+
try {
|
|
2184
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2185
|
+
} catch (e) {
|
|
2186
|
+
geoLocation = null;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
const {
|
|
2190
|
+
token,
|
|
2191
|
+
tokenHash,
|
|
2192
|
+
refreshToken,
|
|
2193
|
+
refreshTokenHash,
|
|
2194
|
+
locale,
|
|
2195
|
+
publishedAt,
|
|
2196
|
+
geoLocation: _geo,
|
|
2197
|
+
...safeSession
|
|
2198
|
+
} = session2;
|
|
1995
2199
|
return {
|
|
1996
2200
|
...safeSession,
|
|
2201
|
+
deviceType,
|
|
2202
|
+
browserName,
|
|
2203
|
+
osName,
|
|
2204
|
+
geoLocation,
|
|
1997
2205
|
isTrulyActive,
|
|
1998
2206
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1999
2207
|
};
|
|
@@ -2110,7 +2318,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2110
2318
|
}
|
|
2111
2319
|
};
|
|
2112
2320
|
};
|
|
2113
|
-
const version = "4.2.
|
|
2321
|
+
const version = "4.2.11";
|
|
2114
2322
|
const require$$2 = {
|
|
2115
2323
|
version
|
|
2116
2324
|
};
|
package/dist/server/index.mjs
CHANGED
|
@@ -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$
|
|
205
|
+
function hashToken$4(token) {
|
|
206
206
|
if (!token) return null;
|
|
207
207
|
return crypto$1.createHash("sha256").update(token).digest("hex");
|
|
208
208
|
}
|
|
@@ -210,10 +210,10 @@ var encryption = {
|
|
|
210
210
|
encryptToken: encryptToken$2,
|
|
211
211
|
decryptToken: decryptToken$3,
|
|
212
212
|
generateSessionId: generateSessionId$1,
|
|
213
|
-
hashToken: hashToken$
|
|
213
|
+
hashToken: hashToken$4
|
|
214
214
|
};
|
|
215
215
|
const SESSION_UID$3 = "plugin::magic-sessionmanager.session";
|
|
216
|
-
const { hashToken: hashToken$
|
|
216
|
+
const { hashToken: hashToken$3 } = encryption;
|
|
217
217
|
const lastTouchCache = /* @__PURE__ */ new Map();
|
|
218
218
|
var lastSeen = ({ strapi: strapi2 }) => {
|
|
219
219
|
return async (ctx, next) => {
|
|
@@ -229,7 +229,7 @@ var lastSeen = ({ strapi: strapi2 }) => {
|
|
|
229
229
|
}
|
|
230
230
|
let matchingSession = null;
|
|
231
231
|
try {
|
|
232
|
-
const currentTokenHash = hashToken$
|
|
232
|
+
const currentTokenHash = hashToken$3(currentToken);
|
|
233
233
|
matchingSession = await strapi2.documents(SESSION_UID$3).findFirst({
|
|
234
234
|
filters: {
|
|
235
235
|
tokenHash: currentTokenHash,
|
|
@@ -274,7 +274,7 @@ var lastSeen = ({ strapi: strapi2 }) => {
|
|
|
274
274
|
};
|
|
275
275
|
};
|
|
276
276
|
const getClientIp = getClientIp_1;
|
|
277
|
-
const { encryptToken: encryptToken$1, decryptToken: decryptToken$2, hashToken: hashToken$
|
|
277
|
+
const { encryptToken: encryptToken$1, decryptToken: decryptToken$2, hashToken: hashToken$2 } = encryption;
|
|
278
278
|
const { createLogger: createLogger$3 } = logger;
|
|
279
279
|
const SESSION_UID$2 = "plugin::magic-sessionmanager.session";
|
|
280
280
|
const USER_UID$2 = "plugin::users-permissions.user";
|
|
@@ -451,8 +451,10 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
451
451
|
userAgent,
|
|
452
452
|
token: ctx.body.jwt,
|
|
453
453
|
// Store Access Token (encrypted)
|
|
454
|
-
refreshToken: ctx.body.refreshToken
|
|
454
|
+
refreshToken: ctx.body.refreshToken,
|
|
455
455
|
// Store Refresh Token (encrypted) if exists
|
|
456
|
+
geoData
|
|
457
|
+
// Store geolocation data if available
|
|
456
458
|
});
|
|
457
459
|
log.info(`[SUCCESS] Session created for user ${userDocId} (IP: ${ip})`);
|
|
458
460
|
if (geoData && (config2.enableEmailAlerts || config2.enableWebhooks)) {
|
|
@@ -559,8 +561,8 @@ var bootstrap$1 = async ({ strapi: strapi2 }) => {
|
|
|
559
561
|
if (matchingSession) {
|
|
560
562
|
const encryptedToken = newAccessToken ? encryptToken$1(newAccessToken) : matchingSession.token;
|
|
561
563
|
const encryptedRefreshToken = newRefreshToken ? encryptToken$1(newRefreshToken) : matchingSession.refreshToken;
|
|
562
|
-
const newTokenHash = newAccessToken ? hashToken$
|
|
563
|
-
const newRefreshTokenHash = newRefreshToken ? hashToken$
|
|
564
|
+
const newTokenHash = newAccessToken ? hashToken$2(newAccessToken) : matchingSession.tokenHash;
|
|
565
|
+
const newRefreshTokenHash = newRefreshToken ? hashToken$2(newRefreshToken) : matchingSession.refreshTokenHash;
|
|
564
566
|
await strapi2.documents(SESSION_UID$2).update({
|
|
565
567
|
documentId: matchingSession.documentId,
|
|
566
568
|
data: {
|
|
@@ -1038,7 +1040,91 @@ var routes$1 = {
|
|
|
1038
1040
|
admin,
|
|
1039
1041
|
"content-api": contentApi
|
|
1040
1042
|
};
|
|
1041
|
-
|
|
1043
|
+
function parseUserAgent$2(userAgent) {
|
|
1044
|
+
if (!userAgent) {
|
|
1045
|
+
return {
|
|
1046
|
+
deviceType: "unknown",
|
|
1047
|
+
browserName: "unknown",
|
|
1048
|
+
browserVersion: null,
|
|
1049
|
+
osName: "unknown",
|
|
1050
|
+
osVersion: null
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
userAgent.toLowerCase();
|
|
1054
|
+
let deviceType = "desktop";
|
|
1055
|
+
if (/mobile|android.*mobile|iphone|ipod|blackberry|iemobile|opera mini|opera mobi/i.test(userAgent)) {
|
|
1056
|
+
deviceType = "mobile";
|
|
1057
|
+
} else if (/tablet|ipad|android(?!.*mobile)|kindle|silk/i.test(userAgent)) {
|
|
1058
|
+
deviceType = "tablet";
|
|
1059
|
+
} else if (/bot|crawl|spider|slurp|mediapartners/i.test(userAgent)) {
|
|
1060
|
+
deviceType = "bot";
|
|
1061
|
+
}
|
|
1062
|
+
let browserName = "unknown";
|
|
1063
|
+
let browserVersion = null;
|
|
1064
|
+
if (/edg\//i.test(userAgent)) {
|
|
1065
|
+
browserName = "Edge";
|
|
1066
|
+
browserVersion = extractVersion(userAgent, /edg\/(\d+[\.\d]*)/i);
|
|
1067
|
+
} else if (/opr\//i.test(userAgent) || /opera/i.test(userAgent)) {
|
|
1068
|
+
browserName = "Opera";
|
|
1069
|
+
browserVersion = extractVersion(userAgent, /(?:opr|opera)[\s\/](\d+[\.\d]*)/i);
|
|
1070
|
+
} else if (/chrome|crios/i.test(userAgent) && !/edg/i.test(userAgent)) {
|
|
1071
|
+
browserName = "Chrome";
|
|
1072
|
+
browserVersion = extractVersion(userAgent, /(?:chrome|crios)\/(\d+[\.\d]*)/i);
|
|
1073
|
+
} else if (/firefox|fxios/i.test(userAgent)) {
|
|
1074
|
+
browserName = "Firefox";
|
|
1075
|
+
browserVersion = extractVersion(userAgent, /(?:firefox|fxios)\/(\d+[\.\d]*)/i);
|
|
1076
|
+
} else if (/safari/i.test(userAgent) && !/chrome|chromium/i.test(userAgent)) {
|
|
1077
|
+
browserName = "Safari";
|
|
1078
|
+
browserVersion = extractVersion(userAgent, /version\/(\d+[\.\d]*)/i);
|
|
1079
|
+
} else if (/msie|trident/i.test(userAgent)) {
|
|
1080
|
+
browserName = "Internet Explorer";
|
|
1081
|
+
browserVersion = extractVersion(userAgent, /(?:msie |rv:)(\d+[\.\d]*)/i);
|
|
1082
|
+
}
|
|
1083
|
+
let osName = "unknown";
|
|
1084
|
+
let osVersion = null;
|
|
1085
|
+
if (/windows nt/i.test(userAgent)) {
|
|
1086
|
+
osName = "Windows";
|
|
1087
|
+
const winVersion = extractVersion(userAgent, /windows nt (\d+[\.\d]*)/i);
|
|
1088
|
+
const winVersionMap = {
|
|
1089
|
+
"10.0": "10/11",
|
|
1090
|
+
"6.3": "8.1",
|
|
1091
|
+
"6.2": "8",
|
|
1092
|
+
"6.1": "7",
|
|
1093
|
+
"6.0": "Vista",
|
|
1094
|
+
"5.1": "XP"
|
|
1095
|
+
};
|
|
1096
|
+
osVersion = winVersionMap[winVersion] || winVersion;
|
|
1097
|
+
} else if (/mac os x/i.test(userAgent)) {
|
|
1098
|
+
osName = "macOS";
|
|
1099
|
+
osVersion = extractVersion(userAgent, /mac os x (\d+[_\.\d]*)/i)?.replace(/_/g, ".");
|
|
1100
|
+
} else if (/iphone|ipad|ipod/i.test(userAgent)) {
|
|
1101
|
+
osName = "iOS";
|
|
1102
|
+
osVersion = extractVersion(userAgent, /os (\d+[_\.\d]*)/i)?.replace(/_/g, ".");
|
|
1103
|
+
} else if (/android/i.test(userAgent)) {
|
|
1104
|
+
osName = "Android";
|
|
1105
|
+
osVersion = extractVersion(userAgent, /android (\d+[\.\d]*)/i);
|
|
1106
|
+
} else if (/linux/i.test(userAgent)) {
|
|
1107
|
+
osName = "Linux";
|
|
1108
|
+
} else if (/cros/i.test(userAgent)) {
|
|
1109
|
+
osName = "Chrome OS";
|
|
1110
|
+
}
|
|
1111
|
+
return {
|
|
1112
|
+
deviceType,
|
|
1113
|
+
browserName,
|
|
1114
|
+
browserVersion,
|
|
1115
|
+
osName,
|
|
1116
|
+
osVersion
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
function extractVersion(userAgent, regex) {
|
|
1120
|
+
const match = userAgent.match(regex);
|
|
1121
|
+
return match ? match[1] : null;
|
|
1122
|
+
}
|
|
1123
|
+
var userAgentParser = {
|
|
1124
|
+
parseUserAgent: parseUserAgent$2
|
|
1125
|
+
};
|
|
1126
|
+
const { decryptToken: decryptToken$1, hashToken: hashToken$1 } = encryption;
|
|
1127
|
+
const { parseUserAgent: parseUserAgent$1 } = userAgentParser;
|
|
1042
1128
|
const SESSION_UID$1 = "plugin::magic-sessionmanager.session";
|
|
1043
1129
|
const USER_UID$1 = "plugin::users-permissions.user";
|
|
1044
1130
|
var session$3 = {
|
|
@@ -1084,12 +1170,13 @@ var session$3 = {
|
|
|
1084
1170
|
* Get own sessions (authenticated user)
|
|
1085
1171
|
* GET /api/magic-sessionmanager/my-sessions
|
|
1086
1172
|
* Automatically uses the authenticated user's documentId
|
|
1087
|
-
* Marks which session is the current one (based on JWT token)
|
|
1173
|
+
* Marks which session is the current one (based on JWT token hash)
|
|
1088
1174
|
*/
|
|
1089
1175
|
async getOwnSessions(ctx) {
|
|
1090
1176
|
try {
|
|
1091
1177
|
const userId = ctx.state.user?.documentId;
|
|
1092
1178
|
const currentToken = ctx.request.headers.authorization?.replace("Bearer ", "");
|
|
1179
|
+
const currentTokenHash = currentToken ? hashToken$1(currentToken) : null;
|
|
1093
1180
|
if (!userId) {
|
|
1094
1181
|
return ctx.throw(401, "Unauthorized");
|
|
1095
1182
|
}
|
|
@@ -1104,17 +1191,38 @@ var session$3 = {
|
|
|
1104
1191
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1105
1192
|
const timeSinceActive = now - lastActiveTime;
|
|
1106
1193
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
1107
|
-
|
|
1108
|
-
|
|
1194
|
+
const isCurrentSession = currentTokenHash && session2.tokenHash === currentTokenHash;
|
|
1195
|
+
const parsedUA = parseUserAgent$1(session2.userAgent);
|
|
1196
|
+
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
1197
|
+
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
1198
|
+
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
1199
|
+
let geoLocation = session2.geoLocation;
|
|
1200
|
+
if (typeof geoLocation === "string") {
|
|
1109
1201
|
try {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1202
|
+
geoLocation = JSON.parse(geoLocation);
|
|
1203
|
+
} catch (e) {
|
|
1204
|
+
geoLocation = null;
|
|
1113
1205
|
}
|
|
1114
1206
|
}
|
|
1115
|
-
const {
|
|
1207
|
+
const {
|
|
1208
|
+
token,
|
|
1209
|
+
tokenHash,
|
|
1210
|
+
refreshToken,
|
|
1211
|
+
refreshTokenHash,
|
|
1212
|
+
locale,
|
|
1213
|
+
publishedAt,
|
|
1214
|
+
// Remove Strapi internal fields
|
|
1215
|
+
geoLocation: _geo,
|
|
1216
|
+
// Remove raw geoLocation
|
|
1217
|
+
...sessionWithoutTokens
|
|
1218
|
+
} = session2;
|
|
1116
1219
|
return {
|
|
1117
1220
|
...sessionWithoutTokens,
|
|
1221
|
+
deviceType,
|
|
1222
|
+
browserName,
|
|
1223
|
+
osName,
|
|
1224
|
+
geoLocation,
|
|
1225
|
+
// Parsed object or null
|
|
1118
1226
|
isCurrentSession,
|
|
1119
1227
|
isTrulyActive,
|
|
1120
1228
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
@@ -1176,21 +1284,14 @@ var session$3 = {
|
|
|
1176
1284
|
return ctx.throw(401, "Unauthorized");
|
|
1177
1285
|
}
|
|
1178
1286
|
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
1179
|
-
const
|
|
1287
|
+
const currentTokenHash = hashToken$1(token);
|
|
1288
|
+
const matchingSession = await strapi.documents(SESSION_UID$1).findFirst({
|
|
1180
1289
|
filters: {
|
|
1181
1290
|
user: { documentId: userId },
|
|
1291
|
+
tokenHash: currentTokenHash,
|
|
1182
1292
|
isActive: true
|
|
1183
1293
|
}
|
|
1184
1294
|
});
|
|
1185
|
-
const matchingSession = sessions.find((session2) => {
|
|
1186
|
-
if (!session2.token) return false;
|
|
1187
|
-
try {
|
|
1188
|
-
const decrypted = decryptToken$1(session2.token);
|
|
1189
|
-
return decrypted === token;
|
|
1190
|
-
} catch (err) {
|
|
1191
|
-
return false;
|
|
1192
|
-
}
|
|
1193
|
-
});
|
|
1194
1295
|
if (matchingSession) {
|
|
1195
1296
|
await sessionService.terminateSession({ sessionId: matchingSession.documentId });
|
|
1196
1297
|
strapi.log.info(`[magic-sessionmanager] User ${userId} logged out (session ${matchingSession.documentId})`);
|
|
@@ -1225,7 +1326,7 @@ var session$3 = {
|
|
|
1225
1326
|
}
|
|
1226
1327
|
},
|
|
1227
1328
|
/**
|
|
1228
|
-
* Get current session info based on JWT token
|
|
1329
|
+
* Get current session info based on JWT token hash
|
|
1229
1330
|
* GET /api/magic-sessionmanager/current-session
|
|
1230
1331
|
* Returns the session associated with the current JWT token
|
|
1231
1332
|
*/
|
|
@@ -1236,21 +1337,14 @@ var session$3 = {
|
|
|
1236
1337
|
if (!userId || !token) {
|
|
1237
1338
|
return ctx.throw(401, "Unauthorized");
|
|
1238
1339
|
}
|
|
1239
|
-
const
|
|
1340
|
+
const currentTokenHash = hashToken$1(token);
|
|
1341
|
+
const currentSession = await strapi.documents(SESSION_UID$1).findFirst({
|
|
1240
1342
|
filters: {
|
|
1241
1343
|
user: { documentId: userId },
|
|
1344
|
+
tokenHash: currentTokenHash,
|
|
1242
1345
|
isActive: true
|
|
1243
1346
|
}
|
|
1244
1347
|
});
|
|
1245
|
-
const currentSession = sessions.find((session2) => {
|
|
1246
|
-
if (!session2.token) return false;
|
|
1247
|
-
try {
|
|
1248
|
-
const decrypted = decryptToken$1(session2.token);
|
|
1249
|
-
return decrypted === token;
|
|
1250
|
-
} catch (err) {
|
|
1251
|
-
return false;
|
|
1252
|
-
}
|
|
1253
|
-
});
|
|
1254
1348
|
if (!currentSession) {
|
|
1255
1349
|
return ctx.notFound("Current session not found");
|
|
1256
1350
|
}
|
|
@@ -1259,10 +1353,36 @@ var session$3 = {
|
|
|
1259
1353
|
const now = /* @__PURE__ */ new Date();
|
|
1260
1354
|
const lastActiveTime = currentSession.lastActive ? new Date(currentSession.lastActive) : new Date(currentSession.loginTime);
|
|
1261
1355
|
const timeSinceActive = now - lastActiveTime;
|
|
1262
|
-
const
|
|
1356
|
+
const parsedUA = parseUserAgent$1(currentSession.userAgent);
|
|
1357
|
+
const deviceType = currentSession.deviceType || parsedUA.deviceType;
|
|
1358
|
+
const browserName = currentSession.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
1359
|
+
const osName = currentSession.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
1360
|
+
let geoLocation = currentSession.geoLocation;
|
|
1361
|
+
if (typeof geoLocation === "string") {
|
|
1362
|
+
try {
|
|
1363
|
+
geoLocation = JSON.parse(geoLocation);
|
|
1364
|
+
} catch (e) {
|
|
1365
|
+
geoLocation = null;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
const {
|
|
1369
|
+
token: _,
|
|
1370
|
+
tokenHash: _th,
|
|
1371
|
+
refreshToken: __,
|
|
1372
|
+
refreshTokenHash: _rth,
|
|
1373
|
+
locale: _l,
|
|
1374
|
+
publishedAt: _p,
|
|
1375
|
+
geoLocation: _geo,
|
|
1376
|
+
...sessionWithoutTokens
|
|
1377
|
+
} = currentSession;
|
|
1263
1378
|
ctx.body = {
|
|
1264
1379
|
data: {
|
|
1265
1380
|
...sessionWithoutTokens,
|
|
1381
|
+
deviceType,
|
|
1382
|
+
browserName,
|
|
1383
|
+
osName,
|
|
1384
|
+
geoLocation,
|
|
1385
|
+
// Parsed object or null
|
|
1266
1386
|
isCurrentSession: true,
|
|
1267
1387
|
isTrulyActive: timeSinceActive < inactivityTimeout,
|
|
1268
1388
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
@@ -1283,6 +1403,7 @@ var session$3 = {
|
|
|
1283
1403
|
const userId = ctx.state.user?.documentId;
|
|
1284
1404
|
const { sessionId } = ctx.params;
|
|
1285
1405
|
const currentToken = ctx.request.headers.authorization?.replace("Bearer ", "");
|
|
1406
|
+
const currentTokenHash = currentToken ? hashToken$1(currentToken) : null;
|
|
1286
1407
|
if (!userId) {
|
|
1287
1408
|
return ctx.throw(401, "Unauthorized");
|
|
1288
1409
|
}
|
|
@@ -1301,14 +1422,8 @@ var session$3 = {
|
|
|
1301
1422
|
strapi.log.warn(`[magic-sessionmanager] Security: User ${userId} tried to terminate session ${sessionId} of user ${sessionUserId}`);
|
|
1302
1423
|
return ctx.forbidden("You can only terminate your own sessions");
|
|
1303
1424
|
}
|
|
1304
|
-
if (sessionToTerminate.
|
|
1305
|
-
|
|
1306
|
-
const decrypted = decryptToken$1(sessionToTerminate.token);
|
|
1307
|
-
if (decrypted === currentToken) {
|
|
1308
|
-
return ctx.badRequest("Cannot terminate current session. Use /logout instead.");
|
|
1309
|
-
}
|
|
1310
|
-
} catch (err) {
|
|
1311
|
-
}
|
|
1425
|
+
if (currentTokenHash && sessionToTerminate.tokenHash === currentTokenHash) {
|
|
1426
|
+
return ctx.badRequest("Cannot terminate current session. Use /logout instead.");
|
|
1312
1427
|
}
|
|
1313
1428
|
const sessionService = strapi.plugin("magic-sessionmanager").service("session");
|
|
1314
1429
|
await sessionService.terminateSession({ sessionId });
|
|
@@ -1803,6 +1918,7 @@ var controllers$1 = {
|
|
|
1803
1918
|
};
|
|
1804
1919
|
const { encryptToken, decryptToken, generateSessionId, hashToken } = encryption;
|
|
1805
1920
|
const { createLogger: createLogger$1 } = logger;
|
|
1921
|
+
const { parseUserAgent } = userAgentParser;
|
|
1806
1922
|
const SESSION_UID = "plugin::magic-sessionmanager.session";
|
|
1807
1923
|
const USER_UID = "plugin::users-permissions.user";
|
|
1808
1924
|
var session$1 = ({ strapi: strapi2 }) => {
|
|
@@ -1810,10 +1926,10 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1810
1926
|
return {
|
|
1811
1927
|
/**
|
|
1812
1928
|
* Create a new session record
|
|
1813
|
-
* @param {Object} params - { userId, ip, userAgent, token, refreshToken }
|
|
1929
|
+
* @param {Object} params - { userId, ip, userAgent, token, refreshToken, geoData }
|
|
1814
1930
|
* @returns {Promise<Object>} Created session
|
|
1815
1931
|
*/
|
|
1816
|
-
async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken }) {
|
|
1932
|
+
async createSession({ userId, ip = "unknown", userAgent = "unknown", token, refreshToken, geoData }) {
|
|
1817
1933
|
try {
|
|
1818
1934
|
const now = /* @__PURE__ */ new Date();
|
|
1819
1935
|
const sessionId = generateSessionId(userId);
|
|
@@ -1821,6 +1937,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1821
1937
|
const encryptedRefreshToken = refreshToken ? encryptToken(refreshToken) : null;
|
|
1822
1938
|
const tokenHashValue = token ? hashToken(token) : null;
|
|
1823
1939
|
const refreshTokenHashValue = refreshToken ? hashToken(refreshToken) : null;
|
|
1940
|
+
const parsedUA = parseUserAgent(userAgent);
|
|
1824
1941
|
const session2 = await strapi2.documents(SESSION_UID).create({
|
|
1825
1942
|
data: {
|
|
1826
1943
|
user: userId,
|
|
@@ -1838,8 +1955,22 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1838
1955
|
// Encrypted Refresh Token
|
|
1839
1956
|
refreshTokenHash: refreshTokenHashValue,
|
|
1840
1957
|
// SHA-256 hash for fast lookup
|
|
1841
|
-
sessionId
|
|
1958
|
+
sessionId,
|
|
1842
1959
|
// Unique identifier
|
|
1960
|
+
// Device info from User-Agent
|
|
1961
|
+
deviceType: parsedUA.deviceType,
|
|
1962
|
+
browserName: parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName,
|
|
1963
|
+
osName: parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName,
|
|
1964
|
+
// Geolocation data (if available from Premium features)
|
|
1965
|
+
geoLocation: geoData ? JSON.stringify({
|
|
1966
|
+
country: geoData.country,
|
|
1967
|
+
country_code: geoData.country_code,
|
|
1968
|
+
country_flag: geoData.country_flag,
|
|
1969
|
+
city: geoData.city,
|
|
1970
|
+
region: geoData.region,
|
|
1971
|
+
timezone: geoData.timezone
|
|
1972
|
+
}) : null,
|
|
1973
|
+
securityScore: geoData?.securityScore || null
|
|
1843
1974
|
}
|
|
1844
1975
|
});
|
|
1845
1976
|
log.info(`[SUCCESS] Session ${session2.documentId} (${sessionId}) created for user ${userId}`);
|
|
@@ -1917,9 +2048,36 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1917
2048
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1918
2049
|
const timeSinceActive = now - lastActiveTime;
|
|
1919
2050
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
1920
|
-
const
|
|
2051
|
+
const parsedUA = parseUserAgent(session2.userAgent);
|
|
2052
|
+
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2053
|
+
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2054
|
+
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2055
|
+
let geoLocation = session2.geoLocation;
|
|
2056
|
+
if (typeof geoLocation === "string") {
|
|
2057
|
+
try {
|
|
2058
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2059
|
+
} catch (e) {
|
|
2060
|
+
geoLocation = null;
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
const {
|
|
2064
|
+
token,
|
|
2065
|
+
tokenHash,
|
|
2066
|
+
refreshToken,
|
|
2067
|
+
refreshTokenHash,
|
|
2068
|
+
locale,
|
|
2069
|
+
publishedAt,
|
|
2070
|
+
geoLocation: _geo,
|
|
2071
|
+
// Remove raw geoLocation, we use parsed version
|
|
2072
|
+
...safeSession
|
|
2073
|
+
} = session2;
|
|
1921
2074
|
return {
|
|
1922
2075
|
...safeSession,
|
|
2076
|
+
deviceType,
|
|
2077
|
+
browserName,
|
|
2078
|
+
osName,
|
|
2079
|
+
geoLocation,
|
|
2080
|
+
// Parsed object or null
|
|
1923
2081
|
isTrulyActive,
|
|
1924
2082
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1925
2083
|
};
|
|
@@ -1948,9 +2106,34 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1948
2106
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1949
2107
|
const timeSinceActive = now - lastActiveTime;
|
|
1950
2108
|
const isTrulyActive = timeSinceActive < inactivityTimeout;
|
|
1951
|
-
const
|
|
2109
|
+
const parsedUA = parseUserAgent(session2.userAgent);
|
|
2110
|
+
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2111
|
+
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2112
|
+
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2113
|
+
let geoLocation = session2.geoLocation;
|
|
2114
|
+
if (typeof geoLocation === "string") {
|
|
2115
|
+
try {
|
|
2116
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2117
|
+
} catch (e) {
|
|
2118
|
+
geoLocation = null;
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
const {
|
|
2122
|
+
token,
|
|
2123
|
+
tokenHash,
|
|
2124
|
+
refreshToken,
|
|
2125
|
+
refreshTokenHash,
|
|
2126
|
+
locale,
|
|
2127
|
+
publishedAt,
|
|
2128
|
+
geoLocation: _geo,
|
|
2129
|
+
...safeSession
|
|
2130
|
+
} = session2;
|
|
1952
2131
|
return {
|
|
1953
2132
|
...safeSession,
|
|
2133
|
+
deviceType,
|
|
2134
|
+
browserName,
|
|
2135
|
+
osName,
|
|
2136
|
+
geoLocation,
|
|
1954
2137
|
isTrulyActive,
|
|
1955
2138
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1956
2139
|
};
|
|
@@ -1987,9 +2170,34 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
1987
2170
|
const lastActiveTime = session2.lastActive ? new Date(session2.lastActive) : new Date(session2.loginTime);
|
|
1988
2171
|
const timeSinceActive = now - lastActiveTime;
|
|
1989
2172
|
const isTrulyActive = session2.isActive && timeSinceActive < inactivityTimeout;
|
|
1990
|
-
const
|
|
2173
|
+
const parsedUA = parseUserAgent(session2.userAgent);
|
|
2174
|
+
const deviceType = session2.deviceType || parsedUA.deviceType;
|
|
2175
|
+
const browserName = session2.browserName || (parsedUA.browserVersion ? `${parsedUA.browserName} ${parsedUA.browserVersion}` : parsedUA.browserName);
|
|
2176
|
+
const osName = session2.osName || (parsedUA.osVersion ? `${parsedUA.osName} ${parsedUA.osVersion}` : parsedUA.osName);
|
|
2177
|
+
let geoLocation = session2.geoLocation;
|
|
2178
|
+
if (typeof geoLocation === "string") {
|
|
2179
|
+
try {
|
|
2180
|
+
geoLocation = JSON.parse(geoLocation);
|
|
2181
|
+
} catch (e) {
|
|
2182
|
+
geoLocation = null;
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
const {
|
|
2186
|
+
token,
|
|
2187
|
+
tokenHash,
|
|
2188
|
+
refreshToken,
|
|
2189
|
+
refreshTokenHash,
|
|
2190
|
+
locale,
|
|
2191
|
+
publishedAt,
|
|
2192
|
+
geoLocation: _geo,
|
|
2193
|
+
...safeSession
|
|
2194
|
+
} = session2;
|
|
1991
2195
|
return {
|
|
1992
2196
|
...safeSession,
|
|
2197
|
+
deviceType,
|
|
2198
|
+
browserName,
|
|
2199
|
+
osName,
|
|
2200
|
+
geoLocation,
|
|
1993
2201
|
isTrulyActive,
|
|
1994
2202
|
minutesSinceActive: Math.floor(timeSinceActive / 1e3 / 60)
|
|
1995
2203
|
};
|
|
@@ -2106,7 +2314,7 @@ var session$1 = ({ strapi: strapi2 }) => {
|
|
|
2106
2314
|
}
|
|
2107
2315
|
};
|
|
2108
2316
|
};
|
|
2109
|
-
const version = "4.2.
|
|
2317
|
+
const version = "4.2.11";
|
|
2110
2318
|
const require$$2 = {
|
|
2111
2319
|
version
|
|
2112
2320
|
};
|
package/package.json
CHANGED