strapi-plugin-magic-sessionmanager 3.3.1 → 3.4.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 +294 -1129
- package/package.json +3 -2
- package/pics/dashboard.png +0 -0
- package/pics/dashboardsessionmodal.png +0 -0
- package/pics/sessioninfopanel.png +0 -0
- package/pics/settings1.png +0 -0
- package/pics/settings2.png +0 -0
package/README.md
CHANGED
|
@@ -1,1298 +1,463 @@
|
|
|
1
1
|
# Magic Session Manager 🔐
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**See who's logged into your Strapi app - and control their sessions!**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
[](https://www.npmjs.com/package/strapi-plugin-magic-sessionmanager)
|
|
7
|
-
[](https://github.com/Schero94/Magic-Sessionmanager/releases)
|
|
5
|
+
Track logins, monitor active users, and secure your app with one simple plugin. No complicated setup required.
|
|
8
6
|
|
|
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 `plugin::magic-sessionmanager.session` content type
|
|
57
|
-
✅ **License-Based** - Premium features via license key
|
|
7
|
+
[](https://www.npmjs.com/package/strapi-plugin-magic-sessionmanager)
|
|
8
|
+
[](LICENSE)
|
|
58
9
|
|
|
59
10
|
---
|
|
60
11
|
|
|
61
|
-
##
|
|
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. Configure Encryption (Important!) 🔐
|
|
12
|
+
## 📸 What It Looks Like
|
|
105
13
|
|
|
106
|
-
|
|
14
|
+
### Main Dashboard - See All Active Sessions
|
|
107
15
|
|
|
108
|
-
|
|
109
|
-
# Option 1: Use Admin Panel
|
|
110
|
-
# Go to Admin → Sessions → Settings → Security Settings
|
|
111
|
-
# Click "Generate Key" and copy to .env
|
|
112
|
-
|
|
113
|
-
# Option 2: Generate manually
|
|
114
|
-
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
115
|
-
|
|
116
|
-
# Add to .env file:
|
|
117
|
-
SESSION_ENCRYPTION_KEY=your-generated-32-char-key-here
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
**Why this is important:**
|
|
121
|
-
- JWT tokens are encrypted before storing in database
|
|
122
|
-
- Prevents token exposure if database is compromised
|
|
123
|
-
- Uses AES-256-GCM encryption standard
|
|
16
|
+

|
|
124
17
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
18
|
+
**What you see:**
|
|
19
|
+
- Who is logged in right now (green = online)
|
|
20
|
+
- When they logged in
|
|
21
|
+
- What device they're using
|
|
22
|
+
- Their IP address and location
|
|
23
|
+
- One-click session termination
|
|
131
24
|
|
|
132
25
|
---
|
|
133
26
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
### Architecture Overview
|
|
137
|
-
|
|
138
|
-
Magic Session Manager works by **intercepting Strapi's native authentication routes** WITHOUT replacing them. It uses middleware to hook into the authentication flow:
|
|
139
|
-
|
|
140
|
-
```
|
|
141
|
-
┌─────────────────────────────────────────────────────────┐
|
|
142
|
-
│ Client sends: │
|
|
143
|
-
│ POST /api/auth/local │
|
|
144
|
-
│ { identifier: "user@example.com", password: "pass123" }│
|
|
145
|
-
└────────────────┬────────────────────────────────────────┘
|
|
146
|
-
│
|
|
147
|
-
▼
|
|
148
|
-
┌─────────────────────────────────────────────────────────┐
|
|
149
|
-
│ Strapi's Native Auth (users-permissions plugin) │
|
|
150
|
-
│ - Validates credentials │
|
|
151
|
-
│ - Creates JWT token │
|
|
152
|
-
│ - Returns: { jwt: "...", user: {...} } │
|
|
153
|
-
└────────────────┬────────────────────────────────────────┘
|
|
154
|
-
│
|
|
155
|
-
▼
|
|
156
|
-
┌─────────────────────────────────────────────────────────┐
|
|
157
|
-
│ Magic Session Manager Middleware (AFTER auth) │
|
|
158
|
-
│ - Detects successful login (status 200 + user object) │
|
|
159
|
-
│ - Extracts: IP, User Agent, JWT Token │
|
|
160
|
-
│ - [PREMIUM] Checks IP geolocation & threat level │
|
|
161
|
-
│ - [PREMIUM] Applies geo-fencing rules │
|
|
162
|
-
│ - Creates session record in database │
|
|
163
|
-
│ - [PREMIUM] Sends notifications (email/webhook) │
|
|
164
|
-
└────────────────┬────────────────────────────────────────┘
|
|
165
|
-
│
|
|
166
|
-
▼
|
|
167
|
-
┌─────────────────────────────────────────────────────────┐
|
|
168
|
-
│ Response returned to client (unchanged) │
|
|
169
|
-
│ { jwt: "...", user: {...} } │
|
|
170
|
-
└─────────────────────────────────────────────────────────┘
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Login Flow (Detailed)
|
|
174
|
-
|
|
175
|
-
```
|
|
176
|
-
User Login Request
|
|
177
|
-
↓
|
|
178
|
-
[POST /api/auth/local]
|
|
179
|
-
Body: { identifier, password }
|
|
180
|
-
↓
|
|
181
|
-
Strapi Auth validates credentials
|
|
182
|
-
↓
|
|
183
|
-
✅ Success → Strapi creates JWT token
|
|
184
|
-
↓
|
|
185
|
-
Strapi prepares response: { jwt, user }
|
|
186
|
-
↓
|
|
187
|
-
[Magic Session Manager Middleware INTERCEPTS]
|
|
188
|
-
↓
|
|
189
|
-
Extract from response:
|
|
190
|
-
- user.id
|
|
191
|
-
- ctx.body.jwt (Access Token)
|
|
192
|
-
- IP address (from headers/proxies)
|
|
193
|
-
- User Agent (browser info)
|
|
194
|
-
↓
|
|
195
|
-
[PREMIUM] Check IP Geolocation:
|
|
196
|
-
- Get country, city, ISP
|
|
197
|
-
- Detect VPN/Proxy/Threat
|
|
198
|
-
- Calculate security score (0-100)
|
|
199
|
-
- Apply geo-fencing rules
|
|
200
|
-
↓
|
|
201
|
-
[PREMIUM] Auto-blocking if:
|
|
202
|
-
- Known threat IP (isThreat = true)
|
|
203
|
-
- VPN detected (isVpn = true)
|
|
204
|
-
- Country blocked (not in allowlist)
|
|
205
|
-
- Security score < 50
|
|
206
|
-
↓
|
|
207
|
-
Block? NO → Continue ✅
|
|
208
|
-
Block? YES → Return 403 Forbidden ❌
|
|
209
|
-
↓
|
|
210
|
-
Create plugin::magic-sessionmanager.session record:
|
|
211
|
-
{
|
|
212
|
-
user: userId,
|
|
213
|
-
token: jwt, // Access Token
|
|
214
|
-
ipAddress: "192.168.1.100",
|
|
215
|
-
userAgent: "Mozilla/5.0...",
|
|
216
|
-
loginTime: now,
|
|
217
|
-
lastActive: now,
|
|
218
|
-
isActive: true,
|
|
219
|
-
geoLocation: {...}, // Premium
|
|
220
|
-
securityScore: 95 // Premium
|
|
221
|
-
}
|
|
222
|
-
↓
|
|
223
|
-
[PREMIUM] Send notifications:
|
|
224
|
-
- Email alert (if suspicious)
|
|
225
|
-
- Webhook (Discord/Slack)
|
|
226
|
-
↓
|
|
227
|
-
Return response to client (unchanged):
|
|
228
|
-
{ jwt: "...", user: {...} }
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### Logout Flow
|
|
232
|
-
|
|
233
|
-
Magic Session Manager **replaces** the default `/api/auth/logout` route:
|
|
234
|
-
|
|
235
|
-
```
|
|
236
|
-
User Logout Request
|
|
237
|
-
↓
|
|
238
|
-
[POST /api/auth/logout]
|
|
239
|
-
Headers: { Authorization: "Bearer <JWT>" }
|
|
240
|
-
↓
|
|
241
|
-
Magic Session Manager Handler (NOT Strapi's default)
|
|
242
|
-
↓
|
|
243
|
-
Extract JWT from Authorization header
|
|
244
|
-
↓
|
|
245
|
-
Find matching session:
|
|
246
|
-
WHERE token = jwt AND isActive = true
|
|
247
|
-
↓
|
|
248
|
-
Found? YES → Update session:
|
|
249
|
-
{
|
|
250
|
-
isActive: false,
|
|
251
|
-
logoutTime: now
|
|
252
|
-
}
|
|
253
|
-
↓
|
|
254
|
-
Found? NO → Continue anyway (idempotent)
|
|
255
|
-
↓
|
|
256
|
-
Return: { message: "Logged out successfully" }
|
|
257
|
-
```
|
|
27
|
+
### Session Details Modal
|
|
258
28
|
|
|
259
|
-
|
|
29
|
+

|
|
260
30
|
|
|
261
|
-
|
|
31
|
+
**Click any session to see:**
|
|
32
|
+
- Full device information
|
|
33
|
+
- Browser and operating system
|
|
34
|
+
- Complete session history
|
|
35
|
+
- IP geolocation (Premium)
|
|
36
|
+
- Security risk score (Premium)
|
|
262
37
|
|
|
263
|
-
|
|
264
|
-
Authenticated API Request
|
|
265
|
-
(Any route with valid JWT)
|
|
266
|
-
↓
|
|
267
|
-
[LastSeen Middleware - BEFORE request]
|
|
268
|
-
↓
|
|
269
|
-
Check: Does user have active session?
|
|
270
|
-
WHERE user.id = X AND isActive = true
|
|
271
|
-
↓
|
|
272
|
-
NO active sessions?
|
|
273
|
-
→ Reject: 401 Unauthorized
|
|
274
|
-
→ Message: "All sessions terminated. Please login again."
|
|
275
|
-
↓
|
|
276
|
-
Has active session? Continue ✅
|
|
277
|
-
↓
|
|
278
|
-
[Process actual request]
|
|
279
|
-
↓
|
|
280
|
-
[LastSeen Middleware - AFTER request]
|
|
281
|
-
↓
|
|
282
|
-
Check: Was lastActive updated < 30s ago?
|
|
283
|
-
(Rate limiting to prevent DB noise)
|
|
284
|
-
↓
|
|
285
|
-
YES (recently updated) → Skip ⏭️
|
|
286
|
-
NO (old timestamp) → Update session:
|
|
287
|
-
{
|
|
288
|
-
lastActive: now
|
|
289
|
-
}
|
|
290
|
-
↓
|
|
291
|
-
Request complete
|
|
292
|
-
```
|
|
38
|
+
---
|
|
293
39
|
|
|
294
|
-
###
|
|
40
|
+
### Content Manager Integration
|
|
295
41
|
|
|
296
|
-
|
|
42
|
+

|
|
297
43
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
AND isActive = true
|
|
304
|
-
↓
|
|
305
|
-
For each inactive session:
|
|
306
|
-
Update: isActive = false
|
|
307
|
-
↓
|
|
308
|
-
Log: "Cleaned up X inactive sessions"
|
|
309
|
-
```
|
|
44
|
+
**When viewing a user:**
|
|
45
|
+
- Sidebar shows their active sessions
|
|
46
|
+
- Quick actions (terminate, block)
|
|
47
|
+
- Offline/Online status indicator
|
|
48
|
+
- No need to leave the page!
|
|
310
49
|
|
|
311
50
|
---
|
|
312
51
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
### Routes Integration
|
|
52
|
+
### Settings Page
|
|
316
53
|
|
|
317
|
-
|
|
54
|
+

|
|
318
55
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
56
|
+
**Easy configuration:**
|
|
57
|
+
- Session timeouts
|
|
58
|
+
- Rate limiting
|
|
59
|
+
- Email alerts
|
|
60
|
+
- Webhook notifications
|
|
61
|
+
- Geo-blocking rules
|
|
323
62
|
|
|
324
|
-
|
|
63
|
+

|
|
325
64
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
65
|
+
**Advanced security:**
|
|
66
|
+
- Encryption key generator (one click!)
|
|
67
|
+
- Country allow/block lists
|
|
68
|
+
- VPN detection
|
|
69
|
+
- Threat blocking
|
|
331
70
|
|
|
332
|
-
|
|
333
|
-
|-------|--------|---------|
|
|
334
|
-
| `/api/magic-sessionmanager/logout` | `POST` | Alternative logout endpoint |
|
|
335
|
-
| `/api/magic-sessionmanager/logout-all` | `POST` | Logout from all devices |
|
|
336
|
-
| `/api/magic-sessionmanager/sessions` | `GET` | Get user's sessions |
|
|
337
|
-
| `/api/magic-sessionmanager/user/:id/sessions` | `GET` | Get sessions for specific user |
|
|
71
|
+
---
|
|
338
72
|
|
|
339
|
-
|
|
73
|
+
## ✨ What This Plugin Does
|
|
340
74
|
|
|
341
|
-
|
|
342
|
-
- **Stored:** YES - in `session.token` field
|
|
343
|
-
- **Used for:** Matching sessions during logout
|
|
344
|
-
- **Expiration:** Controlled by Strapi's JWT config
|
|
345
|
-
- **Validation:** Done by Strapi's auth system (not the plugin)
|
|
75
|
+
### Simple Version
|
|
346
76
|
|
|
347
|
-
**
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
77
|
+
**When users login:**
|
|
78
|
+
- Plugin saves who logged in, when, and from where
|
|
79
|
+
- You can see them in the dashboard (see screenshot above)
|
|
80
|
+
- You can force-logout anyone anytime
|
|
351
81
|
|
|
352
|
-
|
|
82
|
+
**When users logout:**
|
|
83
|
+
- Plugin marks their session as "logged out"
|
|
84
|
+
- They disappear from the active sessions list
|
|
353
85
|
|
|
354
|
-
**
|
|
355
|
-
|
|
86
|
+
**While users are active:**
|
|
87
|
+
- Plugin updates their "last seen" time
|
|
88
|
+
- You always know who's currently using your app
|
|
356
89
|
|
|
357
|
-
|
|
358
|
-
Access Token expires after 30 min
|
|
359
|
-
↓
|
|
360
|
-
User still has Refresh Token
|
|
361
|
-
↓
|
|
362
|
-
User requests new Access Token:
|
|
363
|
-
POST /api/auth/refresh
|
|
364
|
-
↓
|
|
365
|
-
Strapi issues new JWT
|
|
366
|
-
↓
|
|
367
|
-
User continues without re-login
|
|
368
|
-
```
|
|
90
|
+
---
|
|
369
91
|
|
|
370
|
-
|
|
371
|
-
- **Stored:** YES - Refresh tokens are encrypted and stored with sessions ✅
|
|
372
|
-
- **Tracked:** YES - Middleware intercepts `/api/auth/refresh` requests ✅
|
|
373
|
-
- **Validated:** YES - Checks if session is still active before issuing new tokens ✅
|
|
92
|
+
## 🚀 Quick Install
|
|
374
93
|
|
|
375
|
-
|
|
94
|
+
### Step 1: Install
|
|
376
95
|
|
|
96
|
+
```bash
|
|
97
|
+
npm install strapi-plugin-magic-sessionmanager
|
|
377
98
|
```
|
|
378
|
-
Login: User gets JWT + Refresh Token
|
|
379
|
-
↓
|
|
380
|
-
Both tokens encrypted and stored in session
|
|
381
|
-
↓
|
|
382
|
-
Admin terminates session
|
|
383
|
-
↓
|
|
384
|
-
Session: isActive = false ❌
|
|
385
|
-
↓
|
|
386
|
-
User tries to refresh token:
|
|
387
|
-
POST /api/auth/refresh
|
|
388
|
-
{ refreshToken: "..." }
|
|
389
|
-
↓
|
|
390
|
-
[Refresh Token Middleware]
|
|
391
|
-
↓
|
|
392
|
-
Decrypt all active session refresh tokens
|
|
393
|
-
↓
|
|
394
|
-
Find matching session
|
|
395
|
-
↓
|
|
396
|
-
Session found but isActive = false?
|
|
397
|
-
→ BLOCK! Return 401 Unauthorized ❌
|
|
398
|
-
→ Message: "Session terminated. Please login again."
|
|
399
|
-
↓
|
|
400
|
-
Session found and isActive = true?
|
|
401
|
-
→ ALLOW! ✅
|
|
402
|
-
→ Strapi issues new tokens
|
|
403
|
-
→ Session updated with new encrypted tokens
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
**Security Benefits:**
|
|
407
|
-
|
|
408
|
-
✅ **Session termination is FINAL** - User cannot get new tokens
|
|
409
|
-
✅ **Refresh tokens tracked** - Encrypted & stored securely
|
|
410
|
-
✅ **Token rotation** - New tokens automatically updated in session
|
|
411
|
-
✅ **Admin control** - Force logout works even with refresh tokens
|
|
412
99
|
|
|
413
|
-
|
|
100
|
+
### Step 2: Enable Plugin
|
|
414
101
|
|
|
415
|
-
|
|
102
|
+
Add this to `config/plugins.ts`:
|
|
416
103
|
|
|
417
104
|
```typescript
|
|
418
|
-
// src/config/plugins.ts
|
|
419
105
|
export default () => ({
|
|
420
|
-
'users-permissions': {
|
|
421
|
-
config: {
|
|
422
|
-
jwtManagement: 'refresh', // Enable refresh tokens
|
|
423
|
-
sessions: {
|
|
424
|
-
accessTokenLifespan: 3600, // 1 hour (in seconds)
|
|
425
|
-
maxRefreshTokenLifespan: 2592000, // 30 days
|
|
426
|
-
idleRefreshTokenLifespan: 604800, // 7 days idle
|
|
427
|
-
},
|
|
428
|
-
},
|
|
429
|
-
},
|
|
430
106
|
'magic-sessionmanager': {
|
|
431
107
|
enabled: true,
|
|
432
|
-
config: {
|
|
433
|
-
inactivityTimeout: 15 * 60 * 1000, // 15 minutes
|
|
434
|
-
},
|
|
435
108
|
},
|
|
436
109
|
});
|
|
437
110
|
```
|
|
438
111
|
|
|
439
|
-
|
|
112
|
+
### Step 3: Rebuild & Start
|
|
440
113
|
|
|
441
114
|
```bash
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
-H "Content-Type: application/json" \
|
|
445
|
-
-d '{"identifier":"user@example.com","password":"pass"}'
|
|
446
|
-
|
|
447
|
-
# Save both tokens:
|
|
448
|
-
ACCESS_TOKEN="eyJhbGci..."
|
|
449
|
-
REFRESH_TOKEN="abc123..."
|
|
450
|
-
|
|
451
|
-
# 2. Admin terminates session
|
|
452
|
-
# Go to Admin → Sessions → Find session → Terminate
|
|
453
|
-
|
|
454
|
-
# 3. Try to refresh token
|
|
455
|
-
curl -X POST http://localhost:1337/api/auth/refresh \
|
|
456
|
-
-H "Content-Type: application/json" \
|
|
457
|
-
-d "{\"refreshToken\":\"$REFRESH_TOKEN\"}"
|
|
458
|
-
|
|
459
|
-
# Expected: 401 Unauthorized
|
|
460
|
-
# "Session terminated. Please login again."
|
|
115
|
+
npm run build
|
|
116
|
+
npm run develop
|
|
461
117
|
```
|
|
462
118
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
### Without Refresh Tokens (Default Behavior)
|
|
466
|
-
|
|
467
|
-
If you **don't enable** refresh tokens (`jwtManagement: 'refresh'`):
|
|
119
|
+
### Step 4: Open Dashboard
|
|
468
120
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
↓
|
|
474
|
-
JWT expires after 30 min (or configured time)
|
|
475
|
-
↓
|
|
476
|
-
User must re-login ❌
|
|
477
|
-
↓
|
|
478
|
-
No automatic token refresh
|
|
479
|
-
```
|
|
121
|
+
1. Go to Strapi Admin: `http://localhost:1337/admin`
|
|
122
|
+
2. Look in the left sidebar for **"Sessions"**
|
|
123
|
+
3. Click it!
|
|
124
|
+
4. You'll see the dashboard (like the screenshot above)
|
|
480
125
|
|
|
481
|
-
**
|
|
482
|
-
- ✅ Session Manager works normally
|
|
483
|
-
- ✅ Sessions tracked, logout works
|
|
484
|
-
- ✅ Force logout works (no refresh token bypass possible)
|
|
485
|
-
- ⚠️ Users must re-login when JWT expires
|
|
486
|
-
- ℹ️ No refresh token middleware runs (skipped)
|
|
126
|
+
**That's it! You're done!** 🎉
|
|
487
127
|
|
|
488
|
-
|
|
489
|
-
```
|
|
490
|
-
[magic-sessionmanager] ✅ Session created for user 1 (IP: 192.168.1.1)
|
|
491
|
-
[magic-sessionmanager] ℹ️ No refresh token in response (JWT management not enabled)
|
|
492
|
-
[magic-sessionmanager] ✅ Refresh Token interceptor middleware mounted
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
**If you try to call `/api/auth/refresh` without enabling it:**
|
|
496
|
-
- Endpoint returns **404 Not Found** (Strapi doesn't create the route)
|
|
497
|
-
- Or returns **401 Unauthorized** if route exists but tokens not configured
|
|
498
|
-
- This is expected and correct behavior
|
|
128
|
+
---
|
|
499
129
|
|
|
500
|
-
|
|
130
|
+
## 🔐 Security Features (Optional)
|
|
501
131
|
|
|
502
|
-
|
|
503
|
-
|---------|---------------------|------------------------|
|
|
504
|
-
| User Experience | ✅ Seamless (auto-refresh) | ⚠️ Must re-login |
|
|
505
|
-
| Security | ✅ Tracked & blockable | ✅ No bypass risk |
|
|
506
|
-
| Session Duration | Long (days/weeks) | Short (hours) |
|
|
507
|
-
| Force Logout | ✅ Complete | ✅ Complete |
|
|
132
|
+
### Encryption Key (Recommended)
|
|
508
133
|
|
|
509
|
-
|
|
134
|
+
Your JWT tokens are encrypted before saving to database. Generate a key:
|
|
510
135
|
|
|
511
|
-
**
|
|
136
|
+
**In Admin Panel:**
|
|
137
|
+
1. Go to **Sessions → Settings**
|
|
138
|
+
2. Scroll to **"JWT Encryption Key Generator"**
|
|
139
|
+
3. Click **"Generate Key"**
|
|
140
|
+
4. Click **"Copy for .env"**
|
|
141
|
+
5. Paste into your `.env` file
|
|
142
|
+
6. Restart Strapi
|
|
512
143
|
|
|
513
|
-
**
|
|
144
|
+
**Or generate manually:**
|
|
145
|
+
```bash
|
|
146
|
+
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
147
|
+
```
|
|
514
148
|
|
|
149
|
+
Then add to `.env`:
|
|
515
150
|
```
|
|
516
|
-
|
|
517
|
-
POST /api/auth/local
|
|
518
|
-
→ Save: jwt, refreshToken, session_id
|
|
519
|
-
|
|
520
|
-
2. Refresh Token (should work)
|
|
521
|
-
POST /api/auth/refresh
|
|
522
|
-
Body: { "refreshToken": "..." }
|
|
523
|
-
→ Returns: New jwt + refreshToken ✅
|
|
524
|
-
|
|
525
|
-
3. Admin terminates session
|
|
526
|
-
POST /magic-sessionmanager/sessions/:id/terminate
|
|
527
|
-
|
|
528
|
-
4. Try refresh token again
|
|
529
|
-
POST /api/auth/refresh
|
|
530
|
-
Body: { "refreshToken": "..." }
|
|
531
|
-
→ Returns: 401 Unauthorized ✅
|
|
532
|
-
→ Message: "Session terminated. Please login again."
|
|
151
|
+
SESSION_ENCRYPTION_KEY=your-key-here
|
|
533
152
|
```
|
|
534
153
|
|
|
535
|
-
**
|
|
536
|
-
|
|
537
|
-
```bash
|
|
538
|
-
cd /path/to/magic-sessionmanager
|
|
154
|
+
**Why?** If someone hacks your database, they can't steal user sessions! 🔒
|
|
539
155
|
|
|
540
|
-
|
|
541
|
-
export TEST_USER_EMAIL=user@example.com
|
|
542
|
-
export TEST_USER_PASSWORD=password123
|
|
543
|
-
export ADMIN_EMAIL=admin@example.com
|
|
544
|
-
export ADMIN_PASSWORD=adminpass
|
|
156
|
+
---
|
|
545
157
|
|
|
546
|
-
|
|
547
|
-
node test-session-manager.js
|
|
158
|
+
## 🎯 Main Features Explained Simply
|
|
548
159
|
|
|
549
|
-
|
|
550
|
-
# Should show: ✅ Refresh token BLOCKED as expected!
|
|
551
|
-
```
|
|
160
|
+
### 1. See Who's Logged In
|
|
552
161
|
|
|
553
|
-
|
|
162
|
+
**Dashboard Tab:**
|
|
163
|
+
- Shows all active users
|
|
164
|
+
- Green badge = currently online
|
|
165
|
+
- Gray badge = logged out
|
|
166
|
+
- Click to see details
|
|
554
167
|
|
|
555
|
-
|
|
556
|
-
**Magic Session Manager:** Tracks each login as separate session
|
|
168
|
+
### 2. Force Logout Anyone
|
|
557
169
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
- Laptop (Firefox) → Session 3
|
|
170
|
+
**Need to kick someone out?**
|
|
171
|
+
1. Find their session
|
|
172
|
+
2. Click **"Terminate"**
|
|
173
|
+
3. Done! They're logged out immediately
|
|
563
174
|
|
|
564
|
-
|
|
565
|
-
User can logout from one device without affecting others.
|
|
566
|
-
```
|
|
175
|
+
**Even works if they have refresh tokens!** (See below)
|
|
567
176
|
|
|
568
|
-
###
|
|
177
|
+
### 3. Session Details
|
|
569
178
|
|
|
570
|
-
|
|
179
|
+
**Click any session to see:**
|
|
180
|
+
- When they logged in
|
|
181
|
+
- Last time they did something
|
|
182
|
+
- What browser/device they use
|
|
183
|
+
- Their IP address
|
|
184
|
+
- Location (if Premium)
|
|
571
185
|
|
|
572
|
-
|
|
573
|
-
// bootstrap.js line 140
|
|
574
|
-
const isMagicLink = ctx.path.includes('/magic-link/login') && ctx.method === 'POST';
|
|
575
|
-
```
|
|
186
|
+
### 4. Multiple Devices
|
|
576
187
|
|
|
577
|
-
|
|
188
|
+
**Users can login from:**
|
|
189
|
+
- Desktop computer
|
|
190
|
+
- Phone
|
|
191
|
+
- Tablet
|
|
192
|
+
- All at the same time!
|
|
578
193
|
|
|
579
|
-
|
|
194
|
+
Each login = separate session. You can see them all and logout each individually.
|
|
580
195
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
Access at **Admin → Sessions** (sidebar plugin)
|
|
584
|
-
|
|
585
|
-
### Tabs Overview
|
|
586
|
-
|
|
587
|
-
#### 1. 📊 **Active Sessions**
|
|
588
|
-
- Real-time list of currently logged-in users
|
|
589
|
-
- Shows: User, IP, Device, Login Time, Last Seen
|
|
590
|
-
- Actions: Terminate session, View details
|
|
591
|
-
- Live status indicators
|
|
592
|
-
|
|
593
|
-
**Features:**
|
|
594
|
-
- Filter by user, device, location
|
|
595
|
-
- Sort by login time, last activity
|
|
596
|
-
- Bulk actions (terminate multiple)
|
|
597
|
-
- Export to CSV
|
|
598
|
-
|
|
599
|
-
#### 2. 📈 **Analytics**
|
|
600
|
-
- Total sessions today/this week/this month
|
|
601
|
-
- Concurrent users graph (real-time)
|
|
602
|
-
- Geo-heatmap (Premium - shows login locations)
|
|
603
|
-
- Device/browser breakdown
|
|
604
|
-
- Peak usage times
|
|
605
|
-
- Average session duration
|
|
606
|
-
|
|
607
|
-
#### 3. ⚙️ **Settings**
|
|
608
|
-
|
|
609
|
-
**Basic Settings:**
|
|
610
|
-
- Rate limits (lastSeen update frequency)
|
|
611
|
-
- Inactivity timeout
|
|
612
|
-
- Cleanup schedule
|
|
613
|
-
|
|
614
|
-
**Premium Settings:**
|
|
615
|
-
- License key activation
|
|
616
|
-
- Geolocation enabled
|
|
617
|
-
- Security scoring enabled
|
|
618
|
-
- Auto-blocking suspicious logins
|
|
619
|
-
- VPN/Proxy alerts
|
|
620
|
-
|
|
621
|
-
**Notification Settings:**
|
|
622
|
-
- Email alerts configuration
|
|
623
|
-
- Suspicious login alerts
|
|
624
|
-
- Discord webhook URL
|
|
625
|
-
- Slack webhook URL
|
|
626
|
-
|
|
627
|
-
**Geo-Fencing:**
|
|
628
|
-
- Country allow/block lists
|
|
629
|
-
- IP whitelist/blacklist
|
|
196
|
+
### 5. Auto-Cleanup
|
|
630
197
|
|
|
631
|
-
|
|
632
|
-
-
|
|
633
|
-
-
|
|
634
|
-
-
|
|
635
|
-
- License holder details
|
|
636
|
-
- Auto-ping status (15-minute intervals)
|
|
198
|
+
**Inactive sessions are automatically cleaned up:**
|
|
199
|
+
- If user doesn't do anything for 15 minutes (configurable)
|
|
200
|
+
- Session is marked as "inactive"
|
|
201
|
+
- Keeps your database clean
|
|
637
202
|
|
|
638
203
|
---
|
|
639
204
|
|
|
640
|
-
##
|
|
205
|
+
## 🔒 Refresh Token Protection (Advanced)
|
|
641
206
|
|
|
642
|
-
###
|
|
207
|
+
### The Problem (Without This Plugin)
|
|
643
208
|
|
|
644
|
-
All require valid JWT authentication (Bearer token).
|
|
645
|
-
|
|
646
|
-
#### Get User Sessions
|
|
647
|
-
|
|
648
|
-
```bash
|
|
649
|
-
GET /api/magic-sessionmanager/sessions
|
|
650
|
-
Authorization: Bearer YOUR_JWT
|
|
651
|
-
|
|
652
|
-
Response:
|
|
653
|
-
{
|
|
654
|
-
"data": [
|
|
655
|
-
{
|
|
656
|
-
"id": 1,
|
|
657
|
-
"attributes": {
|
|
658
|
-
"loginTime": "2024-01-15T10:30:00Z",
|
|
659
|
-
"lastActive": "2024-01-15T10:35:45Z",
|
|
660
|
-
"logoutTime": null,
|
|
661
|
-
"isActive": true,
|
|
662
|
-
"ipAddress": "192.168.1.100",
|
|
663
|
-
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
|
|
664
|
-
"token": "eyJhbGciOiJIUzI1NiIs...", // JWT Access Token
|
|
665
|
-
"geoLocation": { // Premium
|
|
666
|
-
"country": "Germany",
|
|
667
|
-
"city": "Berlin",
|
|
668
|
-
"country_code": "DE",
|
|
669
|
-
"latitude": 52.52,
|
|
670
|
-
"longitude": 13.41
|
|
671
|
-
},
|
|
672
|
-
"securityScore": 95 // Premium
|
|
673
|
-
},
|
|
674
|
-
"relationships": {
|
|
675
|
-
"user": { "id": 1, "username": "john" }
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
],
|
|
679
|
-
"meta": { "count": 3 }
|
|
680
|
-
}
|
|
681
209
|
```
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
Response:
|
|
690
|
-
{
|
|
691
|
-
"message": "Logged out successfully"
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
# This is the REPLACED Strapi route
|
|
695
|
-
# Terminates session matching the JWT token
|
|
210
|
+
Admin kicks out a user
|
|
211
|
+
↓
|
|
212
|
+
User has "refresh token"
|
|
213
|
+
↓
|
|
214
|
+
User gets new login token automatically
|
|
215
|
+
↓
|
|
216
|
+
User is back in! 😱
|
|
696
217
|
```
|
|
697
218
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
```bash
|
|
701
|
-
POST /api/magic-sessionmanager/logout
|
|
702
|
-
Authorization: Bearer YOUR_JWT
|
|
703
|
-
|
|
704
|
-
Response:
|
|
705
|
-
{
|
|
706
|
-
"message": "Session terminated successfully"
|
|
707
|
-
}
|
|
219
|
+
### The Solution (With This Plugin)
|
|
708
220
|
|
|
709
|
-
# Alternative endpoint with same behavior
|
|
710
221
|
```
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
Response:
|
|
719
|
-
{
|
|
720
|
-
"message": "All sessions terminated",
|
|
721
|
-
"count": 3
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
# Terminates ALL active sessions for the user
|
|
725
|
-
# Useful for "logout everywhere" feature
|
|
222
|
+
Admin kicks out a user
|
|
223
|
+
↓
|
|
224
|
+
User tries to use refresh token
|
|
225
|
+
↓
|
|
226
|
+
Plugin blocks it! 🚫
|
|
227
|
+
↓
|
|
228
|
+
User MUST login again
|
|
726
229
|
```
|
|
727
230
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
### Admin API Routes
|
|
731
|
-
|
|
732
|
-
All require **admin authentication**.
|
|
733
|
-
|
|
734
|
-
| Method | Route | Purpose |
|
|
735
|
-
|--------|-------|---------|
|
|
736
|
-
| `GET` | `/magic-sessionmanager/admin/sessions` | Get all sessions (all users) |
|
|
737
|
-
| `GET` | `/magic-sessionmanager/admin/sessions/active` | Get only active sessions |
|
|
738
|
-
| `GET` | `/magic-sessionmanager/admin/user/:userId/sessions` | Get sessions for a user |
|
|
739
|
-
| `POST` | `/magic-sessionmanager/admin/sessions/:sessionId/terminate` | Mark session inactive |
|
|
740
|
-
| `DELETE` | `/magic-sessionmanager/admin/sessions/:sessionId` | Permanently delete session |
|
|
741
|
-
| `POST` | `/magic-sessionmanager/admin/sessions/clean-inactive` | Delete all inactive sessions |
|
|
742
|
-
| `POST` | `/magic-sessionmanager/admin/user/:userId/terminate-all` | Logout user everywhere |
|
|
743
|
-
| `GET` | `/magic-sessionmanager/admin/geolocation/:ipAddress` | Get IP info (Premium) |
|
|
744
|
-
| `GET` | `/magic-sessionmanager/admin/settings` | Get plugin settings |
|
|
745
|
-
| `PUT` | `/magic-sessionmanager/admin/settings` | Update plugin settings |
|
|
746
|
-
| `GET` | `/magic-sessionmanager/admin/license/status` | Get license status |
|
|
747
|
-
| `POST` | `/magic-sessionmanager/admin/license/activate` | Activate license |
|
|
748
|
-
|
|
749
|
-
---
|
|
750
|
-
|
|
751
|
-
## ⚙️ Configuration
|
|
231
|
+
**How to enable:**
|
|
752
232
|
|
|
753
|
-
|
|
233
|
+
Add to `config/plugins.ts`:
|
|
754
234
|
|
|
755
235
|
```typescript
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
//
|
|
762
|
-
// Prevents excessive DB writes
|
|
763
|
-
lastSeenRateLimit: 30000, // 30 seconds (default)
|
|
764
|
-
|
|
765
|
-
// Session inactivity timeout (milliseconds)
|
|
766
|
-
// Sessions inactive longer than this are marked inactive
|
|
767
|
-
inactivityTimeout: 15 * 60 * 1000, // 15 minutes (default)
|
|
768
|
-
|
|
769
|
-
// IMPORTANT: Set this LOWER than your JWT expiration
|
|
770
|
-
// to prevent orphaned sessions
|
|
771
|
-
},
|
|
772
|
-
},
|
|
773
|
-
});
|
|
774
|
-
```
|
|
775
|
-
|
|
776
|
-
### Relationship with JWT Config
|
|
777
|
-
|
|
778
|
-
```typescript
|
|
779
|
-
// src/config/plugins.ts
|
|
780
|
-
export default () => ({
|
|
781
|
-
// Strapi JWT Configuration
|
|
782
|
-
'users-permissions': {
|
|
783
|
-
config: {
|
|
784
|
-
jwt: {
|
|
785
|
-
expiresIn: '30m', // Access Token expires after 30 minutes
|
|
786
|
-
},
|
|
236
|
+
'users-permissions': {
|
|
237
|
+
config: {
|
|
238
|
+
jwtManagement: 'refresh', // Enable refresh tokens
|
|
239
|
+
sessions: {
|
|
240
|
+
accessTokenLifespan: 3600, // 1 hour
|
|
241
|
+
maxRefreshTokenLifespan: 2592000, // 30 days
|
|
787
242
|
},
|
|
788
243
|
},
|
|
789
|
-
|
|
790
|
-
// Session Manager Configuration
|
|
791
|
-
'magic-sessionmanager': {
|
|
792
|
-
enabled: true,
|
|
793
|
-
config: {
|
|
794
|
-
// Set inactivity timeout LOWER than JWT expiration
|
|
795
|
-
// This prevents orphaned sessions when JWT expires
|
|
796
|
-
inactivityTimeout: 15 * 60 * 1000, // 15 minutes < 30 minutes JWT
|
|
797
|
-
|
|
798
|
-
// Or match JWT expiration exactly:
|
|
799
|
-
// inactivityTimeout: 30 * 60 * 1000, // 30 minutes = JWT expiration
|
|
800
|
-
},
|
|
801
|
-
},
|
|
802
|
-
});
|
|
803
|
-
```
|
|
804
|
-
|
|
805
|
-
### Premium Config
|
|
806
|
-
|
|
807
|
-
Available through Admin UI **Settings → Sessions → Settings**:
|
|
808
|
-
|
|
809
|
-
```typescript
|
|
810
|
-
// Settings stored in database via Admin UI
|
|
811
|
-
{
|
|
812
|
-
// Geolocation & Security
|
|
813
|
-
enableGeolocation: true,
|
|
814
|
-
enableSecurityScoring: true,
|
|
815
|
-
blockSuspiciousSessions: true,
|
|
816
|
-
alertOnVpnProxy: true,
|
|
817
|
-
|
|
818
|
-
// Geo-Fencing
|
|
819
|
-
enableGeofencing: true,
|
|
820
|
-
allowedCountries: ["DE", "AT", "CH"], // Germany, Austria, Switzerland
|
|
821
|
-
blockedCountries: ["RU", "CN"], // Russia, China
|
|
822
|
-
|
|
823
|
-
// Notifications
|
|
824
|
-
enableEmailAlerts: true,
|
|
825
|
-
alertOnSuspiciousLogin: true,
|
|
826
|
-
enableWebhooks: true,
|
|
827
|
-
discordWebhookUrl: "https://discord.com/api/webhooks/...",
|
|
828
|
-
slackWebhookUrl: "https://hooks.slack.com/services/...",
|
|
829
244
|
}
|
|
830
245
|
```
|
|
831
246
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
### Encryption
|
|
837
|
-
|
|
838
|
-
All JWT tokens are **encrypted before storing** in the database using **AES-256-GCM** encryption.
|
|
839
|
-
|
|
840
|
-
#### Why Encrypt Tokens?
|
|
841
|
-
|
|
842
|
-
```
|
|
843
|
-
❌ Without Encryption:
|
|
844
|
-
Database compromised → Attacker sees JWTs → Can impersonate users!
|
|
845
|
-
|
|
846
|
-
✅ With Encryption:
|
|
847
|
-
Database compromised → Attacker sees encrypted data → Useless without key!
|
|
848
|
-
```
|
|
849
|
-
|
|
850
|
-
#### How It Works
|
|
851
|
-
|
|
852
|
-
```
|
|
853
|
-
Login: User gets JWT
|
|
854
|
-
↓
|
|
855
|
-
JWT: "eyJhbGciOiJIUzI1NiIs..."
|
|
856
|
-
↓
|
|
857
|
-
[Encrypt with AES-256-GCM]
|
|
858
|
-
↓
|
|
859
|
-
Encrypted: "a3f7b2c1:8c4d9e2a:f2a5b8c3d4e5f6a7..."
|
|
860
|
-
↓
|
|
861
|
-
Stored in Database (secure!)
|
|
862
|
-
|
|
863
|
-
Logout: User sends JWT
|
|
864
|
-
↓
|
|
865
|
-
[Fetch all active sessions from DB]
|
|
866
|
-
↓
|
|
867
|
-
[Decrypt each token]
|
|
868
|
-
↓
|
|
869
|
-
[Compare with user's JWT]
|
|
870
|
-
↓
|
|
871
|
-
Match found → Terminate session ✅
|
|
872
|
-
```
|
|
873
|
-
|
|
874
|
-
#### Configuration
|
|
875
|
-
|
|
876
|
-
**Generate Encryption Key (Admin Panel):**
|
|
877
|
-
|
|
878
|
-
1. Go to **Admin → Sessions → Settings**
|
|
879
|
-
2. Open **Security Settings** accordion
|
|
880
|
-
3. Find **JWT Encryption Key Generator**
|
|
881
|
-
4. Click **"Generate Key"**
|
|
882
|
-
5. Copy key with **"Copy for .env"** button
|
|
883
|
-
6. Add to your `.env` file
|
|
884
|
-
|
|
885
|
-
**Or generate manually:**
|
|
886
|
-
|
|
887
|
-
```bash
|
|
888
|
-
# Generate secure 32-byte key
|
|
889
|
-
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
247
|
+
**What this does:**
|
|
248
|
+
- Users stay logged in longer (better experience)
|
|
249
|
+
- But admins can still force-logout completely (better security)
|
|
250
|
+
- Best of both worlds! ✅
|
|
890
251
|
|
|
891
|
-
|
|
892
|
-
SESSION_ENCRYPTION_KEY=aBc123XyZ...your-32-char-key
|
|
893
|
-
```
|
|
252
|
+
---
|
|
894
253
|
|
|
895
|
-
|
|
254
|
+
## 🌍 Premium Features (Optional License)
|
|
896
255
|
|
|
897
|
-
|
|
898
|
-
- Plugin uses `APP_KEYS` or `API_TOKEN_SALT` as fallback
|
|
899
|
-
- ⚠️ Warning logged on startup
|
|
900
|
-
- Still encrypted, but key is derived from Strapi's keys
|
|
256
|
+
### IP Geolocation
|
|
901
257
|
|
|
902
|
-
**
|
|
903
|
-
|
|
258
|
+
**See where users login from:**
|
|
259
|
+
- Country (with flag! 🇩🇪🇺🇸🇬🇧)
|
|
260
|
+
- City
|
|
261
|
+
- ISP Provider
|
|
262
|
+
- Coordinates (for map)
|
|
904
263
|
|
|
905
|
-
|
|
264
|
+
### Threat Detection
|
|
906
265
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
| Auth Tag | 128 bits (16 bytes) |
|
|
913
|
-
| Format | `iv:authTag:encryptedData` (hex) |
|
|
266
|
+
**Automatically check if IP is:**
|
|
267
|
+
- VPN
|
|
268
|
+
- Proxy
|
|
269
|
+
- Known threat
|
|
270
|
+
- Security score (0-100)
|
|
914
271
|
|
|
915
|
-
###
|
|
272
|
+
### Auto-Blocking
|
|
916
273
|
|
|
917
|
-
|
|
274
|
+
**Block logins from:**
|
|
275
|
+
- Specific countries
|
|
276
|
+
- VPNs or proxies
|
|
277
|
+
- Low security score IPs
|
|
278
|
+
- Known threat IPs
|
|
918
279
|
|
|
919
|
-
|
|
920
|
-
sessionId: "sess_lx3k7_4f2a8b3c_a1b2c3d4e5f6"
|
|
921
|
-
// prefix^ ^timestamp ^user-hash ^random-bytes
|
|
922
|
-
```
|
|
280
|
+
### Notifications
|
|
923
281
|
|
|
924
|
-
**
|
|
925
|
-
-
|
|
926
|
-
-
|
|
927
|
-
-
|
|
928
|
-
-
|
|
282
|
+
**Get alerts when:**
|
|
283
|
+
- Suspicious login detected
|
|
284
|
+
- VPN used
|
|
285
|
+
- New location login
|
|
286
|
+
- Send to Discord or Slack!
|
|
929
287
|
|
|
930
288
|
---
|
|
931
289
|
|
|
932
|
-
##
|
|
933
|
-
|
|
934
|
-
### IP Geolocation & Threat Detection
|
|
935
|
-
|
|
936
|
-
Uses **ipapi.co** API for accurate IP information:
|
|
937
|
-
|
|
938
|
-
```json
|
|
939
|
-
{
|
|
940
|
-
"country": "Germany",
|
|
941
|
-
"country_code": "DE",
|
|
942
|
-
"city": "Berlin",
|
|
943
|
-
"latitude": 52.52,
|
|
944
|
-
"longitude": 13.41,
|
|
945
|
-
"isp": "Deutsche Telekom",
|
|
946
|
-
"isVpn": false,
|
|
947
|
-
"isProxy": false,
|
|
948
|
-
"isThreat": false,
|
|
949
|
-
"securityScore": 95,
|
|
950
|
-
"threatType": null
|
|
951
|
-
}
|
|
952
|
-
```
|
|
953
|
-
|
|
954
|
-
### Auto-Blocking Rules
|
|
955
|
-
|
|
956
|
-
```
|
|
957
|
-
Login attempt from IP: 1.2.3.4
|
|
958
|
-
↓
|
|
959
|
-
[Geolocation Check]
|
|
960
|
-
↓
|
|
961
|
-
isThreat = true → BLOCK ❌
|
|
962
|
-
isVpn = true (if alertOnVpnProxy) → BLOCK ❌
|
|
963
|
-
country = "RU" (if in blockedCountries) → BLOCK ❌
|
|
964
|
-
country ≠ ["DE","AT","CH"] (if allowedCountries set) → BLOCK ❌
|
|
965
|
-
securityScore < 50 → BLOCK ❌
|
|
966
|
-
↓
|
|
967
|
-
None of above? → ALLOW ✅
|
|
968
|
-
```
|
|
969
|
-
|
|
970
|
-
### Email Alerts
|
|
971
|
-
|
|
972
|
-
```
|
|
973
|
-
Subject: ⚠️ Unusual Login Activity
|
|
974
|
-
|
|
975
|
-
Hi John,
|
|
976
|
-
|
|
977
|
-
A login from a new location was detected:
|
|
290
|
+
## 📋 Simple API Guide
|
|
978
291
|
|
|
979
|
-
|
|
980
|
-
🌐 IP Address: 192.168.1.100
|
|
981
|
-
🔒 Risk Level: Medium (VPN detected)
|
|
982
|
-
⏰ Time: 2024-01-15 10:30:00 UTC
|
|
983
|
-
💻 Device: Chrome on Windows
|
|
292
|
+
### Get Sessions
|
|
984
293
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
294
|
+
```bash
|
|
295
|
+
# Get all active sessions
|
|
296
|
+
GET /magic-sessionmanager/sessions
|
|
988
297
|
```
|
|
989
298
|
|
|
990
|
-
###
|
|
299
|
+
### Logout
|
|
991
300
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
━━━━━━━━━━━━━━━━━━
|
|
996
|
-
User: john@example.com
|
|
997
|
-
IP: 192.168.1.100
|
|
998
|
-
Location: Berlin, Germany
|
|
999
|
-
Risk: ⚠️ Medium (VPN)
|
|
1000
|
-
Browser: Chrome / Windows
|
|
1001
|
-
Time: 2024-01-15 10:30:00
|
|
301
|
+
```bash
|
|
302
|
+
# Logout current user
|
|
303
|
+
POST /api/auth/logout
|
|
1002
304
|
```
|
|
1003
305
|
|
|
1004
|
-
---
|
|
1005
|
-
|
|
1006
|
-
## 💡 Use Cases
|
|
1007
|
-
|
|
1008
306
|
### Force Logout
|
|
1009
307
|
|
|
1010
308
|
```bash
|
|
1011
|
-
# Admin
|
|
1012
|
-
POST /
|
|
1013
|
-
|
|
1014
|
-
# Admin logs out user from all devices
|
|
1015
|
-
POST /api/magic-sessionmanager/admin/user/5/terminate-all
|
|
1016
|
-
|
|
1017
|
-
# Next API request from that user:
|
|
1018
|
-
GET /api/some-endpoint
|
|
1019
|
-
Authorization: Bearer <their JWT>
|
|
1020
|
-
|
|
1021
|
-
# Response: 401 Unauthorized
|
|
1022
|
-
# "All sessions have been terminated. Please login again."
|
|
1023
|
-
```
|
|
1024
|
-
|
|
1025
|
-
### Security Monitoring
|
|
1026
|
-
|
|
1027
|
-
```
|
|
1028
|
-
Premium feature: VPN Detection
|
|
1029
|
-
↓
|
|
1030
|
-
User logs in from VPN
|
|
1031
|
-
↓
|
|
1032
|
-
isVpn = true detected
|
|
1033
|
-
↓
|
|
1034
|
-
Email sent: "Suspicious login from VPN"
|
|
1035
|
-
↓
|
|
1036
|
-
Webhook notification to Slack
|
|
1037
|
-
↓
|
|
1038
|
-
Admin reviews in dashboard
|
|
1039
|
-
↓
|
|
1040
|
-
Admin can terminate session if needed
|
|
309
|
+
# Admin force-logout a session
|
|
310
|
+
POST /magic-sessionmanager/sessions/:sessionId/terminate
|
|
1041
311
|
```
|
|
1042
312
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
```
|
|
1046
|
-
Export all sessions to CSV:
|
|
1047
|
-
- Who logged in
|
|
1048
|
-
- When & where (IP, location)
|
|
1049
|
-
- Device & browser used
|
|
1050
|
-
- Session duration
|
|
1051
|
-
- Logout time (if any)
|
|
1052
|
-
|
|
1053
|
-
Perfect for compliance requirements!
|
|
1054
|
-
```
|
|
313
|
+
**That's all you need to know!**
|
|
1055
314
|
|
|
1056
315
|
---
|
|
1057
316
|
|
|
1058
|
-
##
|
|
317
|
+
## ⚙️ Settings You Can Change
|
|
1059
318
|
|
|
1060
|
-
|
|
319
|
+
**In `config/plugins.ts`:**
|
|
1061
320
|
|
|
1062
|
-
```
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
# Response:
|
|
1072
|
-
{
|
|
1073
|
-
"jwt": "eyJhbGciOiJIUzI1NiIs...",
|
|
1074
|
-
"user": { "id": 1, "email": "test@example.com", ... }
|
|
321
|
+
```typescript
|
|
322
|
+
'magic-sessionmanager': {
|
|
323
|
+
config: {
|
|
324
|
+
// How often to update "last seen" (in milliseconds)
|
|
325
|
+
lastSeenRateLimit: 30000, // Default: 30 seconds
|
|
326
|
+
|
|
327
|
+
// When to mark sessions inactive (in milliseconds)
|
|
328
|
+
inactivityTimeout: 900000, // Default: 15 minutes
|
|
329
|
+
},
|
|
1075
330
|
}
|
|
1076
|
-
|
|
1077
|
-
# Save JWT
|
|
1078
|
-
export JWT="eyJhbGciOiJIUzI1NiIs..."
|
|
1079
|
-
|
|
1080
|
-
# Check session was created
|
|
1081
|
-
curl http://localhost:1337/api/magic-sessionmanager/sessions \
|
|
1082
|
-
-H "Authorization: Bearer $JWT"
|
|
1083
|
-
|
|
1084
|
-
# Should show new session with:
|
|
1085
|
-
# - loginTime
|
|
1086
|
-
# - isActive: true
|
|
1087
|
-
# - ipAddress
|
|
1088
|
-
# - userAgent
|
|
1089
|
-
# - token (matches JWT)
|
|
1090
331
|
```
|
|
1091
332
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
# Check lastActive timestamp
|
|
1100
|
-
curl http://localhost:1337/api/magic-sessionmanager/sessions \
|
|
1101
|
-
-H "Authorization: Bearer $JWT"
|
|
333
|
+
**In Admin Panel (Settings Tab):**
|
|
334
|
+
- Email alerts on/off
|
|
335
|
+
- Webhook URLs (Discord/Slack)
|
|
336
|
+
- Countries to block/allow
|
|
337
|
+
- VPN detection on/off
|
|
338
|
+
- Generate encryption key
|
|
1102
339
|
|
|
1103
|
-
|
|
1104
|
-
sleep 35
|
|
1105
|
-
|
|
1106
|
-
# Second request (should update lastActive)
|
|
1107
|
-
curl http://localhost:1337/api/users \
|
|
1108
|
-
-H "Authorization: Bearer $JWT"
|
|
1109
|
-
|
|
1110
|
-
# Check lastActive changed
|
|
1111
|
-
curl http://localhost:1337/api/magic-sessionmanager/sessions \
|
|
1112
|
-
-H "Authorization: Bearer $JWT"
|
|
1113
|
-
```
|
|
340
|
+
---
|
|
1114
341
|
|
|
1115
|
-
|
|
342
|
+
## 🐛 Common Problems & Fixes
|
|
1116
343
|
|
|
1117
|
-
|
|
1118
|
-
# Logout via Strapi's route (replaced by plugin)
|
|
1119
|
-
curl -X POST http://localhost:1337/api/auth/logout \
|
|
1120
|
-
-H "Authorization: Bearer $JWT"
|
|
344
|
+
### I don't see the Sessions menu
|
|
1121
345
|
|
|
1122
|
-
|
|
346
|
+
**Fix:**
|
|
347
|
+
1. Make sure plugin is in `config/plugins.ts`
|
|
348
|
+
2. Run `npm run build`
|
|
349
|
+
3. Restart Strapi
|
|
350
|
+
4. Refresh browser (Cmd+Shift+R)
|
|
1123
351
|
|
|
1124
|
-
|
|
1125
|
-
curl http://localhost:1337/api/magic-sessionmanager/sessions \
|
|
1126
|
-
-H "Authorization: Bearer $JWT"
|
|
352
|
+
### Sessions not being created
|
|
1127
353
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
354
|
+
**Fix:**
|
|
355
|
+
1. Check Strapi logs for errors
|
|
356
|
+
2. Make sure users are logging in (not already logged in)
|
|
357
|
+
3. Check database is working
|
|
1132
358
|
|
|
1133
|
-
###
|
|
359
|
+
### 401 or 403 errors
|
|
1134
360
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
-H "Authorization: Bearer $JWT_A"
|
|
361
|
+
**Fix:**
|
|
362
|
+
- 401 = Not logged in (need to login as admin)
|
|
363
|
+
- 403 = Not allowed (check you're admin, not regular user)
|
|
1139
364
|
|
|
1140
|
-
|
|
1141
|
-
curl http://localhost:1337/api/users \
|
|
1142
|
-
-H "Authorization: Bearer $JWT_A"
|
|
365
|
+
### Database table "sessions" already exists
|
|
1143
366
|
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
367
|
+
**Fix:**
|
|
368
|
+
- This plugin uses `magic_sessions` table (not `sessions`)
|
|
369
|
+
- If you see this error, another plugin is using that name
|
|
370
|
+
- Our plugin automatically uses the correct name
|
|
1147
371
|
|
|
1148
372
|
---
|
|
1149
373
|
|
|
1150
|
-
##
|
|
1151
|
-
|
|
1152
|
-
### Sessions Not Creating
|
|
1153
|
-
|
|
1154
|
-
**Problem:** Login succeeds but no session record appears.
|
|
1155
|
-
|
|
1156
|
-
**Solutions:**
|
|
1157
|
-
1. Check Strapi logs:
|
|
1158
|
-
```bash
|
|
1159
|
-
npm run develop
|
|
1160
|
-
# Look for: [magic-sessionmanager] 🔍 Login detected!
|
|
1161
|
-
# Look for: [magic-sessionmanager] ✅ Session X created
|
|
1162
|
-
```
|
|
374
|
+
## 💡 When To Use This Plugin
|
|
1163
375
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
376
|
+
**Perfect for:**
|
|
377
|
+
- Multi-tenant apps (see which tenant users are online)
|
|
378
|
+
- E-commerce (track customer sessions)
|
|
379
|
+
- Collaboration tools (show who's currently working)
|
|
380
|
+
- Security-critical apps (force-logout compromised accounts)
|
|
381
|
+
- Compliance requirements (session audit logs)
|
|
1168
382
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
383
|
+
**Not needed if:**
|
|
384
|
+
- Single-user app
|
|
385
|
+
- No need to see who's logged in
|
|
386
|
+
- No security requirements
|
|
1172
387
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
**Problem:** After logout, JWT still authenticates API requests.
|
|
1176
|
-
|
|
1177
|
-
**Explanation:** This is EXPECTED behavior!
|
|
1178
|
-
- JWT tokens are **stateless** - validated by signature alone
|
|
1179
|
-
- Plugin marks session `isActive = false`
|
|
1180
|
-
- But JWT itself remains valid until expiration
|
|
1181
|
-
- Next authenticated request is **blocked** by LastSeen middleware
|
|
388
|
+
---
|
|
1182
389
|
|
|
1183
|
-
|
|
390
|
+
## 🔧 How To Test It
|
|
1184
391
|
|
|
1185
|
-
###
|
|
392
|
+
### Quick Manual Test
|
|
1186
393
|
|
|
1187
|
-
**
|
|
394
|
+
1. **Login to your Strapi app** (frontend or admin)
|
|
395
|
+
2. **Go to Admin → Sessions**
|
|
396
|
+
3. **You should see your session!**
|
|
397
|
+
4. **Click "Terminate" on your session**
|
|
398
|
+
5. **Try to use the app → You're logged out!**
|
|
1188
399
|
|
|
1189
|
-
|
|
400
|
+
### With Postman
|
|
1190
401
|
|
|
1191
|
-
**
|
|
1192
|
-
```
|
|
1193
|
-
|
|
1194
|
-
{
|
|
1195
|
-
'magic-sessionmanager': {
|
|
1196
|
-
config: {
|
|
1197
|
-
inactivityTimeout: 15 * 60 * 1000 // 15 min (if JWT = 30 min)
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
402
|
+
**1. Login:**
|
|
403
|
+
```
|
|
404
|
+
POST http://localhost:1337/api/auth/local
|
|
405
|
+
Body: { "identifier": "user@test.com", "password": "pass123" }
|
|
1201
406
|
```
|
|
1202
407
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
**Solutions:**
|
|
1208
|
-
1. Check rate limit:
|
|
1209
|
-
```typescript
|
|
1210
|
-
config: {
|
|
1211
|
-
lastSeenRateLimit: 5000 // Lower for testing
|
|
1212
|
-
}
|
|
1213
|
-
```
|
|
408
|
+
**2. Check session created:**
|
|
409
|
+
```
|
|
410
|
+
GET http://localhost:1337/magic-sessionmanager/sessions
|
|
411
|
+
```
|
|
1214
412
|
|
|
1215
|
-
|
|
413
|
+
**3. Logout:**
|
|
414
|
+
```
|
|
415
|
+
POST http://localhost:1337/api/auth/logout
|
|
416
|
+
Authorization: Bearer YOUR_JWT_TOKEN
|
|
417
|
+
```
|
|
1216
418
|
|
|
1217
|
-
|
|
1218
|
-
```bash
|
|
1219
|
-
# Look for: [magic-sessionmanager] ✅ LastSeen middleware mounted
|
|
1220
|
-
```
|
|
419
|
+
**Done!**
|
|
1221
420
|
|
|
1222
421
|
---
|
|
1223
422
|
|
|
1224
|
-
##
|
|
423
|
+
## 📦 What Gets Installed
|
|
1225
424
|
|
|
1226
|
-
|
|
425
|
+
When you install this plugin, you get:
|
|
1227
426
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
│ ├── services/
|
|
1235
|
-
│ │ ├── session.js # 💾 Session CRUD operations
|
|
1236
|
-
│ │ ├── geolocation.js # 🌍 IP geolocation (Premium)
|
|
1237
|
-
│ │ ├── notifications.js # 📧 Email/webhook alerts
|
|
1238
|
-
│ │ └── license-guard.js # 🔑 License validation
|
|
1239
|
-
│ ├── controllers/
|
|
1240
|
-
│ │ ├── session.js # 🎮 Session API handlers
|
|
1241
|
-
│ │ ├── settings.js # ⚙️ Settings API
|
|
1242
|
-
│ │ └── license.js # 🔑 License API
|
|
1243
|
-
│ ├── routes/
|
|
1244
|
-
│ │ ├── content-api.js # 🌐 User-facing routes
|
|
1245
|
-
│ │ └── admin.js # 👑 Admin-only routes
|
|
1246
|
-
│ └── utils/
|
|
1247
|
-
│ └── getClientIp.js # 📍 IP extraction (proxy-aware)
|
|
1248
|
-
│
|
|
1249
|
-
├── admin/src/
|
|
1250
|
-
│ ├── pages/
|
|
1251
|
-
│ │ ├── HomePage.jsx # 📊 Main dashboard
|
|
1252
|
-
│ │ ├── ActiveSessions.jsx # 👥 Active sessions tab
|
|
1253
|
-
│ │ ├── Analytics.jsx # 📈 Analytics tab
|
|
1254
|
-
│ │ ├── Settings.jsx # ⚙️ Settings tab
|
|
1255
|
-
│ │ └── License.jsx # 🔑 License tab
|
|
1256
|
-
│ └── components/
|
|
1257
|
-
│ ├── SessionDetailModal.jsx
|
|
1258
|
-
│ └── LicenseGuard.jsx
|
|
1259
|
-
│
|
|
1260
|
-
├── .github/workflows/
|
|
1261
|
-
│ ├── semantic-release.yml # 🚀 NPM publishing
|
|
1262
|
-
│ └── test.yml # ✅ CI/CD tests
|
|
1263
|
-
│
|
|
1264
|
-
├── package.json
|
|
1265
|
-
├── .releaserc.json # 📦 semantic-release config
|
|
1266
|
-
└── README.md
|
|
1267
|
-
```
|
|
427
|
+
- ✅ Dashboard to see all sessions
|
|
428
|
+
- ✅ Session tracking (automatic)
|
|
429
|
+
- ✅ Force logout buttons
|
|
430
|
+
- ✅ Activity monitoring
|
|
431
|
+
- ✅ Encryption (secure)
|
|
432
|
+
- ✅ Multi-device support
|
|
1268
433
|
|
|
1269
|
-
|
|
434
|
+
**Premium features require a license (free to generate):**
|
|
435
|
+
- 🔒 IP Geolocation
|
|
436
|
+
- 🔒 Threat detection
|
|
437
|
+
- 🔒 Auto-blocking
|
|
438
|
+
- 🔒 Email/webhook alerts
|
|
1270
439
|
|
|
1271
|
-
|
|
1272
|
-
# Build
|
|
1273
|
-
npm run build
|
|
440
|
+
---
|
|
1274
441
|
|
|
1275
|
-
|
|
1276
|
-
git commit -m "feat: add new feature" # → MINOR
|
|
1277
|
-
git commit -m "fix: fix bug" # → PATCH
|
|
1278
|
-
git commit -m "feat!: breaking change" # → MAJOR
|
|
1279
|
-
```
|
|
442
|
+
## 🙋 FAQ
|
|
1280
443
|
|
|
1281
|
-
|
|
444
|
+
**Q: Do I need to change my Strapi code?**
|
|
445
|
+
A: No! Just install and enable the plugin.
|
|
1282
446
|
|
|
1283
|
-
|
|
447
|
+
**Q: Will this break my existing logins?**
|
|
448
|
+
A: No! It just tracks them, doesn't change them.
|
|
1284
449
|
|
|
1285
|
-
|
|
450
|
+
**Q: Can users see each other's sessions?**
|
|
451
|
+
A: No! Only admins can see all sessions. Users only see their own.
|
|
1286
452
|
|
|
1287
|
-
|
|
453
|
+
**Q: What if I uninstall the plugin?**
|
|
454
|
+
A: Sessions will stop being tracked. Everything else works normally.
|
|
1288
455
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
fix: correct session cleanup # → v1.0.1
|
|
1292
|
-
feat!: change API response format # → v2.0.0
|
|
1293
|
-
```
|
|
456
|
+
**Q: Does it slow down my app?**
|
|
457
|
+
A: No! It has smart rate-limiting to prevent database spam.
|
|
1294
458
|
|
|
1295
|
-
|
|
459
|
+
**Q: Can I customize the dashboard?**
|
|
460
|
+
A: Not yet, but it's planned for future versions!
|
|
1296
461
|
|
|
1297
462
|
---
|
|
1298
463
|
|
|
@@ -1300,16 +465,16 @@ GitHub Actions automatically publishes to NPM on push to `main`.
|
|
|
1300
465
|
|
|
1301
466
|
- **NPM:** https://www.npmjs.com/package/strapi-plugin-magic-sessionmanager
|
|
1302
467
|
- **GitHub:** https://github.com/Schero94/Magic-Sessionmanager
|
|
1303
|
-
- **
|
|
468
|
+
- **Report Bugs:** https://github.com/Schero94/Magic-Sessionmanager/issues
|
|
1304
469
|
|
|
1305
470
|
---
|
|
1306
471
|
|
|
1307
472
|
## 📄 License
|
|
1308
473
|
|
|
1309
|
-
**MIT License** - Free
|
|
474
|
+
**MIT License** - Free to use!
|
|
1310
475
|
|
|
1311
|
-
**Copyright
|
|
476
|
+
**Copyright © 2025 Schero D.**
|
|
1312
477
|
|
|
1313
478
|
---
|
|
1314
479
|
|
|
1315
|
-
**
|
|
480
|
+
**Made with ❤️ for Strapi v5**
|