strapi-security-suite
v0.3.1
Published
All-in-one authentication and session security plugin for Strapi v5
Downloads
310
Maintainers
Readme
🛡️ Strapi Security Suite
The admin security plugin that takes your sessions personally.
One plugin. Auto-logout. Single-session enforcement. Token revocation. Password policy. Built for Strapi v5. Fueled by in-memory Maps and zero tolerance for stale tokens.
🤔 What Is This?
Imagine a bouncer at a nightclub. But the nightclub is your Strapi admin panel, and the bouncer has a perfect memory, never sleeps, and will physically escort your idle admins out the door after 30 minutes of doing nothing.
That's this plugin.
🔐 Admin logs in
|
| 👀 Every request tracked (timestamp saved in memory)
|
| 😴 Admin goes idle...
|
| ⏰ 30 minutes pass...
|
🚪 BOOM. Logged out. Cookies cleared. Token dead.
No arguments. No appeals. Just security.✨ Features at a Glance
| Feature | What It Does | Vibe | | -------------------------- | ---------------------------------------------- | -------------------- | | ⏰ Auto-Logout | Kicks idle admins after configurable minutes | "Use it or lose it" | | 🚫 Single-Session Lock | One admin = one session. Period. | "No shadow clones" | | 💀 Token Revocation | Dead tokens stay dead. Instantly. | "Ghosts get ghosted" | | 🔑 Password Policy | Expiry + non-reusable passwords (configurable) | "Rotate or regret" | | ⚙️ Admin UI | Settings panel right inside Strapi | "Click, don't code" | | 🛡️ Input Validation | Server-side validation on every settings save | "Trust nobody" |
🚀 Quick Start
Step 1: Install it
yarn add strapi-security-suiteStep 2: Enable it
Add this to your config/plugins.js (or .ts):
module.exports = ({ env }) => ({
'strapi-security-suite': {
enabled: true,
},
});Step 3: Restart Strapi
yarn developStep 4: Find it
Go to Settings → Global → Security Suite
That's it. You're done. Go get a coffee. ☕
🖼️ The Admin Panel
Once installed, you get a beautiful settings page with two panels:
┌───────────────────────────────────────────────────────┐
│ 🛡️ Security & Session Settings │
├───────────────────────────┬───────────────────────────┤
│ │ │
│ 🕐 SESSION MANAGEMENT │ 🔑 PASSWORD MANAGEMENT │
│ │ │
│ Auto Logout Time: [30] │ Password Control: [ON] │
│ (minutes) │ │
│ │ Expiry Days: [30] │
│ Multi-Session │ │
│ Control: [ON] │ Non-Reusable: [ON] │
│ │ │
├───────────────────────────┴───────────────────────────┤
│ [ 💾 Save Settings ] │
└───────────────────────────────────────────────────────┘Everything is stored in the database. Change a value, hit save, it takes effect immediately. No restarts. No config files. No drama.
🧠 How It Actually Works
Here's the whole flow, explained like you're five (but a very smart five):
🔗 The Middleware Pipeline
When any request hits your Strapi server, it passes through 5 security checkpoints (middlewares), in this exact order:
🌐 Incoming Request
│
▼
1. 🐣 seedUserInfos
│ "Who are you? Let me check your JWT and load your profile"
│
▼
2. 🔍 interceptRenewToken
│ "Trying to renew your token? Let me make sure you're still welcome"
│
▼
3. 👣 trackActivity
│ "OK you're legit. I'm writing down the time. Don't be idle."
│
▼
4. ☠️ rejectRevokedTokens
│ "Wait... is your session revoked? GET OUT. Cookies deleted. Token dead."
│
▼
5. 🚫 preventMultipleSessions (on login only)
"Already logged in somewhere else? 409 Conflict. One session only."⏱️ The Auto-Logout Watcher
Running in the background, checking every 5 seconds:
🔄 Every 5 seconds:
│
│ 🔍 Check each active session in memory
│ 🕐 Compare last activity timestamp vs. configured timeout
│
│ 😴 Idle too long?
│ │
│ YES → Add email to revoked set
│ │ → Delete session from activity map
│ │ → Log it: "Auto-logged out admin X after Y seconds"
│ │
│ NO → Carry on, you're fine 👍🖥️ The Frontend Interceptor
On the admin panel side, window.fetch is patched to watch for a special header:
🌐 Admin makes any API call
│
▼
👀 Check response headers for 'app.admin.tk'
│
YES → 🚨 FORCED LOGOUT 🚨
│ window.stop()
│ window.location.reload()
│ (Session is over. Go home.)
│
NO → ✅ Normal response. Continue working.📂 Project Structure
strapi-security-suite/
📁 admin/src/ ← Admin panel (React)
│ 📄 index.js Plugin entry + fetch interceptor
│ 📄 constants.js API paths, header names
│ 📄 pluginId.js Plugin ID constant
│ 📁 components/
│ │ 📄 Initializer.jsx Plugin lifecycle init
│ 📁 pages/
│ │ 📄 App.jsx Router
│ │ 📄 HomePage.jsx Settings UI (the pretty one)
│ 📁 translations/
│ 📄 en.json i18n strings
│
📁 server/src/ ← Server-side (Node.js)
│ 📄 index.js Plugin entry point
│ 📄 register.js Middleware registration phase
│ 📄 bootstrap.js Permissions + settings seeding
│ 📄 destroy.js Cleanup on shutdown
│ 📄 constants.js ⭐ ALL magic values live here
│ │
│ 📁 controllers/
│ │ 📄 adminSecurityController.js GET/POST settings (with validation!)
│ │
│ 📁 services/
│ │ 📄 autoLogoutChecker.js Background watcher (setInterval)
│ │
│ 📁 middlewares/
│ │ 📄 seedUserInfos.js Hydrate session from JWT
│ │ 📄 interceptRenewToken.js Block renewal for dead sessions
│ │ 📄 trackActivity.js Record last activity timestamp
│ │ 📄 rejectRevokedTokens.js Nuke revoked sessions
│ │ 📄 preventMultipleSessions.js One-session-per-admin gate
│ │
│ 📁 policies/
│ │ 📄 has-admin-permission.js Route-level permission check
│ │
│ 📁 globals/ ← In-memory state (the "brain")
│ │ 📄 sessionActivityMap.js Map<"id:email", timestamp>
│ │ 📄 revokedTokenSet.js Set<email> of revoked sessions
│ │ 📄 loginLocks.js Set<email> login race-condition guard
│ │
│ 📁 utils/
│ │ 📄 errors.js PluginError, ValidationError, AuthorizationError
│ │ 📄 force-expire-admin.js Issue a 1-second JWT to kill client token
│ │
│ 📁 content-types/
│ │ 📁 security-settings/
│ │ 📄 schema.json DB schema (singleType)
│ │
│ 📁 routes/
│ │ 📄 index.js Admin-typed routes with policies
│ │
│ 📁 types/
│ 📄 typedefs.js JSDoc type definitions
│
📄 eslint.config.mjs ESLint v9 flat config
📄 .prettierrc Prettier config
📄 package.json Scripts, deps, lint-staged
📁 .husky/
📄 pre-commit Runs lint-staged before every commit🔧 Configuration Schema
All settings live in a single-type content-type in the database:
{
"autoLogoutTime": 30,
"multipleSessionsControl": true,
"passwordExpiryDays": 30,
"nonReusablePassword": true,
"enablePasswordManagement": true
}| Field | Type | Default | What It Does |
| -------------------------- | --------- | ------- | ---------------------------------------- |
| autoLogoutTime | integer | 30 | Minutes of inactivity before auto-logout |
| multipleSessionsControl | boolean | true | Block concurrent sessions for same admin |
| passwordExpiryDays | integer | 30 | Days before password must be changed |
| nonReusablePassword | boolean | true | Prevent reuse of previous passwords |
| enablePasswordManagement | boolean | true | Master switch for password features |
🧪 API Endpoints
All routes are admin-typed (Strapi handles auth automatically):
| Method | Path | Auth | Permission | Description |
| ------ | --------------------------------------- | -------- | ---------------- | --------------- |
| GET | /strapi-security-suite/health | 🔓 None | — | Health check |
| GET | /strapi-security-suite/admin/settings | 🔒 Admin | view-configs | Read settings |
| POST | /strapi-security-suite/admin/settings | 🔒 Admin | manage-configs | Update settings |
🔐 Permissions
The plugin registers three permission actions:
| Permission | What It Allows |
| ---------------------------------------------- | ------------------------ |
| plugin::strapi-security-suite.access | Access the settings page |
| plugin::strapi-security-suite.view-configs | Read security settings |
| plugin::strapi-security-suite.manage-configs | Modify security settings |
✅ Engineering Standards Compliance
This plugin follows the STANDARDS.md operating protocol:
| Standard | Status | Details |
| ----------------------------- | ------ | --------------------------------------------------------------------- |
| 💬 ES6+ JavaScript | ✅ | Arrow functions, destructuring, template literals, native ESM |
| 📄 Full JSDoc | ✅ | Every export documented with @param, @returns, @module |
| 🔢 No Magic Values | ✅ | All values in constants.js (status codes, cookies, headers, timing) |
| 🚨 Custom Error Hierarchy | ✅ | PluginError → ValidationError, AuthorizationError |
| 🛡️ Input Validation | ✅ | saveSettings validates keys, types, and shape |
| 🔍 ESLint | ✅ | v9 flat config with jsdoc plugin + Prettier compat |
| 🎨 Prettier | ✅ | Single quotes, 100 print width, trailing commas |
| 🪝 Husky + lint-staged | ✅ | Pre-commit: ESLint fix + Prettier on staged files |
| 🏗️ Architecture | ✅ | Routes → Controllers → Services → Data (proper layering) |
| 🔐 Security-First | ✅ | Auth/AuthZ separated, input validated, no secrets in logs |
🛠️ Development
# Install dependencies
yarn install
# Build the plugin
yarn build
# Watch mode (auto-rebuild on changes)
yarn watch
# Lint everything
yarn lint
# Lint + auto-fix
yarn lint:fix
# Check formatting
yarn format:check
# Auto-format everything
yarn format
# Verify plugin exports
yarn verify🔮 Roadmap
| Feature | Status | | ----------------------------- | ----------------- | | ⏰ Auto-Logout | ✅ Shipped | | 🚫 Single-Session Enforcement | ✅ Shipped | | 💀 Token Revocation Pipeline | ✅ Shipped | | ⚙️ Admin Settings UI | ✅ Shipped | | 🔑 Password Expiry | 🚧 In Development | | 🔄 Non-Reusable Passwords | 🚧 In Development | | 📝 Admin Activity Logs | 🔜 Planned | | 📊 Security Dashboard | 🔜 Planned | | 👊 Brute Force Detection | 🔜 Planned | | 👁️ Real-time Session Viewer | 🔜 Planned |
🗣️ Real Talk
"We installed this and now our interns can't share logins anymore." — A CTO, probably
"Our admin panel feels like it judges us now. I love it." — That one developer who actually cares
"I left my desk for coffee and came back logged out. Respect." — Someone who now understands security
👥 Author
LPIX-11 — Orange / Sonatel
⚖️ License
MIT — Do whatever you want. Just don't blame us if you turn off all the features and get breached. That's on you.
💡 Philosophy
Security should be:
- Fast — In-memory. No database lookups on every request.
- Unforgiving — Idle? Gone. Revoked? Dead. Duplicated? Blocked.
- Elegant — Clean constants, typed errors, layered architecture.
- Mildly judgmental — This plugin will side-eye your stale sessions.
"The meta-principle: make the right thing the default thing. Discipline compounds. Shortcuts compound too, just in the wrong direction."
