strapi-plugin-magic-sessionmanager 1.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 ADDED
@@ -0,0 +1,1085 @@
1
+ # Magic Session Manager 🔐
2
+
3
+ **Advanced Session Management for Strapi v5** - Track user login/logout, monitor active sessions, and secure your application with IP geolocation, threat detection, and real-time analytics.
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+ [![npm version](https://img.shields.io/npm/v/strapi-plugin-magic-sessionmanager.svg)](https://www.npmjs.com/package/strapi-plugin-magic-sessionmanager)
7
+ [![GitHub release](https://img.shields.io/github/v/release/Schero94/Magic-Sessionmanager.svg)](https://github.com/Schero94/Magic-Sessionmanager/releases)
8
+
9
+ ---
10
+
11
+ ## 📋 Table of Contents
12
+
13
+ - [Features](#features)
14
+ - [Quick Start](#quick-start)
15
+ - [How It Works](#how-it-works)
16
+ - [Strapi Integration](#strapi-integration)
17
+ - [Admin Dashboard](#admin-dashboard)
18
+ - [API Routes](#api-routes)
19
+ - [Configuration](#configuration)
20
+ - [Premium Features](#premium-features)
21
+ - [Use Cases](#use-cases)
22
+ - [Testing](#testing)
23
+ - [Troubleshooting](#troubleshooting)
24
+ - [Development](#development)
25
+
26
+ ---
27
+
28
+ ## ✨ Features
29
+
30
+ ### Core Session Management
31
+ ✅ **Automatic Session Tracking** - Sessions created on login, terminated on logout
32
+ ✅ **Session History** - Complete record of all login/logout events with IP & browser
33
+ ✅ **Activity Monitoring** - Track last seen time with rate limiting
34
+ ✅ **Multi-Session Support** - Users can have multiple active sessions
35
+ ✅ **Auto-Cleanup** - Inactive sessions automatically marked inactive
36
+ ✅ **Real-time Dashboard** - View all active & historical sessions
37
+
38
+ ### Security Features (Premium)
39
+ 🔒 **IP Geolocation** - Get country, city, ISP from IP addresses
40
+ 🔒 **Threat Detection** - Identify VPN, proxy, and threat IPs
41
+ 🔒 **Geo-Fencing** - Block/allow logins by country
42
+ 🔒 **Security Scoring** - Risk analysis for each login
43
+ 🔒 **Auto-Blocking** - Prevent logins from high-risk locations
44
+ 🔒 **Email Alerts** - Notify users of suspicious login attempts
45
+ 🔒 **Webhook Notifications** - Send Discord/Slack alerts on key events
46
+
47
+ ### Admin Dashboard
48
+ 📊 **Active Sessions** - Real-time view of logged-in users
49
+ 📊 **Analytics** - Session trends, concurrent users, geo-heatmap
50
+ 📊 **Settings** - Configure timeouts, notifications, geo-restrictions
51
+ 📊 **License Management** - Built-in license activation interface
52
+
53
+ ### Non-Invasive Architecture
54
+ ✅ **No Core Modifications** - Pure plugin, zero changes to Strapi core
55
+ ✅ **Runtime Injection** - Middleware-based architecture
56
+ ✅ **DB-Backed** - Uses `api::session.session` content type
57
+ ✅ **License-Based** - Premium features via license key
58
+
59
+ ---
60
+
61
+ ## 🚀 Quick Start
62
+
63
+ ### 1. Install Plugin
64
+
65
+ ```bash
66
+ npm install strapi-plugin-magic-sessionmanager
67
+ # or
68
+ yarn add strapi-plugin-magic-sessionmanager
69
+ ```
70
+
71
+ ### 2. Register in Config
72
+
73
+ Add to `src/config/plugins.ts` (or `plugins.js`):
74
+
75
+ ```typescript
76
+ export default () => ({
77
+ 'magic-sessionmanager': {
78
+ enabled: true,
79
+ resolve: './src/plugins/magic-sessionmanager',
80
+ config: {
81
+ // Optional: rate limit for lastSeen updates (ms)
82
+ lastSeenRateLimit: 30000, // 30 seconds (default)
83
+
84
+ // Optional: session inactivity timeout (ms)
85
+ inactivityTimeout: 15 * 60 * 1000, // 15 minutes (default)
86
+ },
87
+ },
88
+ });
89
+ ```
90
+
91
+ ### 3. Build & Run
92
+
93
+ ```bash
94
+ # Install dependencies
95
+ npm install
96
+
97
+ # Build the plugin (includes admin UI)
98
+ npm run build
99
+
100
+ # Start Strapi
101
+ npm run develop
102
+ ```
103
+
104
+ ### 4. Access Admin Dashboard
105
+
106
+ - Navigate to Strapi Admin: `http://localhost:1337/admin`
107
+ - Find **Sessions** in the left sidebar under plugins
108
+ - Start with the **License** tab to activate your license
109
+
110
+ ---
111
+
112
+ ## 🔄 How It Works
113
+
114
+ ### Architecture Overview
115
+
116
+ Magic Session Manager works by **intercepting Strapi's native authentication routes** WITHOUT replacing them. It uses middleware to hook into the authentication flow:
117
+
118
+ ```
119
+ ┌─────────────────────────────────────────────────────────┐
120
+ │ Client sends: │
121
+ │ POST /api/auth/local │
122
+ │ { identifier: "user@example.com", password: "pass123" }│
123
+ └────────────────┬────────────────────────────────────────┘
124
+
125
+
126
+ ┌─────────────────────────────────────────────────────────┐
127
+ │ Strapi's Native Auth (users-permissions plugin) │
128
+ │ - Validates credentials │
129
+ │ - Creates JWT token │
130
+ │ - Returns: { jwt: "...", user: {...} } │
131
+ └────────────────┬────────────────────────────────────────┘
132
+
133
+
134
+ ┌─────────────────────────────────────────────────────────┐
135
+ │ Magic Session Manager Middleware (AFTER auth) │
136
+ │ - Detects successful login (status 200 + user object) │
137
+ │ - Extracts: IP, User Agent, JWT Token │
138
+ │ - [PREMIUM] Checks IP geolocation & threat level │
139
+ │ - [PREMIUM] Applies geo-fencing rules │
140
+ │ - Creates session record in database │
141
+ │ - [PREMIUM] Sends notifications (email/webhook) │
142
+ └────────────────┬────────────────────────────────────────┘
143
+
144
+
145
+ ┌─────────────────────────────────────────────────────────┐
146
+ │ Response returned to client (unchanged) │
147
+ │ { jwt: "...", user: {...} } │
148
+ └─────────────────────────────────────────────────────────┘
149
+ ```
150
+
151
+ ### Login Flow (Detailed)
152
+
153
+ ```
154
+ User Login Request
155
+
156
+ [POST /api/auth/local]
157
+ Body: { identifier, password }
158
+
159
+ Strapi Auth validates credentials
160
+
161
+ ✅ Success → Strapi creates JWT token
162
+
163
+ Strapi prepares response: { jwt, user }
164
+
165
+ [Magic Session Manager Middleware INTERCEPTS]
166
+
167
+ Extract from response:
168
+ - user.id
169
+ - ctx.body.jwt (Access Token)
170
+ - IP address (from headers/proxies)
171
+ - User Agent (browser info)
172
+
173
+ [PREMIUM] Check IP Geolocation:
174
+ - Get country, city, ISP
175
+ - Detect VPN/Proxy/Threat
176
+ - Calculate security score (0-100)
177
+ - Apply geo-fencing rules
178
+
179
+ [PREMIUM] Auto-blocking if:
180
+ - Known threat IP (isThreat = true)
181
+ - VPN detected (isVpn = true)
182
+ - Country blocked (not in allowlist)
183
+ - Security score < 50
184
+
185
+ Block? NO → Continue ✅
186
+ Block? YES → Return 403 Forbidden ❌
187
+
188
+ Create api::session.session record:
189
+ {
190
+ user: userId,
191
+ token: jwt, // Access Token
192
+ ipAddress: "192.168.1.100",
193
+ userAgent: "Mozilla/5.0...",
194
+ loginTime: now,
195
+ lastActive: now,
196
+ isActive: true,
197
+ geoLocation: {...}, // Premium
198
+ securityScore: 95 // Premium
199
+ }
200
+
201
+ [PREMIUM] Send notifications:
202
+ - Email alert (if suspicious)
203
+ - Webhook (Discord/Slack)
204
+
205
+ Return response to client (unchanged):
206
+ { jwt: "...", user: {...} }
207
+ ```
208
+
209
+ ### Logout Flow
210
+
211
+ Magic Session Manager **replaces** the default `/api/auth/logout` route:
212
+
213
+ ```
214
+ User Logout Request
215
+
216
+ [POST /api/auth/logout]
217
+ Headers: { Authorization: "Bearer <JWT>" }
218
+
219
+ Magic Session Manager Handler (NOT Strapi's default)
220
+
221
+ Extract JWT from Authorization header
222
+
223
+ Find matching session:
224
+ WHERE token = jwt AND isActive = true
225
+
226
+ Found? YES → Update session:
227
+ {
228
+ isActive: false,
229
+ logoutTime: now
230
+ }
231
+
232
+ Found? NO → Continue anyway (idempotent)
233
+
234
+ Return: { message: "Logged out successfully" }
235
+ ```
236
+
237
+ ### Activity Tracking
238
+
239
+ Every authenticated request updates `lastActive`:
240
+
241
+ ```
242
+ Authenticated API Request
243
+ (Any route with valid JWT)
244
+
245
+ [LastSeen Middleware - BEFORE request]
246
+
247
+ Check: Does user have active session?
248
+ WHERE user.id = X AND isActive = true
249
+
250
+ NO active sessions?
251
+ → Reject: 401 Unauthorized
252
+ → Message: "All sessions terminated. Please login again."
253
+
254
+ Has active session? Continue ✅
255
+
256
+ [Process actual request]
257
+
258
+ [LastSeen Middleware - AFTER request]
259
+
260
+ Check: Was lastActive updated < 30s ago?
261
+ (Rate limiting to prevent DB noise)
262
+
263
+ YES (recently updated) → Skip ⏭️
264
+ NO (old timestamp) → Update session:
265
+ {
266
+ lastActive: now
267
+ }
268
+
269
+ Request complete
270
+ ```
271
+
272
+ ### Periodic Cleanup
273
+
274
+ Runs automatically every 30 minutes:
275
+
276
+ ```
277
+ Cleanup Job (every 30 min)
278
+
279
+ Find sessions where:
280
+ lastActive < (now - inactivityTimeout)
281
+ AND isActive = true
282
+
283
+ For each inactive session:
284
+ Update: isActive = false
285
+
286
+ Log: "Cleaned up X inactive sessions"
287
+ ```
288
+
289
+ ---
290
+
291
+ ## 🔌 Strapi Integration
292
+
293
+ ### Routes Integration
294
+
295
+ #### Native Strapi Routes (Intercepted)
296
+
297
+ | Route | Method | Magic Session Manager Action |
298
+ |-------|--------|------------------------------|
299
+ | `/api/auth/local` | `POST` | **Intercepted** - Middleware runs AFTER Strapi auth creates JWT, then creates session |
300
+ | `/api/auth/local/register` | `POST` | **Intercepted** - Same as login (auto-login after registration) |
301
+
302
+ #### Overridden Routes
303
+
304
+ | Route | Method | Magic Session Manager Action |
305
+ |-------|--------|------------------------------|
306
+ | `/api/auth/logout` | `POST` | **Replaced** - Custom handler terminates session by JWT token |
307
+
308
+ #### Plugin Routes
309
+
310
+ | Route | Method | Purpose |
311
+ |-------|--------|---------|
312
+ | `/api/magic-sessionmanager/logout` | `POST` | Alternative logout endpoint |
313
+ | `/api/magic-sessionmanager/logout-all` | `POST` | Logout from all devices |
314
+ | `/api/magic-sessionmanager/sessions` | `GET` | Get user's sessions |
315
+ | `/api/magic-sessionmanager/user/:id/sessions` | `GET` | Get sessions for specific user |
316
+
317
+ ### JWT Token Handling
318
+
319
+ #### Access Tokens (JWT)
320
+ - **Stored:** YES - in `session.token` field
321
+ - **Used for:** Matching sessions during logout
322
+ - **Expiration:** Controlled by Strapi's JWT config
323
+ - **Validation:** Done by Strapi's auth system (not the plugin)
324
+
325
+ **Important:** When a JWT expires, the session becomes orphaned but remains `isActive = true` until:
326
+ 1. User explicitly logs out
327
+ 2. Inactivity timeout triggers cleanup
328
+ 3. Admin terminates the session
329
+
330
+ #### Refresh Tokens ⚠️ **Critical Limitation**
331
+
332
+ **What are Refresh Tokens?**
333
+ Refresh tokens allow users to get new Access Tokens (JWTs) without re-entering credentials. This enables longer sessions:
334
+
335
+ ```
336
+ Access Token expires after 30 min
337
+
338
+ User still has Refresh Token
339
+
340
+ User requests new Access Token:
341
+ POST /api/auth/refresh-token
342
+
343
+ Strapi issues new JWT
344
+
345
+ User continues without re-login
346
+ ```
347
+
348
+ **The Problem:**
349
+ - **Stored:** NO - Refresh tokens are NOT tracked by this plugin
350
+ - **Reason:** Strapi v5 handles refresh tokens in `users-permissions` plugin
351
+ - **Impact:** User can bypass session termination! ⚠️
352
+
353
+ **Scenario:**
354
+ ```
355
+ Admin terminates user's session
356
+
357
+ Session Manager: isActive = false ❌
358
+
359
+ User's JWT still valid OR
360
+ User has refresh token
361
+
362
+ User gets new JWT via refresh token
363
+
364
+ Plugin creates NEW session automatically! ⚠️
365
+
366
+ User is "logged in" again despite termination
367
+ ```
368
+
369
+ **Current Limitation:**
370
+ This plugin **cannot prevent** users with valid refresh tokens from getting new JWTs. The session termination only affects the current JWT token.
371
+
372
+ **Workarounds:**
373
+
374
+ **Option 1: Disable Refresh Tokens (Strict Control)**
375
+ ```typescript
376
+ // src/config/plugins.ts
377
+ export default () => ({
378
+ 'users-permissions': {
379
+ config: {
380
+ jwt: {
381
+ expiresIn: '30m',
382
+ // Don't issue refresh tokens
383
+ },
384
+ },
385
+ },
386
+ 'magic-sessionmanager': {
387
+ config: {
388
+ inactivityTimeout: 15 * 60 * 1000, // < JWT expiration
389
+ },
390
+ },
391
+ });
392
+ ```
393
+
394
+ **Option 2: Short Refresh Token Expiry**
395
+ ```typescript
396
+ 'users-permissions': {
397
+ config: {
398
+ jwt: {
399
+ expiresIn: '15m', // Short Access Token
400
+ refreshExpiresIn: '30m', // Short Refresh Token
401
+ },
402
+ },
403
+ }
404
+ ```
405
+
406
+ **Option 3: Accept the Limitation**
407
+ Understand that:
408
+ - Session termination stops the **current** JWT
409
+ - Users with refresh tokens can get **new** JWTs
410
+ - New JWTs create **new** sessions
411
+ - Use session analytics to detect unusual patterns
412
+
413
+ **Future Enhancement:**
414
+ To fully block users, the plugin would need to:
415
+ 1. Track refresh tokens (complex)
416
+ 2. Hook into Strapi's token refresh endpoint
417
+ 3. Validate against active sessions before issuing new JWTs
418
+
419
+ This is planned for a future version.
420
+
421
+ ### Multi-Login Behavior
422
+
423
+ **Strapi Default:** Allows multiple simultaneous logins
424
+ **Magic Session Manager:** Tracks each login as separate session
425
+
426
+ ```
427
+ User logs in from:
428
+ - Desktop (Chrome) → Session 1
429
+ - Mobile (Safari) → Session 2
430
+ - Laptop (Firefox) → Session 3
431
+
432
+ All sessions are active simultaneously.
433
+ User can logout from one device without affecting others.
434
+ ```
435
+
436
+ ### Magic Link Integration
437
+
438
+ If you use `strapi-plugin-magic-link`, the session manager automatically detects Magic Link logins:
439
+
440
+ ```javascript
441
+ // bootstrap.js line 140
442
+ const isMagicLink = ctx.path.includes('/magic-link/login') && ctx.method === 'POST';
443
+ ```
444
+
445
+ Sessions are created the same way for Magic Link logins.
446
+
447
+ ---
448
+
449
+ ## 🎛️ Admin Dashboard
450
+
451
+ Access at **Admin → Sessions** (sidebar plugin)
452
+
453
+ ### Tabs Overview
454
+
455
+ #### 1. 📊 **Active Sessions**
456
+ - Real-time list of currently logged-in users
457
+ - Shows: User, IP, Device, Login Time, Last Seen
458
+ - Actions: Terminate session, View details
459
+ - Live status indicators
460
+
461
+ **Features:**
462
+ - Filter by user, device, location
463
+ - Sort by login time, last activity
464
+ - Bulk actions (terminate multiple)
465
+ - Export to CSV
466
+
467
+ #### 2. 📈 **Analytics**
468
+ - Total sessions today/this week/this month
469
+ - Concurrent users graph (real-time)
470
+ - Geo-heatmap (Premium - shows login locations)
471
+ - Device/browser breakdown
472
+ - Peak usage times
473
+ - Average session duration
474
+
475
+ #### 3. ⚙️ **Settings**
476
+
477
+ **Basic Settings:**
478
+ - Rate limits (lastSeen update frequency)
479
+ - Inactivity timeout
480
+ - Cleanup schedule
481
+
482
+ **Premium Settings:**
483
+ - License key activation
484
+ - Geolocation enabled
485
+ - Security scoring enabled
486
+ - Auto-blocking suspicious logins
487
+ - VPN/Proxy alerts
488
+
489
+ **Notification Settings:**
490
+ - Email alerts configuration
491
+ - Suspicious login alerts
492
+ - Discord webhook URL
493
+ - Slack webhook URL
494
+
495
+ **Geo-Fencing:**
496
+ - Country allow/block lists
497
+ - IP whitelist/blacklist
498
+
499
+ #### 4. 🔑 **License**
500
+ - Activate license key
501
+ - View license status & expiry
502
+ - Offline mode information
503
+ - License holder details
504
+ - Auto-ping status (15-minute intervals)
505
+
506
+ ---
507
+
508
+ ## 📡 API Routes
509
+
510
+ ### Content API Routes
511
+
512
+ All require valid JWT authentication (Bearer token).
513
+
514
+ #### Get User Sessions
515
+
516
+ ```bash
517
+ GET /api/magic-sessionmanager/sessions
518
+ Authorization: Bearer YOUR_JWT
519
+
520
+ Response:
521
+ {
522
+ "data": [
523
+ {
524
+ "id": 1,
525
+ "attributes": {
526
+ "loginTime": "2024-01-15T10:30:00Z",
527
+ "lastActive": "2024-01-15T10:35:45Z",
528
+ "logoutTime": null,
529
+ "isActive": true,
530
+ "ipAddress": "192.168.1.100",
531
+ "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
532
+ "token": "eyJhbGciOiJIUzI1NiIs...", // JWT Access Token
533
+ "geoLocation": { // Premium
534
+ "country": "Germany",
535
+ "city": "Berlin",
536
+ "country_code": "DE",
537
+ "latitude": 52.52,
538
+ "longitude": 13.41
539
+ },
540
+ "securityScore": 95 // Premium
541
+ },
542
+ "relationships": {
543
+ "user": { "id": 1, "username": "john" }
544
+ }
545
+ }
546
+ ],
547
+ "meta": { "count": 3 }
548
+ }
549
+ ```
550
+
551
+ #### Logout (Method 1 - Strapi Native)
552
+
553
+ ```bash
554
+ POST /api/auth/logout
555
+ Authorization: Bearer YOUR_JWT
556
+
557
+ Response:
558
+ {
559
+ "message": "Logged out successfully"
560
+ }
561
+
562
+ # This is the REPLACED Strapi route
563
+ # Terminates session matching the JWT token
564
+ ```
565
+
566
+ #### Logout (Method 2 - Plugin Endpoint)
567
+
568
+ ```bash
569
+ POST /api/magic-sessionmanager/logout
570
+ Authorization: Bearer YOUR_JWT
571
+
572
+ Response:
573
+ {
574
+ "message": "Session terminated successfully"
575
+ }
576
+
577
+ # Alternative endpoint with same behavior
578
+ ```
579
+
580
+ #### Logout All Devices
581
+
582
+ ```bash
583
+ POST /api/magic-sessionmanager/logout-all
584
+ Authorization: Bearer YOUR_JWT
585
+
586
+ Response:
587
+ {
588
+ "message": "All sessions terminated",
589
+ "count": 3
590
+ }
591
+
592
+ # Terminates ALL active sessions for the user
593
+ # Useful for "logout everywhere" feature
594
+ ```
595
+
596
+ ---
597
+
598
+ ### Admin API Routes
599
+
600
+ All require **admin authentication**.
601
+
602
+ | Method | Route | Purpose |
603
+ |--------|-------|---------|
604
+ | `GET` | `/magic-sessionmanager/admin/sessions` | Get all sessions (all users) |
605
+ | `GET` | `/magic-sessionmanager/admin/sessions/active` | Get only active sessions |
606
+ | `GET` | `/magic-sessionmanager/admin/user/:userId/sessions` | Get sessions for a user |
607
+ | `POST` | `/magic-sessionmanager/admin/sessions/:sessionId/terminate` | Mark session inactive |
608
+ | `DELETE` | `/magic-sessionmanager/admin/sessions/:sessionId` | Permanently delete session |
609
+ | `POST` | `/magic-sessionmanager/admin/sessions/clean-inactive` | Delete all inactive sessions |
610
+ | `POST` | `/magic-sessionmanager/admin/user/:userId/terminate-all` | Logout user everywhere |
611
+ | `GET` | `/magic-sessionmanager/admin/geolocation/:ipAddress` | Get IP info (Premium) |
612
+ | `GET` | `/magic-sessionmanager/admin/settings` | Get plugin settings |
613
+ | `PUT` | `/magic-sessionmanager/admin/settings` | Update plugin settings |
614
+ | `GET` | `/magic-sessionmanager/admin/license/status` | Get license status |
615
+ | `POST` | `/magic-sessionmanager/admin/license/activate` | Activate license |
616
+
617
+ ---
618
+
619
+ ## ⚙️ Configuration
620
+
621
+ ### Basic Config
622
+
623
+ ```typescript
624
+ // src/config/plugins.ts
625
+ export default () => ({
626
+ 'magic-sessionmanager': {
627
+ enabled: true,
628
+ config: {
629
+ // Rate limit for lastSeen updates (milliseconds)
630
+ // Prevents excessive DB writes
631
+ lastSeenRateLimit: 30000, // 30 seconds (default)
632
+
633
+ // Session inactivity timeout (milliseconds)
634
+ // Sessions inactive longer than this are marked inactive
635
+ inactivityTimeout: 15 * 60 * 1000, // 15 minutes (default)
636
+
637
+ // IMPORTANT: Set this LOWER than your JWT expiration
638
+ // to prevent orphaned sessions
639
+ },
640
+ },
641
+ });
642
+ ```
643
+
644
+ ### Relationship with JWT Config
645
+
646
+ ```typescript
647
+ // src/config/plugins.ts
648
+ export default () => ({
649
+ // Strapi JWT Configuration
650
+ 'users-permissions': {
651
+ config: {
652
+ jwt: {
653
+ expiresIn: '30m', // Access Token expires after 30 minutes
654
+ },
655
+ },
656
+ },
657
+
658
+ // Session Manager Configuration
659
+ 'magic-sessionmanager': {
660
+ enabled: true,
661
+ config: {
662
+ // Set inactivity timeout LOWER than JWT expiration
663
+ // This prevents orphaned sessions when JWT expires
664
+ inactivityTimeout: 15 * 60 * 1000, // 15 minutes < 30 minutes JWT
665
+
666
+ // Or match JWT expiration exactly:
667
+ // inactivityTimeout: 30 * 60 * 1000, // 30 minutes = JWT expiration
668
+ },
669
+ },
670
+ });
671
+ ```
672
+
673
+ ### Premium Config
674
+
675
+ Available through Admin UI **Settings → Sessions → Settings**:
676
+
677
+ ```typescript
678
+ // Settings stored in database via Admin UI
679
+ {
680
+ // Geolocation & Security
681
+ enableGeolocation: true,
682
+ enableSecurityScoring: true,
683
+ blockSuspiciousSessions: true,
684
+ alertOnVpnProxy: true,
685
+
686
+ // Geo-Fencing
687
+ enableGeofencing: true,
688
+ allowedCountries: ["DE", "AT", "CH"], // Germany, Austria, Switzerland
689
+ blockedCountries: ["RU", "CN"], // Russia, China
690
+
691
+ // Notifications
692
+ enableEmailAlerts: true,
693
+ alertOnSuspiciousLogin: true,
694
+ enableWebhooks: true,
695
+ discordWebhookUrl: "https://discord.com/api/webhooks/...",
696
+ slackWebhookUrl: "https://hooks.slack.com/services/...",
697
+ }
698
+ ```
699
+
700
+ ---
701
+
702
+ ## 🔒 Premium Features
703
+
704
+ ### IP Geolocation & Threat Detection
705
+
706
+ Uses **ipapi.co** API for accurate IP information:
707
+
708
+ ```json
709
+ {
710
+ "country": "Germany",
711
+ "country_code": "DE",
712
+ "city": "Berlin",
713
+ "latitude": 52.52,
714
+ "longitude": 13.41,
715
+ "isp": "Deutsche Telekom",
716
+ "isVpn": false,
717
+ "isProxy": false,
718
+ "isThreat": false,
719
+ "securityScore": 95,
720
+ "threatType": null
721
+ }
722
+ ```
723
+
724
+ ### Auto-Blocking Rules
725
+
726
+ ```
727
+ Login attempt from IP: 1.2.3.4
728
+
729
+ [Geolocation Check]
730
+
731
+ isThreat = true → BLOCK ❌
732
+ isVpn = true (if alertOnVpnProxy) → BLOCK ❌
733
+ country = "RU" (if in blockedCountries) → BLOCK ❌
734
+ country ≠ ["DE","AT","CH"] (if allowedCountries set) → BLOCK ❌
735
+ securityScore < 50 → BLOCK ❌
736
+
737
+ None of above? → ALLOW ✅
738
+ ```
739
+
740
+ ### Email Alerts
741
+
742
+ ```
743
+ Subject: ⚠️ Unusual Login Activity
744
+
745
+ Hi John,
746
+
747
+ A login from a new location was detected:
748
+
749
+ 📍 Location: Berlin, Germany
750
+ 🌐 IP Address: 192.168.1.100
751
+ 🔒 Risk Level: Medium (VPN detected)
752
+ ⏰ Time: 2024-01-15 10:30:00 UTC
753
+ 💻 Device: Chrome on Windows
754
+
755
+ If this wasn't you, secure your account immediately.
756
+
757
+ — Magic Session Manager
758
+ ```
759
+
760
+ ### Webhook Notifications
761
+
762
+ **Discord:**
763
+ ```
764
+ 🔓 NEW LOGIN
765
+ ━━━━━━━━━━━━━━━━━━
766
+ User: john@example.com
767
+ IP: 192.168.1.100
768
+ Location: Berlin, Germany
769
+ Risk: ⚠️ Medium (VPN)
770
+ Browser: Chrome / Windows
771
+ Time: 2024-01-15 10:30:00
772
+ ```
773
+
774
+ ---
775
+
776
+ ## 💡 Use Cases
777
+
778
+ ### Force Logout
779
+
780
+ ```bash
781
+ # Admin terminates specific session
782
+ POST /api/magic-sessionmanager/admin/sessions/123/terminate
783
+
784
+ # Admin logs out user from all devices
785
+ POST /api/magic-sessionmanager/admin/user/5/terminate-all
786
+
787
+ # Next API request from that user:
788
+ GET /api/some-endpoint
789
+ Authorization: Bearer <their JWT>
790
+
791
+ # Response: 401 Unauthorized
792
+ # "All sessions have been terminated. Please login again."
793
+ ```
794
+
795
+ ### Security Monitoring
796
+
797
+ ```
798
+ Premium feature: VPN Detection
799
+
800
+ User logs in from VPN
801
+
802
+ isVpn = true detected
803
+
804
+ Email sent: "Suspicious login from VPN"
805
+
806
+ Webhook notification to Slack
807
+
808
+ Admin reviews in dashboard
809
+
810
+ Admin can terminate session if needed
811
+ ```
812
+
813
+ ### Compliance Audit
814
+
815
+ ```
816
+ Export all sessions to CSV:
817
+ - Who logged in
818
+ - When & where (IP, location)
819
+ - Device & browser used
820
+ - Session duration
821
+ - Logout time (if any)
822
+
823
+ Perfect for compliance requirements!
824
+ ```
825
+
826
+ ---
827
+
828
+ ## 🧪 Testing
829
+
830
+ ### 1. Test Login & Session Creation
831
+
832
+ ```bash
833
+ # Login via Strapi's native route
834
+ curl -X POST http://localhost:1337/api/auth/local \
835
+ -H "Content-Type: application/json" \
836
+ -d '{
837
+ "identifier": "test@example.com",
838
+ "password": "Test@123"
839
+ }'
840
+
841
+ # Response:
842
+ {
843
+ "jwt": "eyJhbGciOiJIUzI1NiIs...",
844
+ "user": { "id": 1, "email": "test@example.com", ... }
845
+ }
846
+
847
+ # Save JWT
848
+ export JWT="eyJhbGciOiJIUzI1NiIs..."
849
+
850
+ # Check session was created
851
+ curl http://localhost:1337/api/magic-sessionmanager/sessions \
852
+ -H "Authorization: Bearer $JWT"
853
+
854
+ # Should show new session with:
855
+ # - loginTime
856
+ # - isActive: true
857
+ # - ipAddress
858
+ # - userAgent
859
+ # - token (matches JWT)
860
+ ```
861
+
862
+ ### 2. Test Activity Tracking
863
+
864
+ ```bash
865
+ # First request (updates lastActive)
866
+ curl http://localhost:1337/api/users \
867
+ -H "Authorization: Bearer $JWT"
868
+
869
+ # Check lastActive timestamp
870
+ curl http://localhost:1337/api/magic-sessionmanager/sessions \
871
+ -H "Authorization: Bearer $JWT"
872
+
873
+ # Wait 35 seconds (> 30s rate limit)
874
+ sleep 35
875
+
876
+ # Second request (should update lastActive)
877
+ curl http://localhost:1337/api/users \
878
+ -H "Authorization: Bearer $JWT"
879
+
880
+ # Check lastActive changed
881
+ curl http://localhost:1337/api/magic-sessionmanager/sessions \
882
+ -H "Authorization: Bearer $JWT"
883
+ ```
884
+
885
+ ### 3. Test Logout
886
+
887
+ ```bash
888
+ # Logout via Strapi's route (replaced by plugin)
889
+ curl -X POST http://localhost:1337/api/auth/logout \
890
+ -H "Authorization: Bearer $JWT"
891
+
892
+ # Response: { "message": "Logged out successfully" }
893
+
894
+ # Check session is inactive
895
+ curl http://localhost:1337/api/magic-sessionmanager/sessions \
896
+ -H "Authorization: Bearer $JWT"
897
+
898
+ # Should show:
899
+ # - isActive: false
900
+ # - logoutTime: (timestamp)
901
+ ```
902
+
903
+ ### 4. Test Force Logout
904
+
905
+ ```bash
906
+ # User A terminates all their sessions
907
+ curl -X POST http://localhost:1337/api/magic-sessionmanager/logout-all \
908
+ -H "Authorization: Bearer $JWT_A"
909
+
910
+ # Try to use API with old JWT
911
+ curl http://localhost:1337/api/users \
912
+ -H "Authorization: Bearer $JWT_A"
913
+
914
+ # Response: 401 Unauthorized
915
+ # "All sessions have been terminated. Please login again."
916
+ ```
917
+
918
+ ---
919
+
920
+ ## 🐛 Troubleshooting
921
+
922
+ ### Sessions Not Creating
923
+
924
+ **Problem:** Login succeeds but no session record appears.
925
+
926
+ **Solutions:**
927
+ 1. Check Strapi logs:
928
+ ```bash
929
+ npm run develop
930
+ # Look for: [magic-sessionmanager] 🔍 Login detected!
931
+ # Look for: [magic-sessionmanager] ✅ Session X created
932
+ ```
933
+
934
+ 2. Verify middleware is mounted:
935
+ ```bash
936
+ # Look for: [magic-sessionmanager] ✅ Login/Logout interceptor middleware mounted
937
+ ```
938
+
939
+ 3. Check `api::session.session` collection exists:
940
+ - Go to Admin → Content Manager
941
+ - Look for "Session" collection
942
+
943
+ ### JWT Still Works After Logout
944
+
945
+ **Problem:** After logout, JWT still authenticates API requests.
946
+
947
+ **Explanation:** This is EXPECTED behavior!
948
+ - JWT tokens are **stateless** - validated by signature alone
949
+ - Plugin marks session `isActive = false`
950
+ - But JWT itself remains valid until expiration
951
+ - Next authenticated request is **blocked** by LastSeen middleware
952
+
953
+ **Solution:** This is by design. The middleware blocks requests from users with no active sessions.
954
+
955
+ ### Orphaned Sessions
956
+
957
+ **Problem:** Sessions remain `isActive = true` after JWT expires.
958
+
959
+ **Cause:** JWT expiration > inactivity timeout
960
+
961
+ **Solution:**
962
+ ```typescript
963
+ // Set inactivity timeout LOWER than JWT expiration
964
+ {
965
+ 'magic-sessionmanager': {
966
+ config: {
967
+ inactivityTimeout: 15 * 60 * 1000 // 15 min (if JWT = 30 min)
968
+ }
969
+ }
970
+ }
971
+ ```
972
+
973
+ ### LastSeen Not Updating
974
+
975
+ **Problem:** `lastActive` timestamp doesn't change.
976
+
977
+ **Solutions:**
978
+ 1. Check rate limit:
979
+ ```typescript
980
+ config: {
981
+ lastSeenRateLimit: 5000 // Lower for testing
982
+ }
983
+ ```
984
+
985
+ 2. Wait longer than rate limit (default 30s)
986
+
987
+ 3. Verify middleware mounted:
988
+ ```bash
989
+ # Look for: [magic-sessionmanager] ✅ LastSeen middleware mounted
990
+ ```
991
+
992
+ ---
993
+
994
+ ## 🛠️ Development
995
+
996
+ ### Plugin Structure
997
+
998
+ ```
999
+ magic-sessionmanager/
1000
+ ├── server/src/
1001
+ │ ├── bootstrap.js # ⚙️ CORE: Mounts middlewares & intercepts routes
1002
+ │ ├── middlewares/
1003
+ │ │ └── last-seen.js # 🔄 Updates lastActive on each request
1004
+ │ ├── services/
1005
+ │ │ ├── session.js # 💾 Session CRUD operations
1006
+ │ │ ├── geolocation.js # 🌍 IP geolocation (Premium)
1007
+ │ │ ├── notifications.js # 📧 Email/webhook alerts
1008
+ │ │ └── license-guard.js # 🔑 License validation
1009
+ │ ├── controllers/
1010
+ │ │ ├── session.js # 🎮 Session API handlers
1011
+ │ │ ├── settings.js # ⚙️ Settings API
1012
+ │ │ └── license.js # 🔑 License API
1013
+ │ ├── routes/
1014
+ │ │ ├── content-api.js # 🌐 User-facing routes
1015
+ │ │ └── admin.js # 👑 Admin-only routes
1016
+ │ └── utils/
1017
+ │ └── getClientIp.js # 📍 IP extraction (proxy-aware)
1018
+
1019
+ ├── admin/src/
1020
+ │ ├── pages/
1021
+ │ │ ├── HomePage.jsx # 📊 Main dashboard
1022
+ │ │ ├── ActiveSessions.jsx # 👥 Active sessions tab
1023
+ │ │ ├── Analytics.jsx # 📈 Analytics tab
1024
+ │ │ ├── Settings.jsx # ⚙️ Settings tab
1025
+ │ │ └── License.jsx # 🔑 License tab
1026
+ │ └── components/
1027
+ │ ├── SessionDetailModal.jsx
1028
+ │ └── LicenseGuard.jsx
1029
+
1030
+ ├── .github/workflows/
1031
+ │ ├── semantic-release.yml # 🚀 NPM publishing
1032
+ │ └── test.yml # ✅ CI/CD tests
1033
+
1034
+ ├── package.json
1035
+ ├── .releaserc.json # 📦 semantic-release config
1036
+ └── README.md
1037
+ ```
1038
+
1039
+ ### Build & Release
1040
+
1041
+ ```bash
1042
+ # Build
1043
+ npm run build
1044
+
1045
+ # Release (automatic via semantic commits)
1046
+ git commit -m "feat: add new feature" # → MINOR
1047
+ git commit -m "fix: fix bug" # → PATCH
1048
+ git commit -m "feat!: breaking change" # → MAJOR
1049
+ ```
1050
+
1051
+ ---
1052
+
1053
+ ## 📦 NPM Release Process
1054
+
1055
+ Uses **semantic-release** for automated versioning.
1056
+
1057
+ ### Commit Format
1058
+
1059
+ ```bash
1060
+ feat: add geo-fencing support # → v1.1.0
1061
+ fix: correct session cleanup # → v1.0.1
1062
+ feat!: change API response format # → v2.0.0
1063
+ ```
1064
+
1065
+ GitHub Actions automatically publishes to NPM on push to `main`.
1066
+
1067
+ ---
1068
+
1069
+ ## 📚 Resources
1070
+
1071
+ - **NPM:** https://www.npmjs.com/package/strapi-plugin-magic-sessionmanager
1072
+ - **GitHub:** https://github.com/Schero94/Magic-Sessionmanager
1073
+ - **Issues:** https://github.com/Schero94/Magic-Sessionmanager/issues
1074
+
1075
+ ---
1076
+
1077
+ ## 📄 License
1078
+
1079
+ **MIT License** - Free for personal & commercial use
1080
+
1081
+ **Copyright (c) 2025 Schero D.**
1082
+
1083
+ ---
1084
+
1085
+ **Built with ❤️ for Strapi v5**