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/LICENSE +22 -0
- package/README.md +1085 -0
- package/dist/_chunks/Analytics-BSvvI-Hg.js +600 -0
- package/dist/_chunks/Analytics-ZQSBqi6k.mjs +598 -0
- package/dist/_chunks/App-DUVizmi_.js +1955 -0
- package/dist/_chunks/App-MPqlEJVm.mjs +1953 -0
- package/dist/_chunks/License-BI7jDjPV.js +403 -0
- package/dist/_chunks/License-WSbNrJuz.mjs +401 -0
- package/dist/_chunks/OnlineUsersWidget-ArMl0nen.mjs +174 -0
- package/dist/_chunks/OnlineUsersWidget-B8JS1xZu.js +174 -0
- package/dist/_chunks/Settings-Ds8IhBiX.mjs +1322 -0
- package/dist/_chunks/Settings-L1ryVQMS.js +1324 -0
- package/dist/_chunks/de-BxFx1pwE.js +23 -0
- package/dist/_chunks/de-CdO3s01z.mjs +23 -0
- package/dist/_chunks/en-CsPpPJL3.mjs +23 -0
- package/dist/_chunks/en-RqmpDHdS.js +23 -0
- package/dist/_chunks/index-BGCs2pNv.mjs +508 -0
- package/dist/_chunks/index-Dd_SkI79.js +507 -0
- package/dist/_chunks/useLicense-Bw66_4CW.js +85 -0
- package/dist/_chunks/useLicense-Dw_-SJHP.mjs +86 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/server/index.js +2127 -0
- package/dist/server/index.mjs +2125 -0
- package/package.json +89 -0
- package/strapi-admin.js +4 -0
- package/strapi-server.js +4 -0
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)
|
|
6
|
+
[](https://www.npmjs.com/package/strapi-plugin-magic-sessionmanager)
|
|
7
|
+
[](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**
|