@vivinkv28/strapi-2fa-admin-plugin 0.1.0 → 0.1.2

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.
Files changed (2) hide show
  1. package/README.md +172 -94
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -2,49 +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 package handles:
5
+ ## What This Plugin Handles
6
6
 
7
- - admin credential check
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
- This package does **not** replace the Strapi admin login UI by itself. You still need a frontend/admin integration layer that calls the plugin endpoints.
14
+ ## Important Scope
15
15
 
16
- ## Documentation
16
+ This package is a backend/admin-auth engine.
17
17
 
18
- - [Integration guide](./docs/INTEGRATION.md)
19
- - [Architecture guide](./docs/ARCHITECTURE.md)
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
20
25
 
21
26
  ## Endpoints
22
27
 
23
- The plugin exposes these content API routes:
28
+ The plugin exposes these routes:
24
29
 
25
30
  - `POST /api/admin-2fa/login`
26
31
  - `POST /api/admin-2fa/verify`
27
32
  - `POST /api/admin-2fa/resend`
28
33
 
29
- See the full request and response flow in [docs/INTEGRATION.md](./docs/INTEGRATION.md).
30
-
31
34
  ## Requirements
32
35
 
33
36
  - Strapi 5
34
37
  - Node.js `20.x` or `22.x`
35
- - A configured Strapi email provider
36
-
37
- ## Install In An Existing Strapi Project
38
+ - a configured Strapi email provider
38
39
 
39
- ### Option 1: Use as a published npm package
40
-
41
- Install the package in your Strapi app:
40
+ ## Install
42
41
 
43
42
  ```bash
44
43
  npm install @vivinkv28/strapi-2fa-admin-plugin
45
44
  ```
46
45
 
47
- Then enable it in your Strapi app plugin config:
46
+ ## Enable In A Strapi Project
47
+
48
+ Add the plugin to your Strapi app config:
48
49
 
49
50
  ```ts
50
51
  // config/plugins.ts
@@ -76,73 +77,133 @@ const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Plugin =>
76
77
  export default config;
77
78
  ```
78
79
 
79
- ### Option 2: Use as a local external plugin
80
+ ## Admin UI Integration
80
81
 
81
- If the plugin lives next to your Strapi app, point Strapi to it with `resolve`:
82
+ Because this package does not inject the full login UI by itself, the host project must integrate the admin flow.
82
83
 
83
- ```ts
84
- // config/plugins.ts
85
- import type { Core } from "@strapi/strapi";
84
+ ### Expected UI flow
86
85
 
87
- const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Plugin => ({
88
- "admin-2fa": {
89
- enabled: true,
90
- resolve: "../strapi-2fa-admin-plugin",
91
- config: {
92
- otpDigits: env.int("ADMIN_OTP_DIGITS", 6),
93
- otpTtlSeconds: env.int("ADMIN_OTP_TTL_SECONDS", 300),
94
- maxAttempts: env.int("ADMIN_OTP_MAX_ATTEMPTS", 5),
95
- maxResends: env.int("ADMIN_OTP_MAX_RESENDS", 3),
96
- rateLimitWindowSeconds: env.int("ADMIN_OTP_RATE_LIMIT_WINDOW_SECONDS", 900),
97
- loginIpLimit: env.int("ADMIN_OTP_LOGIN_IP_LIMIT", 10),
98
- loginEmailLimit: env.int("ADMIN_OTP_LOGIN_EMAIL_LIMIT", 5),
99
- verifyIpLimit: env.int("ADMIN_OTP_VERIFY_IP_LIMIT", 20),
100
- verifyEmailLimit: env.int("ADMIN_OTP_VERIFY_EMAIL_LIMIT", 10),
101
- resendIpLimit: env.int("ADMIN_OTP_RESEND_IP_LIMIT", 10),
102
- resendEmailLimit: env.int("ADMIN_OTP_RESEND_EMAIL_LIMIT", 5),
103
- debugTimings: env.bool(
104
- "ADMIN_OTP_DEBUG_TIMINGS",
105
- env("NODE_ENV", "development") !== "production"
106
- ),
107
- },
108
- },
109
- });
86
+ #### 1. Credentials step
110
87
 
111
- export default config;
88
+ - collect email and password
89
+ - send them to `POST /api/admin-2fa/login`
90
+ - if successful, store `challengeId` and switch to OTP mode
91
+
92
+ #### 2. OTP step
93
+
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`
98
+
99
+ ### Recommended UI behavior
100
+
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
105
+
106
+ ## Integration Guide
107
+
108
+ ### Login request
109
+
110
+ ```http
111
+ POST /api/admin-2fa/login
112
+ Content-Type: application/json
113
+ ```
114
+
115
+ Example payload:
116
+
117
+ ```json
118
+ {
119
+ "email": "admin@example.com",
120
+ "password": "super-secret-password",
121
+ "rememberMe": true,
122
+ "deviceId": "browser-device-id"
123
+ }
124
+ ```
125
+
126
+ Example success response:
127
+
128
+ ```json
129
+ {
130
+ "data": {
131
+ "challengeId": "0d3af6fd-b351-4d1e-bb81-2a8201d8a0f4",
132
+ "expiresAt": "2026-04-05T18:30:00.000Z",
133
+ "maskedEmail": "admin@example.com",
134
+ "rememberMe": true
135
+ }
136
+ }
137
+ ```
138
+
139
+ ### Verify request
140
+
141
+ ```http
142
+ POST /api/admin-2fa/verify
143
+ Content-Type: application/json
112
144
  ```
113
145
 
114
- ## Required Host App Setup
146
+ Example payload:
115
147
 
116
- ### 1. Configure an email provider
148
+ ```json
149
+ {
150
+ "challengeId": "0d3af6fd-b351-4d1e-bb81-2a8201d8a0f4",
151
+ "code": "123456"
152
+ }
153
+ ```
154
+
155
+ Example success response:
156
+
157
+ ```json
158
+ {
159
+ "data": {
160
+ "token": "<access-token>",
161
+ "accessToken": "<access-token>",
162
+ "user": {
163
+ "id": 1,
164
+ "email": "admin@example.com"
165
+ }
166
+ }
167
+ }
168
+ ```
117
169
 
118
- The plugin uses Strapi's `email` plugin to send OTP emails. Your host project must configure an email provider in `config/plugins.ts`.
170
+ ### Resend request
119
171
 
120
- ### 2. Add an admin login integration layer
172
+ ```http
173
+ POST /api/admin-2fa/resend
174
+ Content-Type: application/json
175
+ ```
176
+
177
+ Example payload:
178
+
179
+ ```json
180
+ {
181
+ "challengeId": "0d3af6fd-b351-4d1e-bb81-2a8201d8a0f4"
182
+ }
183
+ ```
121
184
 
122
- This plugin is backend-only. To use it for real admin login 2FA, your project must:
185
+ ### UI error states to handle
123
186
 
124
- - intercept the normal admin login flow
125
- - call `POST /api/admin-2fa/login`
126
- - show an OTP input step
127
- - call `POST /api/admin-2fa/verify`
128
- - optionally call `POST /api/admin-2fa/resend`
187
+ - invalid email or password
188
+ - OTP expired
189
+ - OTP session not found
190
+ - invalid OTP code
191
+ - too many authentication attempts
192
+ - maximum resend attempts exceeded
129
193
 
130
- The expected frontend flow, payloads, and response handling are documented in [docs/INTEGRATION.md](./docs/INTEGRATION.md).
194
+ ## Host Project Requirements
131
195
 
132
- If you already maintain a patched Strapi admin login screen, point it to:
196
+ ### Email provider
133
197
 
134
- - `/api/admin-2fa/login`
135
- - `/api/admin-2fa/verify`
136
- - `/api/admin-2fa/resend`
198
+ The plugin sends OTP emails through Strapi's email plugin, so the host project must configure an email provider.
137
199
 
138
- ### 3. Ensure proxy / HTTPS settings are correct in production
200
+ ### Proxy and HTTPS
139
201
 
140
- If your Strapi app runs behind a proxy, make sure `config/server.ts` is configured correctly so secure admin cookies work.
202
+ If the project runs behind a reverse proxy, configure `config/server.ts` correctly so secure admin cookies work.
141
203
 
142
- Example:
204
+ Typical example:
143
205
 
144
206
  ```ts
145
- // config/server.ts
146
207
  import type { Core } from "@strapi/strapi";
147
208
 
148
209
  const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Server => ({
@@ -160,7 +221,7 @@ export default config;
160
221
 
161
222
  ## Environment Variables
162
223
 
163
- Suggested variables for the host Strapi project:
224
+ Suggested defaults:
164
225
 
165
226
  ```env
166
227
  ADMIN_OTP_DIGITS=6
@@ -177,51 +238,68 @@ ADMIN_OTP_RESEND_EMAIL_LIMIT=5
177
238
  ADMIN_OTP_DEBUG_TIMINGS=false
178
239
  ```
179
240
 
180
- ## Plugin Development
241
+ ## Code-Level Architecture
181
242
 
182
- Install dependencies:
243
+ Main files:
183
244
 
184
- ```bash
185
- npm install
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
186
252
  ```
187
253
 
188
- Build the plugin:
254
+ Responsibilities:
189
255
 
190
- ```bash
191
- npm run build
192
- ```
256
+ - `admin/src/index.js`
257
+ Minimal admin plugin stub required by the Strapi Plugin SDK.
193
258
 
194
- Watch during development:
259
+ - `server/src/routes/index.js`
260
+ Declares the login, verify, and resend routes.
195
261
 
196
- ```bash
197
- npm run watch
198
- ```
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.
199
264
 
200
- Verify publishable output:
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.
201
267
 
202
- ```bash
203
- npm run verify
204
- ```
268
+ - `server/src/utils/strapi-session-auth.js`
269
+ Runtime helper that resolves Strapi's internal admin session utility for final session creation.
205
270
 
206
- Link to a Strapi project with the SDK workflow:
271
+ ## Repo Docs
272
+
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
207
279
 
208
280
  ```bash
209
- npm run watch:link
281
+ npm install
282
+ npm run build
210
283
  ```
211
284
 
212
- ## Publishing Checklist
285
+ Useful commands:
213
286
 
214
- Before publishing:
287
+ - `npm run build`
288
+ - `npm run watch`
289
+ - `npm run watch:link`
290
+ - `npm run verify`
291
+
292
+ ## Publishing Checklist
215
293
 
216
- 1. Run `npm install`
217
- 2. Run `npm run build`
218
- 3. Run `npm run verify`
219
- 4. Confirm the host Strapi app works with the built package
220
- 5. Update the package version
221
- 6. Publish with `npm publish`
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`
222
300
 
223
301
  ## Production Notes
224
302
 
225
- - Email OTP is a practical 2FA improvement, but it is weaker than TOTP or WebAuthn.
226
- - If an attacker controls the admin email inbox, they can still complete the second factor.
227
- - For stronger security, consider adding trusted devices, backup codes, TOTP, or passkeys in a future version.
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vivinkv28/strapi-2fa-admin-plugin",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Reusable Strapi admin 2FA plugin",
5
5
  "type": "commonjs",
6
6
  "keywords": [