@vivinkv28/strapi-2fa-admin-plugin 0.1.6 → 0.1.8

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 CHANGED
@@ -145,6 +145,34 @@ your-project/
145
145
 
146
146
  This keeps your admin customizations reproducible and easy to reapply after `npm install`.
147
147
 
148
+ ## Full Host Patch Source Included In This Package
149
+
150
+ This npm package now includes the ready-to-copy host-side admin patch source directly inside the package under:
151
+
152
+ ```text
153
+ host-patch/
154
+ apply-strapi-admin-2fa-patch.js
155
+ strapi-admin-2fa-patch/
156
+ services/
157
+ auth.js
158
+ auth.mjs
159
+ pages/
160
+ Auth/
161
+ components/
162
+ Login.js
163
+ Login.mjs
164
+ ```
165
+
166
+ Included files:
167
+
168
+ - `host-patch/apply-strapi-admin-2fa-patch.js`
169
+ - `host-patch/strapi-admin-2fa-patch/services/auth.js`
170
+ - `host-patch/strapi-admin-2fa-patch/services/auth.mjs`
171
+ - `host-patch/strapi-admin-2fa-patch/pages/Auth/components/Login.js`
172
+ - `host-patch/strapi-admin-2fa-patch/pages/Auth/components/Login.mjs`
173
+
174
+ These files are the exact host-side patch source for the admin login UI, OTP screen, and patch script, bundled in the npm package itself so you do not need a separate integration document.
175
+
148
176
  ## Step 5: Patch The Strapi Admin Auth Service
149
177
 
150
178
  Create these files in your Strapi project:
@@ -325,6 +353,156 @@ The working implementation used in the companion project includes:
325
353
  - auto focus
326
354
  - inline error state
327
355
 
356
+ ## Exact Admin UI Pieces You Need
357
+
358
+ If you want the admin OTP screen to look and behave like the working portfolio backend, these are the UI pieces that must exist in your patched `Login` component:
359
+
360
+ ### 1. Shared OTP constants and helpers
361
+
362
+ Use:
363
+
364
+ ```js
365
+ const OTP_LENGTH = 6;
366
+ const sanitizeOtp = (value = '') => value.replace(/\D/g, '').slice(0, OTP_LENGTH);
367
+ const createOtpDigits = (value = '') =>
368
+ Array.from({ length: OTP_LENGTH }, (_, index) => value[index] ?? '');
369
+ ```
370
+
371
+ Why this matters:
372
+
373
+ - forces numeric-only OTP input
374
+ - keeps the code fixed to 6 digits
375
+ - makes the 6-box UI easy to control
376
+
377
+ ### 2. Two screen states inside one login component
378
+
379
+ Your login screen should switch between:
380
+
381
+ - credentials screen
382
+ - OTP verification screen
383
+
384
+ The state shape used in the working backend is:
385
+
386
+ ```js
387
+ const [apiError, setApiError] = React.useState();
388
+ const [otpStep, setOtpStep] = React.useState(null);
389
+ ```
390
+
391
+ `otpStep` stores:
392
+
393
+ - `challengeId`
394
+ - `expiresAt`
395
+ - `maskedEmail`
396
+ - `rememberMe`
397
+
398
+ When `otpStep` is `null`, show email/password fields.
399
+
400
+ When `otpStep` exists, show the OTP UI.
401
+
402
+ ### 3. Patched auth hooks
403
+
404
+ Your patched auth service must export:
405
+
406
+ ```js
407
+ const {
408
+ useAdminLoginWithOtpMutation,
409
+ useVerifyAdminLoginOtpMutation,
410
+ useResendAdminLoginOtpMutation,
411
+ } = authService;
412
+ ```
413
+
414
+ Then the patched login screen uses:
415
+
416
+ ```js
417
+ const [adminLoginWithOtp, { isLoading: isLoggingIn }] = useAdminLoginWithOtpMutation();
418
+ const [verifyAdminLoginOtp, { isLoading: isVerifyingOtp }] =
419
+ useVerifyAdminLoginOtpMutation();
420
+ const [resendAdminLoginOtp, { isLoading: isResendingOtp }] =
421
+ useResendAdminLoginOtpMutation();
422
+ ```
423
+
424
+ ### 4. Credentials form step
425
+
426
+ In the first step, keep the normal Strapi fields:
427
+
428
+ - `email`
429
+ - `password`
430
+ - `rememberMe`
431
+
432
+ On submit:
433
+
434
+ 1. call the OTP login endpoint
435
+ 2. do not create a Strapi session yet
436
+ 3. store the returned `challengeId`, `expiresAt`, and `maskedEmail`
437
+ 4. switch to the OTP step
438
+
439
+ ### 5. OTP visual layout
440
+
441
+ The working backend UI uses:
442
+
443
+ - a heading such as `Enter your OTP code`
444
+ - a subtitle showing the masked email
445
+ - an expiry message
446
+ - a centered 6-digit input row
447
+ - a primary `Verify OTP` button
448
+ - a secondary `Back` button
449
+ - a tertiary `Resend OTP` button
450
+ - an inline error message area
451
+
452
+ ### 6. OTP input box behavior
453
+
454
+ The working UI is not a single text input. It is a 6-box OTP component with:
455
+
456
+ - one visible input per digit
457
+ - automatic focus movement
458
+ - paste support for full OTP values
459
+ - backspace support to move backward
460
+ - left/right arrow key navigation
461
+ - red error styling when validation fails
462
+
463
+ That behavior lives inside a dedicated `OtpField` component inside the patched `Login.js` / `Login.mjs` files.
464
+
465
+ ### 7. Verify flow
466
+
467
+ On OTP submit:
468
+
469
+ 1. call the verify endpoint with `challengeId` and `code`
470
+ 2. if it succeeds, dispatch the normal Strapi admin login action
471
+ 3. navigate to `/` or the `redirectTo` query target
472
+
473
+ ### 8. Resend flow
474
+
475
+ On resend:
476
+
477
+ 1. call the resend endpoint with `challengeId`
478
+ 2. keep the user on the OTP screen
479
+ 3. update `expiresAt`
480
+ 4. update `maskedEmail` if needed
481
+ 5. show a success notification
482
+
483
+ ### 9. Back button behavior
484
+
485
+ The working backend keeps a `Back` button on the OTP screen that simply resets:
486
+
487
+ ```js
488
+ setOtpStep(null);
489
+ ```
490
+
491
+ That sends the admin back to the email/password screen without refreshing the page.
492
+
493
+ ### 10. Why both `.js` and `.mjs` files are needed
494
+
495
+ Strapi ships both CommonJS and ESM admin build files inside `node_modules/@strapi/admin/...`.
496
+
497
+ To keep the admin patch stable, the same logic should be copied into both:
498
+
499
+ - `Login.js`
500
+ - `Login.mjs`
501
+ - `auth.js`
502
+ - `auth.mjs`
503
+
504
+ If you patch only one side, the admin build can drift or break depending on how Strapi resolves the files.
505
+
328
506
  ## Step 7: Add A Patch Apply Script
329
507
 
330
508
  Create:
@@ -455,66 +633,20 @@ After setup, test these cases:
455
633
  5. expired OTP restarts the flow properly
456
634
  6. wrong email/password still fails safely
457
635
 
458
- ## Code-Level Overview
459
-
460
- Main plugin files:
461
-
462
- ```text
463
- admin/src/index.js
464
- server/src/index.js
465
- server/src/routes/index.js
466
- server/src/controllers/auth.js
467
- server/src/services/auth.js
468
- server/src/utils/strapi-session-auth.js
469
- ```
470
-
471
- Responsibilities:
472
-
473
- - `admin/src/index.js`
474
- Minimal admin plugin stub required by the Strapi Plugin SDK.
475
-
476
- - `server/src/routes/index.js`
477
- Declares `/login`, `/verify`, and `/resend`.
478
-
479
- - `server/src/controllers/auth.js`
480
- Extracts request data, resolves client IP, sets refresh cookies after verification.
481
-
482
- - `server/src/services/auth.js`
483
- Core OTP engine: credentials, challenge lifecycle, rate limits, email sending, and session creation.
484
-
485
- - `server/src/utils/strapi-session-auth.js`
486
- Resolves Strapi's internal admin session helper at runtime.
487
-
488
- ## Deeper Docs
489
-
490
- If you want more detail from the repository:
491
-
492
- - `docs/INTEGRATION.md`
493
- - `docs/ARCHITECTURE.md`
494
- - `admin-screen.md`
495
-
496
- ## Development
497
-
498
- ```bash
499
- npm install
500
- npm run build
501
- ```
502
-
503
- Useful commands:
504
-
505
- - `npm run build`
506
- - `npm run watch`
507
- - `npm run watch:link`
508
- - `npm run verify`
636
+ ## Admin UI Documentation Summary
509
637
 
510
- ## Publishing Checklist
638
+ If you want your project README or integration guide to document the admin UI clearly, include these sections in order:
511
639
 
512
- 1. run `npm install`
513
- 2. run `npm run build`
514
- 3. run `npm run verify`
515
- 4. test in a real Strapi app
516
- 5. bump the version
517
- 6. run `npm publish --access public`
640
+ 1. exact patch folder structure
641
+ 2. exact file names
642
+ 3. purpose of each file
643
+ 4. auth service mutations to add
644
+ 5. login component state you need
645
+ 6. OTP UI behavior details
646
+ 7. patch apply script behavior
647
+ 8. package.json hooks
648
+ 9. request/response examples
649
+ 10. testing checklist
518
650
 
519
651
  ## Production Notes
520
652
 
@@ -0,0 +1,115 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const rootDir = process.cwd();
5
+
6
+ const patchFiles = [
7
+ {
8
+ source: path.join(
9
+ rootDir,
10
+ "scripts",
11
+ "strapi-admin-2fa-patch",
12
+ "pages",
13
+ "Auth",
14
+ "components",
15
+ "Login.js"
16
+ ),
17
+ target: path.join(
18
+ rootDir,
19
+ "node_modules",
20
+ "@strapi",
21
+ "admin",
22
+ "dist",
23
+ "admin",
24
+ "admin",
25
+ "src",
26
+ "pages",
27
+ "Auth",
28
+ "components",
29
+ "Login.js"
30
+ ),
31
+ },
32
+ {
33
+ source: path.join(
34
+ rootDir,
35
+ "scripts",
36
+ "strapi-admin-2fa-patch",
37
+ "pages",
38
+ "Auth",
39
+ "components",
40
+ "Login.mjs"
41
+ ),
42
+ target: path.join(
43
+ rootDir,
44
+ "node_modules",
45
+ "@strapi",
46
+ "admin",
47
+ "dist",
48
+ "admin",
49
+ "admin",
50
+ "src",
51
+ "pages",
52
+ "Auth",
53
+ "components",
54
+ "Login.mjs"
55
+ ),
56
+ },
57
+ {
58
+ source: path.join(rootDir, "scripts", "strapi-admin-2fa-patch", "services", "auth.js"),
59
+ target: path.join(
60
+ rootDir,
61
+ "node_modules",
62
+ "@strapi",
63
+ "admin",
64
+ "dist",
65
+ "admin",
66
+ "admin",
67
+ "src",
68
+ "services",
69
+ "auth.js"
70
+ ),
71
+ },
72
+ {
73
+ source: path.join(rootDir, "scripts", "strapi-admin-2fa-patch", "services", "auth.mjs"),
74
+ target: path.join(
75
+ rootDir,
76
+ "node_modules",
77
+ "@strapi",
78
+ "admin",
79
+ "dist",
80
+ "admin",
81
+ "admin",
82
+ "src",
83
+ "services",
84
+ "auth.mjs"
85
+ ),
86
+ },
87
+ ];
88
+
89
+ const generatedCacheDirs = [
90
+ path.join(rootDir, "node_modules", ".strapi", "vite"),
91
+ path.join(rootDir, ".strapi", "client"),
92
+ ];
93
+
94
+ const missing = patchFiles.find(
95
+ ({ source, target }) => !fs.existsSync(source) || !fs.existsSync(target)
96
+ );
97
+
98
+ if (missing) {
99
+ console.warn("[admin-2fa-patch] Skipping Strapi admin patch because a source or target file is missing.");
100
+ process.exit(0);
101
+ }
102
+
103
+ for (const { source, target } of patchFiles) {
104
+ fs.copyFileSync(source, target);
105
+ }
106
+
107
+ for (const generatedDir of generatedCacheDirs) {
108
+ if (fs.existsSync(generatedDir)) {
109
+ fs.rmSync(generatedDir, { recursive: true, force: true });
110
+ }
111
+ }
112
+
113
+ console.log(
114
+ "[admin-2fa-patch] Applied Strapi admin OTP login patch and cleared Strapi admin caches."
115
+ );