@vivinkv28/strapi-2fa-admin-plugin 0.1.0 → 0.1.1
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 +183 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,8 +15,9 @@ This package does **not** replace the Strapi admin login UI by itself. You still
|
|
|
15
15
|
|
|
16
16
|
## Documentation
|
|
17
17
|
|
|
18
|
-
- [Integration
|
|
19
|
-
- [Architecture
|
|
18
|
+
- [Integration Guide](#integration-guide)
|
|
19
|
+
- [Architecture Guide](#architecture-guide)
|
|
20
|
+
- [Admin UI Integration](#admin-ui-integration)
|
|
20
21
|
|
|
21
22
|
## Endpoints
|
|
22
23
|
|
|
@@ -26,7 +27,7 @@ The plugin exposes these content API routes:
|
|
|
26
27
|
- `POST /api/admin-2fa/verify`
|
|
27
28
|
- `POST /api/admin-2fa/resend`
|
|
28
29
|
|
|
29
|
-
See the
|
|
30
|
+
See the detailed request and response flow in the `Integration Guide` section below.
|
|
30
31
|
|
|
31
32
|
## Requirements
|
|
32
33
|
|
|
@@ -128,6 +129,185 @@ This plugin is backend-only. To use it for real admin login 2FA, your project mu
|
|
|
128
129
|
- optionally call `POST /api/admin-2fa/resend`
|
|
129
130
|
|
|
130
131
|
The expected frontend flow, payloads, and response handling are documented in [docs/INTEGRATION.md](./docs/INTEGRATION.md).
|
|
132
|
+
The expected frontend flow, payloads, and response handling are documented in the `Integration Guide` and `Admin UI Integration` sections below.
|
|
133
|
+
|
|
134
|
+
## Integration Guide
|
|
135
|
+
|
|
136
|
+
This plugin is intended to be used as the backend engine for an admin OTP login flow.
|
|
137
|
+
|
|
138
|
+
The normal integration flow is:
|
|
139
|
+
|
|
140
|
+
1. collect admin email and password
|
|
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`
|
|
145
|
+
|
|
146
|
+
### Login request
|
|
147
|
+
|
|
148
|
+
```http
|
|
149
|
+
POST /api/admin-2fa/login
|
|
150
|
+
Content-Type: application/json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Example payload:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"email": "admin@example.com",
|
|
158
|
+
"password": "super-secret-password",
|
|
159
|
+
"rememberMe": true,
|
|
160
|
+
"deviceId": "browser-device-id"
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Example success response:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"data": {
|
|
169
|
+
"challengeId": "0d3af6fd-b351-4d1e-bb81-2a8201d8a0f4",
|
|
170
|
+
"expiresAt": "2026-04-05T18:30:00.000Z",
|
|
171
|
+
"maskedEmail": "admin@example.com",
|
|
172
|
+
"rememberMe": true
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Verify request
|
|
178
|
+
|
|
179
|
+
```http
|
|
180
|
+
POST /api/admin-2fa/verify
|
|
181
|
+
Content-Type: application/json
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Example payload:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"challengeId": "0d3af6fd-b351-4d1e-bb81-2a8201d8a0f4",
|
|
189
|
+
"code": "123456"
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Example success response:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"data": {
|
|
198
|
+
"token": "<access-token>",
|
|
199
|
+
"accessToken": "<access-token>",
|
|
200
|
+
"user": {
|
|
201
|
+
"id": 1,
|
|
202
|
+
"email": "admin@example.com"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Resend request
|
|
209
|
+
|
|
210
|
+
```http
|
|
211
|
+
POST /api/admin-2fa/resend
|
|
212
|
+
Content-Type: application/json
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Example payload:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"challengeId": "0d3af6fd-b351-4d1e-bb81-2a8201d8a0f4"
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### UI error states to handle
|
|
224
|
+
|
|
225
|
+
- invalid email or password
|
|
226
|
+
- OTP expired
|
|
227
|
+
- OTP session not found
|
|
228
|
+
- invalid OTP code
|
|
229
|
+
- too many authentication attempts
|
|
230
|
+
- resend limit exceeded
|
|
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:
|
|
261
|
+
|
|
262
|
+
- `/api/admin-2fa/login`
|
|
263
|
+
- `/api/admin-2fa/verify`
|
|
264
|
+
- `/api/admin-2fa/resend`
|
|
265
|
+
|
|
266
|
+
This plugin itself does not inject that UI automatically. That choice is left to the host app because Strapi admin login customization is more app-specific than the backend OTP engine.
|
|
267
|
+
|
|
268
|
+
## Architecture Guide
|
|
269
|
+
|
|
270
|
+
The plugin has a minimal admin entry and a backend-focused server implementation.
|
|
271
|
+
|
|
272
|
+
### Main files
|
|
273
|
+
|
|
274
|
+
```text
|
|
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.
|
|
131
311
|
|
|
132
312
|
If you already maintain a patched Strapi admin login screen, point it to:
|
|
133
313
|
|