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.
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.
| 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" |
yarn add strapi-security-suiteAdd this to your config/plugins.js (or .ts):
module.exports = ({ env }) => ({
'strapi-security-suite': {
enabled: true,
},
});yarn developGo to Settings โ Global โ Security Suite
That's it. You're done. Go get a coffee. โ
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.
Here's the whole flow, explained like you're five (but a very smart five):
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."
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 ๐
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.
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
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 |
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 |
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 |
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 |
# 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| 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 |
"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
LPIX-11 โ Orange / Sonatel
MIT โ Do whatever you want. Just don't blame us if you turn off all the features and get breached. That's on you.
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."