@vivinkv28/strapi-2fa-admin-plugin 0.1.13 → 0.1.15

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
@@ -10,6 +10,20 @@ This package adds the backend flow for:
10
10
  - rate limiting for login, verify, and resend
11
11
  - final Strapi admin session creation only after OTP verification
12
12
 
13
+ ## UI Preview
14
+
15
+ ### Login Screen
16
+
17
+ ![Admin 2FA login screen](https://raw.githubusercontent.com/vivinkv6/strapi-admin-2fa-plugin/master/public/login.png)
18
+
19
+ The first step collects the admin email and password before starting the OTP challenge flow.
20
+
21
+ ### OTP Screen
22
+
23
+ ![Admin 2FA OTP screen](https://raw.githubusercontent.com/vivinkv6/strapi-admin-2fa-plugin/master/public/otp.png)
24
+
25
+ The second step shows the OTP input, resend action, and inline validation feedback during verification.
26
+
13
27
  ## Important
14
28
 
15
29
  This package does not automatically replace the default Strapi admin login UI.
@@ -77,13 +77,23 @@ const getService = () => strapi.plugin("admin-2fa").service("auth");
77
77
  const APPLICATION_ERROR_STATUS = {
78
78
  ApplicationError: 400,
79
79
  ValidationError: 400,
80
- UnauthorizedError: 401,
81
- ForbiddenError: 403,
80
+ UnauthorizedError: 400,
81
+ ForbiddenError: 400,
82
82
  NotFoundError: 404,
83
83
  PayloadTooLargeError: 413,
84
84
  RateLimitError: 429,
85
85
  NotImplementedError: 501
86
86
  };
87
+ const deriveApplicationErrorStatus = (error2) => {
88
+ if (typeof error2?.status === "number" && error2.status >= 400 && error2.status < 500) {
89
+ return error2.status;
90
+ }
91
+ const message = typeof error2?.message === "string" ? error2.message.toLowerCase() : "";
92
+ if (message.includes("session not found") || message.includes("please log in again") || message.includes("otp expired") || message.includes("expired otp")) {
93
+ return 409;
94
+ }
95
+ return APPLICATION_ERROR_STATUS[error2?.name] ?? 400;
96
+ };
87
97
  const setRefreshCookie = (ctx, refreshToken, cookieOptions) => {
88
98
  ctx.cookies.set(sessionAuth$1.REFRESH_COOKIE_NAME, refreshToken, cookieOptions);
89
99
  };
@@ -98,7 +108,7 @@ const getClientIp = (ctx) => {
98
108
  return String(ctx.request.ip ?? ctx.ip ?? "").trim();
99
109
  };
100
110
  const sendApplicationError = (ctx, error2) => {
101
- const derivedStatus = typeof error2?.status === "number" && error2.status >= 400 && error2.status < 500 ? error2.status : APPLICATION_ERROR_STATUS[error2?.name] ?? 400;
111
+ const derivedStatus = deriveApplicationErrorStatus(error2);
102
112
  ctx.status = derivedStatus;
103
113
  ctx.body = {
104
114
  data: null,
@@ -63,13 +63,23 @@ const getService = () => strapi.plugin("admin-2fa").service("auth");
63
63
  const APPLICATION_ERROR_STATUS = {
64
64
  ApplicationError: 400,
65
65
  ValidationError: 400,
66
- UnauthorizedError: 401,
67
- ForbiddenError: 403,
66
+ UnauthorizedError: 400,
67
+ ForbiddenError: 400,
68
68
  NotFoundError: 404,
69
69
  PayloadTooLargeError: 413,
70
70
  RateLimitError: 429,
71
71
  NotImplementedError: 501
72
72
  };
73
+ const deriveApplicationErrorStatus = (error2) => {
74
+ if (typeof error2?.status === "number" && error2.status >= 400 && error2.status < 500) {
75
+ return error2.status;
76
+ }
77
+ const message = typeof error2?.message === "string" ? error2.message.toLowerCase() : "";
78
+ if (message.includes("session not found") || message.includes("please log in again") || message.includes("otp expired") || message.includes("expired otp")) {
79
+ return 409;
80
+ }
81
+ return APPLICATION_ERROR_STATUS[error2?.name] ?? 400;
82
+ };
73
83
  const setRefreshCookie = (ctx, refreshToken, cookieOptions) => {
74
84
  ctx.cookies.set(sessionAuth$1.REFRESH_COOKIE_NAME, refreshToken, cookieOptions);
75
85
  };
@@ -84,7 +94,7 @@ const getClientIp = (ctx) => {
84
94
  return String(ctx.request.ip ?? ctx.ip ?? "").trim();
85
95
  };
86
96
  const sendApplicationError = (ctx, error2) => {
87
- const derivedStatus = typeof error2?.status === "number" && error2.status >= 400 && error2.status < 500 ? error2.status : APPLICATION_ERROR_STATUS[error2?.name] ?? 400;
97
+ const derivedStatus = deriveApplicationErrorStatus(error2);
88
98
  ctx.status = derivedStatus;
89
99
  ctx.body = {
90
100
  data: null,
@@ -226,17 +226,18 @@ const OtpField = ()=>{
226
226
  }, index))
227
227
  })
228
228
  }),
229
- errors.code ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
230
- paddingTop: 3,
231
- children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
232
- id: "otp-code-error",
233
- variant: "pi",
234
- textColor: "danger600",
235
- children: errors.code
236
- })
237
- }) : null
238
- ]
239
- });
229
+ errors.code ? /*#__PURE__*/ jsxRuntime.jsx(designSystem.Box, {
230
+ paddingTop: 3,
231
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
232
+ id: "otp-code-error",
233
+ variant: "pi",
234
+ textColor: "danger600",
235
+ textAlign: "center",
236
+ children: errors.code
237
+ })
238
+ }) : null
239
+ ]
240
+ });
240
241
  };
241
242
  const Login = ({ children })=>{
242
243
  const [apiError, setApiError] = React__namespace.useState();
@@ -204,17 +204,18 @@ const OtpField = ()=>{
204
204
  }, index))
205
205
  })
206
206
  }),
207
- errors.code ? /*#__PURE__*/ jsx(Box, {
208
- paddingTop: 3,
209
- children: /*#__PURE__*/ jsx(Typography, {
210
- id: "otp-code-error",
211
- variant: "pi",
212
- textColor: "danger600",
213
- children: errors.code
214
- })
215
- }) : null
216
- ]
217
- });
207
+ errors.code ? /*#__PURE__*/ jsx(Box, {
208
+ paddingTop: 3,
209
+ children: /*#__PURE__*/ jsx(Typography, {
210
+ id: "otp-code-error",
211
+ variant: "pi",
212
+ textColor: "danger600",
213
+ textAlign: "center",
214
+ children: errors.code
215
+ })
216
+ }) : null
217
+ ]
218
+ });
218
219
  };
219
220
  const Login = ({ children })=>{
220
221
  const [apiError, setApiError] = React.useState();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vivinkv28/strapi-2fa-admin-plugin",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Reusable Strapi admin 2FA plugin",
5
5
  "type": "commonjs",
6
6
  "keywords": [