@vivinkv28/strapi-2fa-admin-plugin 0.1.7 → 0.1.9

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.
@@ -1,246 +0,0 @@
1
- # Integration Guide
2
-
3
- This guide explains how to use `@vivinkv28/strapi-2fa-admin-plugin` inside an existing Strapi 5 project.
4
-
5
- ## What The Plugin Does
6
-
7
- The plugin provides the backend endpoints and auth logic for an OTP-based admin login flow.
8
-
9
- It does not replace the Strapi admin login UI by itself.
10
-
11
- Your host project must provide a login integration layer that:
12
-
13
- 1. collects admin email and password
14
- 2. calls the plugin login endpoint
15
- 3. shows an OTP input screen
16
- 4. calls the plugin verify endpoint
17
- 5. optionally allows OTP resend
18
-
19
- ## Endpoints
20
-
21
- The plugin registers these routes:
22
-
23
- - `POST /api/admin-2fa/login`
24
- - `POST /api/admin-2fa/verify`
25
- - `POST /api/admin-2fa/resend`
26
-
27
- ## Install And Enable
28
-
29
- ### Published npm package
30
-
31
- ```bash
32
- npm install @vivinkv28/strapi-2fa-admin-plugin
33
- ```
34
-
35
- ### Local external plugin
36
-
37
- In your Strapi app:
38
-
39
- ```ts
40
- // config/plugins.ts
41
- import type { Core } from "@strapi/strapi";
42
-
43
- const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Plugin => ({
44
- "admin-2fa": {
45
- enabled: true,
46
- resolve: "../strapi-2fa-admin-plugin",
47
- config: {
48
- otpDigits: env.int("ADMIN_OTP_DIGITS", 6),
49
- otpTtlSeconds: env.int("ADMIN_OTP_TTL_SECONDS", 300),
50
- maxAttempts: env.int("ADMIN_OTP_MAX_ATTEMPTS", 5),
51
- maxResends: env.int("ADMIN_OTP_MAX_RESENDS", 3),
52
- rateLimitWindowSeconds: env.int("ADMIN_OTP_RATE_LIMIT_WINDOW_SECONDS", 900),
53
- loginIpLimit: env.int("ADMIN_OTP_LOGIN_IP_LIMIT", 10),
54
- loginEmailLimit: env.int("ADMIN_OTP_LOGIN_EMAIL_LIMIT", 5),
55
- verifyIpLimit: env.int("ADMIN_OTP_VERIFY_IP_LIMIT", 20),
56
- verifyEmailLimit: env.int("ADMIN_OTP_VERIFY_EMAIL_LIMIT", 10),
57
- resendIpLimit: env.int("ADMIN_OTP_RESEND_IP_LIMIT", 10),
58
- resendEmailLimit: env.int("ADMIN_OTP_RESEND_EMAIL_LIMIT", 5),
59
- debugTimings: env.bool(
60
- "ADMIN_OTP_DEBUG_TIMINGS",
61
- env("NODE_ENV", "development") !== "production"
62
- ),
63
- },
64
- },
65
- });
66
-
67
- export default config;
68
- ```
69
-
70
- ## Required Host App Setup
71
-
72
- ### Email provider
73
-
74
- The plugin calls `strapi.plugin("email").service("email").send(...)`.
75
-
76
- Your Strapi project must configure an email provider or OTP delivery will fail.
77
-
78
- ### Proxy and HTTPS
79
-
80
- If your project runs behind a reverse proxy, configure `config/server.ts` correctly so Strapi recognizes secure requests and admin cookies are set with the right options.
81
-
82
- Typical example:
83
-
84
- ```ts
85
- import type { Core } from "@strapi/strapi";
86
-
87
- const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Server => ({
88
- host: env("HOST", "0.0.0.0"),
89
- port: env.int("PORT", 1337),
90
- url: env("URL", "http://localhost:1337"),
91
- proxy: env.bool("IS_PROXIED", env("NODE_ENV", "development") === "production"),
92
- app: {
93
- keys: env.array("APP_KEYS"),
94
- },
95
- });
96
-
97
- export default config;
98
- ```
99
-
100
- ## Request Flow
101
-
102
- ### 1. Start login
103
-
104
- Send admin email and password to:
105
-
106
- ```http
107
- POST /api/admin-2fa/login
108
- Content-Type: application/json
109
- ```
110
-
111
- Example payload:
112
-
113
- ```json
114
- {
115
- "email": "admin@example.com",
116
- "password": "super-secret-password",
117
- "rememberMe": true,
118
- "deviceId": "browser-device-id"
119
- }
120
- ```
121
-
122
- Example success response:
123
-
124
- ```json
125
- {
126
- "data": {
127
- "challengeId": "0d3af6fd-b351-4d1e-bb81-2a8201d8a0f4",
128
- "expiresAt": "2026-04-05T18:30:00.000Z",
129
- "maskedEmail": "admin@example.com",
130
- "rememberMe": true
131
- }
132
- }
133
- ```
134
-
135
- What happens here:
136
-
137
- - Strapi validates the admin credentials
138
- - the plugin creates an OTP challenge
139
- - the plugin emails the OTP
140
- - no final admin session is created yet
141
-
142
- ### 2. Verify OTP
143
-
144
- Send the OTP code to:
145
-
146
- ```http
147
- POST /api/admin-2fa/verify
148
- Content-Type: application/json
149
- ```
150
-
151
- Example payload:
152
-
153
- ```json
154
- {
155
- "challengeId": "0d3af6fd-b351-4d1e-bb81-2a8201d8a0f4",
156
- "code": "123456"
157
- }
158
- ```
159
-
160
- Example success response:
161
-
162
- ```json
163
- {
164
- "data": {
165
- "token": "<access-token>",
166
- "accessToken": "<access-token>",
167
- "user": {
168
- "id": 1,
169
- "email": "admin@example.com"
170
- }
171
- }
172
- }
173
- ```
174
-
175
- What happens here:
176
-
177
- - the plugin reloads the OTP challenge
178
- - the submitted code is hashed and compared
179
- - the real Strapi admin session is created only after OTP verification
180
- - the refresh cookie is set by the plugin controller
181
-
182
- ### 3. Resend OTP
183
-
184
- Send:
185
-
186
- ```http
187
- POST /api/admin-2fa/resend
188
- Content-Type: application/json
189
- ```
190
-
191
- Example payload:
192
-
193
- ```json
194
- {
195
- "challengeId": "0d3af6fd-b351-4d1e-bb81-2a8201d8a0f4"
196
- }
197
- ```
198
-
199
- This generates a new OTP for the same challenge and extends the expiry.
200
-
201
- ## Error Cases To Handle In The UI
202
-
203
- Your login integration should handle these cases cleanly:
204
-
205
- - invalid email or password
206
- - OTP session not found
207
- - OTP expired
208
- - invalid OTP code
209
- - too many authentication attempts
210
- - maximum resend attempts exceeded
211
-
212
- These are returned as normal Strapi error responses, so the frontend should surface the message to the admin user in a safe and friendly way.
213
-
214
- ## Recommended Frontend Behavior
215
-
216
- - Keep login and OTP entry as two separate UI states.
217
- - Store `challengeId` in memory for the current login attempt.
218
- - Do not create your own session after password validation; wait for `/verify`.
219
- - After successful OTP verification, treat the returned user/token exactly like a successful admin login.
220
- - If resend fails due to limits or expiry, restart the login flow.
221
-
222
- ## Environment Variables
223
-
224
- Recommended defaults:
225
-
226
- ```env
227
- ADMIN_OTP_DIGITS=6
228
- ADMIN_OTP_TTL_SECONDS=300
229
- ADMIN_OTP_MAX_ATTEMPTS=5
230
- ADMIN_OTP_MAX_RESENDS=3
231
- ADMIN_OTP_RATE_LIMIT_WINDOW_SECONDS=900
232
- ADMIN_OTP_LOGIN_IP_LIMIT=10
233
- ADMIN_OTP_LOGIN_EMAIL_LIMIT=5
234
- ADMIN_OTP_VERIFY_IP_LIMIT=20
235
- ADMIN_OTP_VERIFY_EMAIL_LIMIT=10
236
- ADMIN_OTP_RESEND_IP_LIMIT=10
237
- ADMIN_OTP_RESEND_EMAIL_LIMIT=5
238
- ADMIN_OTP_DEBUG_TIMINGS=false
239
- ```
240
-
241
- ## Production Notes
242
-
243
- - Email OTP is better than password-only login, but weaker than TOTP or WebAuthn.
244
- - If the admin mailbox is compromised, the second factor can still be bypassed.
245
- - Use app passwords or provider-managed SMTP credentials for OTP delivery.
246
- - Make sure your host project sets `URL` and proxy settings correctly in production.