@vivinkv28/strapi-2fa-admin-plugin 0.1.1 → 0.1.4
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 +88 -190
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,84 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
`@vivinkv28/strapi-2fa-admin-plugin` is a Strapi 5 plugin that provides the backend side of an OTP-based 2FA flow for Strapi admin authentication.
|
|
4
4
|
|
|
5
|
-
This
|
|
5
|
+
## What This Plugin Handles
|
|
6
6
|
|
|
7
|
-
- admin credential
|
|
7
|
+
- admin credential validation
|
|
8
8
|
- OTP challenge generation and hashing
|
|
9
9
|
- OTP resend and verification
|
|
10
10
|
- rate limiting for login, verify, and resend
|
|
11
11
|
- OTP delivery through Strapi's email plugin
|
|
12
12
|
- final Strapi admin session creation after OTP verification
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## Important Scope
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
This package is a backend/admin-auth engine.
|
|
17
17
|
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
It does **not** replace the Strapi admin login UI by itself. Your host project still needs an admin-side integration layer that:
|
|
19
|
+
|
|
20
|
+
1. collects admin email and password
|
|
21
|
+
2. calls the plugin login endpoint
|
|
22
|
+
3. shows an OTP input UI
|
|
23
|
+
4. calls the plugin verify endpoint
|
|
24
|
+
5. optionally calls the resend endpoint
|
|
21
25
|
|
|
22
26
|
## Endpoints
|
|
23
27
|
|
|
24
|
-
The plugin exposes these
|
|
28
|
+
The plugin exposes these routes:
|
|
25
29
|
|
|
26
30
|
- `POST /api/admin-2fa/login`
|
|
27
31
|
- `POST /api/admin-2fa/verify`
|
|
28
32
|
- `POST /api/admin-2fa/resend`
|
|
29
33
|
|
|
30
|
-
See the detailed request and response flow in the `Integration Guide` section below.
|
|
31
|
-
|
|
32
34
|
## Requirements
|
|
33
35
|
|
|
34
36
|
- Strapi 5
|
|
35
37
|
- Node.js `20.x` or `22.x`
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
## Install In An Existing Strapi Project
|
|
39
|
-
|
|
40
|
-
### Option 1: Use as a published npm package
|
|
38
|
+
- a configured Strapi email provider
|
|
41
39
|
|
|
42
|
-
Install
|
|
40
|
+
## Install
|
|
43
41
|
|
|
44
42
|
```bash
|
|
45
43
|
npm install @vivinkv28/strapi-2fa-admin-plugin
|
|
46
44
|
```
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
## Enable In A Strapi Project
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
// config/plugins.ts
|
|
52
|
-
import type { Core } from "@strapi/strapi";
|
|
53
|
-
|
|
54
|
-
const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Plugin => ({
|
|
55
|
-
"admin-2fa": {
|
|
56
|
-
enabled: true,
|
|
57
|
-
config: {
|
|
58
|
-
otpDigits: env.int("ADMIN_OTP_DIGITS", 6),
|
|
59
|
-
otpTtlSeconds: env.int("ADMIN_OTP_TTL_SECONDS", 300),
|
|
60
|
-
maxAttempts: env.int("ADMIN_OTP_MAX_ATTEMPTS", 5),
|
|
61
|
-
maxResends: env.int("ADMIN_OTP_MAX_RESENDS", 3),
|
|
62
|
-
rateLimitWindowSeconds: env.int("ADMIN_OTP_RATE_LIMIT_WINDOW_SECONDS", 900),
|
|
63
|
-
loginIpLimit: env.int("ADMIN_OTP_LOGIN_IP_LIMIT", 10),
|
|
64
|
-
loginEmailLimit: env.int("ADMIN_OTP_LOGIN_EMAIL_LIMIT", 5),
|
|
65
|
-
verifyIpLimit: env.int("ADMIN_OTP_VERIFY_IP_LIMIT", 20),
|
|
66
|
-
verifyEmailLimit: env.int("ADMIN_OTP_VERIFY_EMAIL_LIMIT", 10),
|
|
67
|
-
resendIpLimit: env.int("ADMIN_OTP_RESEND_IP_LIMIT", 10),
|
|
68
|
-
resendEmailLimit: env.int("ADMIN_OTP_RESEND_EMAIL_LIMIT", 5),
|
|
69
|
-
debugTimings: env.bool(
|
|
70
|
-
"ADMIN_OTP_DEBUG_TIMINGS",
|
|
71
|
-
env("NODE_ENV", "development") !== "production"
|
|
72
|
-
),
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
export default config;
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Option 2: Use as a local external plugin
|
|
81
|
-
|
|
82
|
-
If the plugin lives next to your Strapi app, point Strapi to it with `resolve`:
|
|
48
|
+
Add the plugin to your Strapi app config:
|
|
83
49
|
|
|
84
50
|
```ts
|
|
85
51
|
// config/plugins.ts
|
|
@@ -88,7 +54,6 @@ import type { Core } from "@strapi/strapi";
|
|
|
88
54
|
const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Plugin => ({
|
|
89
55
|
"admin-2fa": {
|
|
90
56
|
enabled: true,
|
|
91
|
-
resolve: "../strapi-2fa-admin-plugin",
|
|
92
57
|
config: {
|
|
93
58
|
otpDigits: env.int("ADMIN_OTP_DIGITS", 6),
|
|
94
59
|
otpTtlSeconds: env.int("ADMIN_OTP_TTL_SECONDS", 300),
|
|
@@ -112,36 +77,33 @@ const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Plugin =>
|
|
|
112
77
|
export default config;
|
|
113
78
|
```
|
|
114
79
|
|
|
115
|
-
##
|
|
116
|
-
|
|
117
|
-
### 1. Configure an email provider
|
|
80
|
+
## Admin UI Integration
|
|
118
81
|
|
|
119
|
-
|
|
82
|
+
Because this package does not inject the full login UI by itself, the host project must integrate the admin flow.
|
|
120
83
|
|
|
121
|
-
###
|
|
84
|
+
### Expected UI flow
|
|
122
85
|
|
|
123
|
-
|
|
86
|
+
#### 1. Credentials step
|
|
124
87
|
|
|
125
|
-
-
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
- call `POST /api/admin-2fa/verify`
|
|
129
|
-
- optionally call `POST /api/admin-2fa/resend`
|
|
88
|
+
- collect email and password
|
|
89
|
+
- send them to `POST /api/admin-2fa/login`
|
|
90
|
+
- if successful, store `challengeId` and switch to OTP mode
|
|
130
91
|
|
|
131
|
-
|
|
132
|
-
The expected frontend flow, payloads, and response handling are documented in the `Integration Guide` and `Admin UI Integration` sections below.
|
|
92
|
+
#### 2. OTP step
|
|
133
93
|
|
|
134
|
-
|
|
94
|
+
- collect the OTP code
|
|
95
|
+
- send it to `POST /api/admin-2fa/verify`
|
|
96
|
+
- if successful, continue the normal authenticated admin flow
|
|
97
|
+
- provide a resend action that calls `POST /api/admin-2fa/resend`
|
|
135
98
|
|
|
136
|
-
|
|
99
|
+
### Recommended UI behavior
|
|
137
100
|
|
|
138
|
-
|
|
101
|
+
- keep login and OTP as separate form states
|
|
102
|
+
- do not treat password validation as a completed login
|
|
103
|
+
- complete the login only after `/verify` succeeds
|
|
104
|
+
- restart from the credentials step if the challenge expires
|
|
139
105
|
|
|
140
|
-
|
|
141
|
-
2. call `POST /api/admin-2fa/login`
|
|
142
|
-
3. display an OTP entry screen
|
|
143
|
-
4. call `POST /api/admin-2fa/verify`
|
|
144
|
-
5. optionally call `POST /api/admin-2fa/resend`
|
|
106
|
+
## Integration Guide
|
|
145
107
|
|
|
146
108
|
### Login request
|
|
147
109
|
|
|
@@ -227,102 +189,21 @@ Example payload:
|
|
|
227
189
|
- OTP session not found
|
|
228
190
|
- invalid OTP code
|
|
229
191
|
- too many authentication attempts
|
|
230
|
-
- resend
|
|
231
|
-
|
|
232
|
-
## Admin UI Integration
|
|
233
|
-
|
|
234
|
-
This package is backend-focused. To make it usable in a real Strapi admin login flow, the host project must provide an admin-side integration.
|
|
235
|
-
|
|
236
|
-
A typical admin UI integration has two screens or states:
|
|
237
|
-
|
|
238
|
-
### 1. Credentials step
|
|
239
|
-
|
|
240
|
-
- collect email and password
|
|
241
|
-
- send them to `/api/admin-2fa/login`
|
|
242
|
-
- if successful, store `challengeId` in memory and switch the UI into OTP mode
|
|
243
|
-
|
|
244
|
-
### 2. OTP step
|
|
245
|
-
|
|
246
|
-
- collect the OTP code
|
|
247
|
-
- submit it to `/api/admin-2fa/verify`
|
|
248
|
-
- if successful, continue the normal authenticated admin flow
|
|
249
|
-
- provide a resend button that calls `/api/admin-2fa/resend`
|
|
250
|
-
|
|
251
|
-
### Recommended UI behavior
|
|
252
|
-
|
|
253
|
-
- keep login and OTP as separate form states
|
|
254
|
-
- do not create a session after password validation alone
|
|
255
|
-
- only treat the login as complete after `/verify` succeeds
|
|
256
|
-
- if resend or verify says the challenge expired, restart from the credentials step
|
|
257
|
-
|
|
258
|
-
### Current integration approach used by the example project
|
|
259
|
-
|
|
260
|
-
In the companion Strapi app used during development, the admin login UI is integrated by patching Strapi's admin login screen and auth service so they call:
|
|
192
|
+
- maximum resend attempts exceeded
|
|
261
193
|
|
|
262
|
-
|
|
263
|
-
- `/api/admin-2fa/verify`
|
|
264
|
-
- `/api/admin-2fa/resend`
|
|
194
|
+
## Host Project Requirements
|
|
265
195
|
|
|
266
|
-
|
|
196
|
+
### Email provider
|
|
267
197
|
|
|
268
|
-
|
|
198
|
+
The plugin sends OTP emails through Strapi's email plugin, so the host project must configure an email provider.
|
|
269
199
|
|
|
270
|
-
|
|
200
|
+
### Proxy and HTTPS
|
|
271
201
|
|
|
272
|
-
|
|
202
|
+
If the project runs behind a reverse proxy, configure `config/server.ts` correctly so secure admin cookies work.
|
|
273
203
|
|
|
274
|
-
|
|
275
|
-
admin/src/index.js
|
|
276
|
-
server/src/index.js
|
|
277
|
-
server/src/routes/index.js
|
|
278
|
-
server/src/controllers/auth.js
|
|
279
|
-
server/src/services/auth.js
|
|
280
|
-
server/src/utils/strapi-session-auth.js
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
### Responsibilities
|
|
284
|
-
|
|
285
|
-
- `admin/src/index.js`
|
|
286
|
-
Minimal admin plugin stub required by the Strapi Plugin SDK.
|
|
287
|
-
|
|
288
|
-
- `server/src/routes/index.js`
|
|
289
|
-
Declares the plugin routes for login, verify, and resend.
|
|
290
|
-
|
|
291
|
-
- `server/src/controllers/auth.js`
|
|
292
|
-
Reads requests, extracts client IP, delegates to the service, and sets the admin refresh cookie after successful verification.
|
|
293
|
-
|
|
294
|
-
- `server/src/services/auth.js`
|
|
295
|
-
Core OTP logic: credential validation, challenge lifecycle, resend/verify rules, rate limiting, email sending, and final session creation.
|
|
296
|
-
|
|
297
|
-
- `server/src/utils/strapi-session-auth.js`
|
|
298
|
-
Runtime helper that resolves Strapi's internal admin session utility needed to create the final admin session.
|
|
299
|
-
|
|
300
|
-
### Security model
|
|
301
|
-
|
|
302
|
-
- password-only login is not enough
|
|
303
|
-
- raw OTP values are never stored
|
|
304
|
-
- OTPs expire
|
|
305
|
-
- verify/resend/login are rate-limited
|
|
306
|
-
- the final Strapi admin session is created only after OTP verification
|
|
307
|
-
|
|
308
|
-
### Important limitation
|
|
309
|
-
|
|
310
|
-
This is email OTP, not TOTP or WebAuthn. It improves admin security substantially, but it is still weaker than authenticator-app or passkey-based 2FA.
|
|
311
|
-
|
|
312
|
-
If you already maintain a patched Strapi admin login screen, point it to:
|
|
313
|
-
|
|
314
|
-
- `/api/admin-2fa/login`
|
|
315
|
-
- `/api/admin-2fa/verify`
|
|
316
|
-
- `/api/admin-2fa/resend`
|
|
317
|
-
|
|
318
|
-
### 3. Ensure proxy / HTTPS settings are correct in production
|
|
319
|
-
|
|
320
|
-
If your Strapi app runs behind a proxy, make sure `config/server.ts` is configured correctly so secure admin cookies work.
|
|
321
|
-
|
|
322
|
-
Example:
|
|
204
|
+
Typical example:
|
|
323
205
|
|
|
324
206
|
```ts
|
|
325
|
-
// config/server.ts
|
|
326
207
|
import type { Core } from "@strapi/strapi";
|
|
327
208
|
|
|
328
209
|
const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Server => ({
|
|
@@ -340,7 +221,7 @@ export default config;
|
|
|
340
221
|
|
|
341
222
|
## Environment Variables
|
|
342
223
|
|
|
343
|
-
Suggested
|
|
224
|
+
Suggested defaults:
|
|
344
225
|
|
|
345
226
|
```env
|
|
346
227
|
ADMIN_OTP_DIGITS=6
|
|
@@ -357,51 +238,68 @@ ADMIN_OTP_RESEND_EMAIL_LIMIT=5
|
|
|
357
238
|
ADMIN_OTP_DEBUG_TIMINGS=false
|
|
358
239
|
```
|
|
359
240
|
|
|
360
|
-
##
|
|
241
|
+
## Code-Level Architecture
|
|
361
242
|
|
|
362
|
-
|
|
243
|
+
Main files:
|
|
363
244
|
|
|
364
|
-
```
|
|
365
|
-
|
|
245
|
+
```text
|
|
246
|
+
admin/src/index.js
|
|
247
|
+
server/src/index.js
|
|
248
|
+
server/src/routes/index.js
|
|
249
|
+
server/src/controllers/auth.js
|
|
250
|
+
server/src/services/auth.js
|
|
251
|
+
server/src/utils/strapi-session-auth.js
|
|
366
252
|
```
|
|
367
253
|
|
|
368
|
-
|
|
254
|
+
Responsibilities:
|
|
369
255
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
```
|
|
256
|
+
- `admin/src/index.js`
|
|
257
|
+
Minimal admin plugin stub required by the Strapi Plugin SDK.
|
|
373
258
|
|
|
374
|
-
|
|
259
|
+
- `server/src/routes/index.js`
|
|
260
|
+
Declares the login, verify, and resend routes.
|
|
375
261
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
262
|
+
- `server/src/controllers/auth.js`
|
|
263
|
+
Reads the request, extracts client IP, delegates to the service, and sets the admin refresh cookie after successful OTP verification.
|
|
264
|
+
|
|
265
|
+
- `server/src/services/auth.js`
|
|
266
|
+
Core OTP logic: credential validation, challenge lifecycle, resend/verify rules, rate limiting, email sending, and final session creation.
|
|
379
267
|
|
|
380
|
-
|
|
268
|
+
- `server/src/utils/strapi-session-auth.js`
|
|
269
|
+
Runtime helper that resolves Strapi's internal admin session utility for final session creation.
|
|
381
270
|
|
|
382
|
-
|
|
383
|
-
npm run verify
|
|
384
|
-
```
|
|
271
|
+
## Repo Docs
|
|
385
272
|
|
|
386
|
-
|
|
273
|
+
If you are reading the source repository directly, deeper docs are also available in:
|
|
274
|
+
|
|
275
|
+
- `docs/INTEGRATION.md`
|
|
276
|
+
- `docs/ARCHITECTURE.md`
|
|
277
|
+
|
|
278
|
+
## Development
|
|
387
279
|
|
|
388
280
|
```bash
|
|
389
|
-
npm
|
|
281
|
+
npm install
|
|
282
|
+
npm run build
|
|
390
283
|
```
|
|
391
284
|
|
|
392
|
-
|
|
285
|
+
Useful commands:
|
|
286
|
+
|
|
287
|
+
- `npm run build`
|
|
288
|
+
- `npm run watch`
|
|
289
|
+
- `npm run watch:link`
|
|
290
|
+
- `npm run verify`
|
|
393
291
|
|
|
394
|
-
|
|
292
|
+
## Publishing Checklist
|
|
395
293
|
|
|
396
|
-
1.
|
|
397
|
-
2.
|
|
398
|
-
3.
|
|
399
|
-
4.
|
|
400
|
-
5.
|
|
401
|
-
6.
|
|
294
|
+
1. run `npm install`
|
|
295
|
+
2. run `npm run build`
|
|
296
|
+
3. run `npm run verify`
|
|
297
|
+
4. verify the plugin in a real Strapi app
|
|
298
|
+
5. bump the version
|
|
299
|
+
6. publish with `npm publish --access public`
|
|
402
300
|
|
|
403
301
|
## Production Notes
|
|
404
302
|
|
|
405
|
-
- Email OTP is
|
|
406
|
-
- If
|
|
407
|
-
- For stronger security, consider
|
|
303
|
+
- Email OTP is better than password-only login, but weaker than TOTP or WebAuthn.
|
|
304
|
+
- If the admin mailbox is compromised, the second factor can still be bypassed.
|
|
305
|
+
- For stronger security later, consider TOTP, backup codes, trusted devices, or passkeys.
|