strapi-identity 0.1.2 → 0.3.0
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 +2 -1
- package/dist/admin/{AdminReset-H453s-DE.mjs → AdminReset-CCGgw-0k.mjs} +2 -2
- package/dist/admin/{AdminReset-C0QibZXW.js → AdminReset-QClQP2Il.js} +2 -2
- package/dist/admin/{ProfileToggle-BFmIWCrN.js → ProfileToggle-H31GHNDA.js} +299 -39
- package/dist/admin/{ProfileToggle-DQeXCx34.mjs → ProfileToggle-gPuH6dGP.mjs} +299 -39
- package/dist/admin/{SettingsPage-OwMik_IK.mjs → SettingsPage-DgPikS3m.mjs} +344 -102
- package/dist/admin/{SettingsPage-DyF7YbsX.js → SettingsPage-DulEjadb.js} +343 -101
- package/dist/admin/{ar-i2eiMZkz.js → ar-BYnI7Tsa.js} +36 -23
- package/dist/admin/{ar-BXaam37U.mjs → ar-DwZqj0qM.mjs} +36 -23
- package/dist/admin/{ca-DZ9DbEcQ.mjs → ca-aKVVc8iQ.mjs} +36 -23
- package/dist/admin/{ca-BVpGzY8r.js → ca-sBRHuaFU.js} +36 -23
- package/dist/admin/{cs-Gok16KLy.mjs → cs--prflMHS.mjs} +36 -23
- package/dist/admin/{cs-_PZVkwt0.js → cs-gU7KP3Lx.js} +36 -23
- package/dist/admin/de-BT25lv_6.mjs +49 -0
- package/dist/admin/de-CrlCAUuf.js +49 -0
- package/dist/admin/{dk-B7EOsAdU.js → dk-BNC3WUzY.js} +36 -23
- package/dist/admin/{dk-CI64Xmli.mjs → dk-Ck3AQYU7.mjs} +36 -23
- package/dist/admin/{en-B_vJwdfS.mjs → en-9qzlpde0.mjs} +36 -23
- package/dist/admin/{en-D4KP9t1Y.js → en-DBj0AD5g.js} +36 -23
- package/dist/admin/{es-CHwF7YK-.js → es-D5Sn41_H.js} +36 -23
- package/dist/admin/{es-CqJcXo4j.mjs → es-lh6XoPb7.mjs} +36 -23
- package/dist/admin/{eu-D-snytN8.mjs → eu-Cuz6ijBX.mjs} +36 -23
- package/dist/admin/{eu-DvdbwE5E.js → eu-Qr3RvDPW.js} +36 -23
- package/dist/admin/fr-C4pmkPYn.js +49 -0
- package/dist/admin/fr-ChlDcZsG.mjs +49 -0
- package/dist/admin/{gu-3wJbbAmw.mjs → gu-B6zyD1bW.mjs} +36 -23
- package/dist/admin/{gu-D2LgVfMp.js → gu-BMZL76zM.js} +36 -23
- package/dist/admin/{he-Bjv7eygt.mjs → he-C5V-qZCX.mjs} +36 -23
- package/dist/admin/{he-DnhYpbvN.js → he-H6iBa45A.js} +36 -23
- package/dist/admin/{hi-DDD2E3A3.js → hi-Be8rPk7I.js} +36 -23
- package/dist/admin/{hi-CNiDezU7.mjs → hi-czhOWo6-.mjs} +36 -23
- package/dist/admin/{hu-C1_YkZHU.js → hu-DKp6kOmc.js} +36 -23
- package/dist/admin/{hu-aLaIWmGw.mjs → hu-NbZ3aiYV.mjs} +36 -23
- package/dist/admin/{id-u3wVE6Rv.js → id-DO0bwFgY.js} +36 -23
- package/dist/admin/{id-C8WRgGm1.mjs → id-NH9PvcR5.mjs} +36 -23
- package/dist/admin/{index-BXZI8nMZ.js → index-C9K2h5UC.js} +65 -12
- package/dist/admin/{index-D45I6rWF.mjs → index-C_4USMnn.mjs} +65 -12
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/{it-CjoRoJj1.mjs → it-Cmrey6tg.mjs} +36 -23
- package/dist/admin/{it-CDw6dG9Z.js → it-Df6-7-M7.js} +36 -23
- package/dist/admin/{ja-CewucIUY.mjs → ja-DH3KMqOL.mjs} +36 -23
- package/dist/admin/{ja-CbMXy2ym.js → ja-HuAq9ZwT.js} +36 -23
- package/dist/admin/{ko-D-kAxDtd.mjs → ko-DPN28RE8.mjs} +36 -23
- package/dist/admin/{ko-BEtJPpfJ.js → ko-S9k8KA8K.js} +36 -23
- package/dist/admin/{ml-0fR2_MmA.js → ml-Bh9GGqcW.js} +36 -23
- package/dist/admin/{ml-DR3AaofF.mjs → ml-MsHNacm6.mjs} +36 -23
- package/dist/admin/{ms-COHLS5e5.mjs → ms-TjHAaxTd.mjs} +36 -23
- package/dist/admin/{ms-DLvuGSlk.js → ms-hO5YeEg4.js} +36 -23
- package/dist/admin/{nl-wj6kn642.js → nl-BF98NBwL.js} +36 -23
- package/dist/admin/{nl-DVtHsM2H.mjs → nl-BLILZU8-.mjs} +36 -23
- package/dist/admin/{no-D_0yjyCy.mjs → no-BtVZ-siy.mjs} +36 -23
- package/dist/admin/{no-DVBgWt8q.js → no-bl1OXlfa.js} +36 -23
- package/dist/admin/{pl-C3GNxjVX.mjs → pl-DCSB6LwZ.mjs} +36 -23
- package/dist/admin/{pl-B2ghisbC.js → pl-DCnOWIDw.js} +36 -23
- package/dist/admin/{pt-BR-BbKV8YoX.mjs → pt-BR-CeLqmj88.mjs} +36 -23
- package/dist/admin/{pt-BR-CfgNaB1-.js → pt-BR-D2_UrxTp.js} +36 -23
- package/dist/admin/{pt-DKe8rRWa.js → pt-DIu8RT_X.js} +36 -23
- package/dist/admin/{pt-z4K3cCjf.mjs → pt-fgjdOyW5.mjs} +36 -23
- package/dist/admin/{ru-C85izLFa.mjs → ru-B_hlpAyP.mjs} +36 -23
- package/dist/admin/{ru-BFSm68HC.js → ru-BccMCf0l.js} +36 -23
- package/dist/admin/{sa-B1XoTTrE.mjs → sa-BtuJ_I1t.mjs} +36 -23
- package/dist/admin/{sa-BOPaqylt.js → sa-D3A-fo85.js} +36 -23
- package/dist/admin/{sk-C48lUPuC.mjs → sk-mmuTFlCK.mjs} +36 -23
- package/dist/admin/{sk-Dd-S1612.js → sk-uSLC6KhO.js} +36 -23
- package/dist/admin/{sv-BLma_kJl.mjs → sv-BlaHc5ax.mjs} +36 -23
- package/dist/admin/{sv-lg64Cw78.js → sv-CuKk5tE-.js} +36 -23
- package/dist/admin/{th-DPbm5NrX.js → th-Bv3NKkYO.js} +36 -23
- package/dist/admin/{th-BJEu5n7q.mjs → th-BwyhFaeE.mjs} +36 -23
- package/dist/admin/{tokenHelpers-jtoRu0q5.js → tokenHelpers-B54WRTn1.js} +4 -10
- package/dist/admin/{tokenHelpers-DagDzpso.mjs → tokenHelpers-CKVyT1sz.mjs} +4 -10
- package/dist/admin/{tr-DkIUODKq.mjs → tr-BLocNlbZ.mjs} +36 -23
- package/dist/admin/{tr-Bm1QZr4v.js → tr-Bmvs-Hx-.js} +36 -23
- package/dist/admin/{uk-FARzIGx4.js → uk-BDxn-EZU.js} +36 -23
- package/dist/admin/{uk-D7ArtSe3.mjs → uk-CyZ10xtq.mjs} +36 -23
- package/dist/admin/{vi-DS0yslPP.mjs → vi-Bx_UJ8up.mjs} +36 -23
- package/dist/admin/{vi-Bi9B6eTY.js → vi-F_mqQCme.js} +36 -23
- package/dist/admin/{zh-DkEx28ZA.js → zh-CFZJPG5N.js} +36 -23
- package/dist/admin/{zh-DwCvIPSz.mjs → zh-CjJdRa3l.mjs} +36 -23
- package/dist/admin/{zh-Hans-BwwKCR6_.js → zh-Hans-4BhSwSQw.js} +36 -23
- package/dist/admin/{zh-Hans-DP2xZyda.mjs → zh-Hans-s7G2GUHU.mjs} +36 -23
- package/dist/server/index.js +487 -50
- package/dist/server/index.mjs +487 -50
- package/package.json +5 -4
- package/dist/admin/de-BuYn1AYX.mjs +0 -26
- package/dist/admin/de-GItli7en.js +0 -26
- package/dist/admin/fr-Bt6sS5GX.mjs +0 -26
- package/dist/admin/fr-CbCW6hVD.js +0 -26
|
@@ -2,10 +2,10 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState, useEffect } from "react";
|
|
3
3
|
import { Modal, Flex, Typography, Grid, Button, TextInput, Box, Field, Toggle } from "@strapi/design-system";
|
|
4
4
|
import styled from "styled-components";
|
|
5
|
-
import { g as getTranslation, I as InputOTP, a as InputOTPGroup, b as InputOTPSlot, c as InputOTPSeparator } from "./index-
|
|
5
|
+
import { g as getTranslation, I as InputOTP, a as InputOTPGroup, b as InputOTPSlot, c as InputOTPSeparator } from "./index-C_4USMnn.mjs";
|
|
6
6
|
import { QRCodeCanvas } from "qrcode.react";
|
|
7
7
|
import { useIntl } from "react-intl";
|
|
8
|
-
import { g as getToken } from "./tokenHelpers-
|
|
8
|
+
import { g as getToken } from "./tokenHelpers-CKVyT1sz.mjs";
|
|
9
9
|
function ConfirmModal({
|
|
10
10
|
open,
|
|
11
11
|
onOpenChange,
|
|
@@ -120,11 +120,155 @@ function RemoveModal({ open, onOpenChange, onSubmit }) {
|
|
|
120
120
|
] })
|
|
121
121
|
] }) }) });
|
|
122
122
|
}
|
|
123
|
+
function EmailOTPModal({
|
|
124
|
+
mode,
|
|
125
|
+
open,
|
|
126
|
+
email,
|
|
127
|
+
onOpenChange,
|
|
128
|
+
onSuccess
|
|
129
|
+
}) {
|
|
130
|
+
const { formatMessage } = useIntl();
|
|
131
|
+
const [step, setStep] = useState("send");
|
|
132
|
+
const [loading, setLoading] = useState(false);
|
|
133
|
+
const [error, setError] = useState(null);
|
|
134
|
+
const handleOpenChange = (nextOpen) => {
|
|
135
|
+
if (!nextOpen) {
|
|
136
|
+
setStep("send");
|
|
137
|
+
setError(null);
|
|
138
|
+
}
|
|
139
|
+
onOpenChange(nextOpen);
|
|
140
|
+
};
|
|
141
|
+
const handleSend = async () => {
|
|
142
|
+
const token = getToken();
|
|
143
|
+
setLoading(true);
|
|
144
|
+
setError(null);
|
|
145
|
+
try {
|
|
146
|
+
const endpoint = mode === "setup" ? "/strapi-identity/enable-email" : "/strapi-identity/disable-email/request";
|
|
147
|
+
const response = await fetch(endpoint, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: { "Content-Type": "application/json", authorization: `Bearer ${token}` }
|
|
150
|
+
});
|
|
151
|
+
const body = await response.json();
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
throw new Error(body.error || "Failed to send verification email");
|
|
154
|
+
}
|
|
155
|
+
setStep("confirm");
|
|
156
|
+
} catch (err) {
|
|
157
|
+
setError(err.message);
|
|
158
|
+
} finally {
|
|
159
|
+
setLoading(false);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const handleConfirm = async (e) => {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
const token = getToken();
|
|
165
|
+
const formData = new FormData(e.target);
|
|
166
|
+
const code = formData.get("otp");
|
|
167
|
+
setLoading(true);
|
|
168
|
+
setError(null);
|
|
169
|
+
try {
|
|
170
|
+
const endpoint = mode === "setup" ? "/strapi-identity/setup-email" : "/strapi-identity/disable";
|
|
171
|
+
const response = await fetch(endpoint, {
|
|
172
|
+
method: "POST",
|
|
173
|
+
headers: { "Content-Type": "application/json", authorization: `Bearer ${token}` },
|
|
174
|
+
body: JSON.stringify({ code })
|
|
175
|
+
});
|
|
176
|
+
const body = await response.json();
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
throw new Error(body.error || "Invalid or expired code");
|
|
179
|
+
}
|
|
180
|
+
handleOpenChange(false);
|
|
181
|
+
onSuccess();
|
|
182
|
+
} catch (err) {
|
|
183
|
+
setError(err.message);
|
|
184
|
+
} finally {
|
|
185
|
+
setLoading(false);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
const title = mode === "setup" ? formatMessage({
|
|
189
|
+
id: getTranslation("email_otp.setup_title"),
|
|
190
|
+
defaultMessage: "Enable Email OTP"
|
|
191
|
+
}) : formatMessage({
|
|
192
|
+
id: getTranslation("email_otp.disable_title"),
|
|
193
|
+
defaultMessage: "Disable Email OTP"
|
|
194
|
+
});
|
|
195
|
+
return /* @__PURE__ */ jsx(Modal.Root, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
|
|
196
|
+
/* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: title }) }),
|
|
197
|
+
step === "send" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
198
|
+
/* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", gap: 4, marginTop: 4, marginBottom: 4, children: [
|
|
199
|
+
/* @__PURE__ */ jsx(Typography, { textAlign: "center", children: mode === "setup" ? formatMessage(
|
|
200
|
+
{
|
|
201
|
+
id: getTranslation("email_otp.setup_description"),
|
|
202
|
+
defaultMessage: "We'll send a 6-digit verification code to {email}. Enter it to enable Email OTP."
|
|
203
|
+
},
|
|
204
|
+
{ email: /* @__PURE__ */ jsx("strong", { children: email }) }
|
|
205
|
+
) : formatMessage(
|
|
206
|
+
{
|
|
207
|
+
id: getTranslation("email_otp.disable_description"),
|
|
208
|
+
defaultMessage: "We'll send a 6-digit verification code to {email}. Enter it to disable Email OTP."
|
|
209
|
+
},
|
|
210
|
+
{ email: /* @__PURE__ */ jsx("strong", { children: email }) }
|
|
211
|
+
) }),
|
|
212
|
+
error ? /* @__PURE__ */ jsx(Typography, { role: "alert", textColor: "danger600", textAlign: "center", children: error }) : null
|
|
213
|
+
] }) }),
|
|
214
|
+
/* @__PURE__ */ jsxs(Modal.Footer, { children: [
|
|
215
|
+
/* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({ id: "app.components.Button.cancel", defaultMessage: "Cancel" }) }) }),
|
|
216
|
+
/* @__PURE__ */ jsx(Button, { onClick: handleSend, loading, children: formatMessage({
|
|
217
|
+
id: getTranslation("email_otp.send_code"),
|
|
218
|
+
defaultMessage: "Send verification email"
|
|
219
|
+
}) })
|
|
220
|
+
] })
|
|
221
|
+
] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleConfirm, children: [
|
|
222
|
+
/* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", gap: 4, marginTop: 4, marginBottom: 4, children: [
|
|
223
|
+
/* @__PURE__ */ jsx(Typography, { textAlign: "center", children: formatMessage(
|
|
224
|
+
{
|
|
225
|
+
id: getTranslation("email_otp.confirm_description"),
|
|
226
|
+
defaultMessage: "Enter the 6-digit code sent to {email}."
|
|
227
|
+
},
|
|
228
|
+
{ email: /* @__PURE__ */ jsx("strong", { children: email }) }
|
|
229
|
+
) }),
|
|
230
|
+
/* @__PURE__ */ jsxs(InputOTP, { maxLength: 6, name: "otp", id: "otp", autoFocus: true, children: [
|
|
231
|
+
/* @__PURE__ */ jsxs(InputOTPGroup, { children: [
|
|
232
|
+
/* @__PURE__ */ jsx(InputOTPSlot, { index: 0 }),
|
|
233
|
+
/* @__PURE__ */ jsx(InputOTPSlot, { index: 1 }),
|
|
234
|
+
/* @__PURE__ */ jsx(InputOTPSlot, { index: 2 })
|
|
235
|
+
] }),
|
|
236
|
+
/* @__PURE__ */ jsx(InputOTPSeparator, {}),
|
|
237
|
+
/* @__PURE__ */ jsxs(InputOTPGroup, { children: [
|
|
238
|
+
/* @__PURE__ */ jsx(InputOTPSlot, { index: 3 }),
|
|
239
|
+
/* @__PURE__ */ jsx(InputOTPSlot, { index: 4 }),
|
|
240
|
+
/* @__PURE__ */ jsx(InputOTPSlot, { index: 5 })
|
|
241
|
+
] })
|
|
242
|
+
] }),
|
|
243
|
+
error ? /* @__PURE__ */ jsx(Typography, { role: "alert", textColor: "danger600", textAlign: "center", children: error }) : null,
|
|
244
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", type: "button", onClick: () => handleSend(), children: formatMessage({
|
|
245
|
+
id: getTranslation("email_otp.resend_code"),
|
|
246
|
+
defaultMessage: "Resend code"
|
|
247
|
+
}) })
|
|
248
|
+
] }) }),
|
|
249
|
+
/* @__PURE__ */ jsxs(Modal.Footer, { children: [
|
|
250
|
+
/* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: formatMessage({
|
|
251
|
+
id: "app.components.Button.cancel",
|
|
252
|
+
defaultMessage: "Cancel"
|
|
253
|
+
}) }) }),
|
|
254
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: formatMessage({
|
|
255
|
+
id: "app.components.Button.confirm",
|
|
256
|
+
defaultMessage: "Confirm"
|
|
257
|
+
}) })
|
|
258
|
+
] })
|
|
259
|
+
] })
|
|
260
|
+
] }) });
|
|
261
|
+
}
|
|
123
262
|
const ProfileToggle = () => {
|
|
124
263
|
const { formatMessage } = useIntl();
|
|
125
264
|
const [enabled, setEnabled] = useState(null);
|
|
265
|
+
const [mfaType, setMfaType] = useState(null);
|
|
126
266
|
const [mfaEnabled, setMfaEnabled] = useState(false);
|
|
267
|
+
const [emailConfigured, setEmailConfigured] = useState(false);
|
|
268
|
+
const [userEmail, setUserEmail] = useState("");
|
|
127
269
|
const [disableDialogOpen, setDisableDialogOpen] = useState(false);
|
|
270
|
+
const [emailSetupOpen, setEmailSetupOpen] = useState(false);
|
|
271
|
+
const [emailDisableOpen, setEmailDisableOpen] = useState(false);
|
|
128
272
|
const [modalOpen, setModalOpen] = useState(false);
|
|
129
273
|
const [uri, setUri] = useState(null);
|
|
130
274
|
const [secret, setSecret] = useState(null);
|
|
@@ -136,6 +280,9 @@ const ProfileToggle = () => {
|
|
|
136
280
|
setDisableDialogOpen(true);
|
|
137
281
|
return;
|
|
138
282
|
}
|
|
283
|
+
if (enable && enabled === "full" && mfaType === "email") {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
139
286
|
try {
|
|
140
287
|
const response = await fetch("/strapi-identity/enable", {
|
|
141
288
|
method: "POST",
|
|
@@ -154,11 +301,25 @@ const ProfileToggle = () => {
|
|
|
154
301
|
setUri(data?.uri || null);
|
|
155
302
|
setSecret(data?.secret || null);
|
|
156
303
|
setEnabled(enable ? "temp" : null);
|
|
304
|
+
if (enable) setMfaType("totp");
|
|
157
305
|
} catch (error) {
|
|
158
306
|
console.error(error);
|
|
159
307
|
setEnabled(null);
|
|
160
308
|
}
|
|
161
309
|
};
|
|
310
|
+
const handleEmailToggle = ({ target }) => {
|
|
311
|
+
const enable = target?.checked || false;
|
|
312
|
+
if (!enable && enabled === "full" && mfaType === "email") {
|
|
313
|
+
setEmailDisableOpen(true);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (enable && enabled === "full") {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (enable) {
|
|
320
|
+
setEmailSetupOpen(true);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
162
323
|
const handleConfirm = async (e) => {
|
|
163
324
|
e.preventDefault();
|
|
164
325
|
const form = e.target;
|
|
@@ -183,6 +344,7 @@ const ProfileToggle = () => {
|
|
|
183
344
|
setUri(null);
|
|
184
345
|
setSecret(null);
|
|
185
346
|
setEnabled("full");
|
|
347
|
+
setMfaType("totp");
|
|
186
348
|
} catch (error) {
|
|
187
349
|
console.error(error);
|
|
188
350
|
}
|
|
@@ -214,6 +376,7 @@ const ProfileToggle = () => {
|
|
|
214
376
|
setUri(null);
|
|
215
377
|
setSecret(null);
|
|
216
378
|
setEnabled(null);
|
|
379
|
+
setMfaType(null);
|
|
217
380
|
} catch (error) {
|
|
218
381
|
console.error(error);
|
|
219
382
|
}
|
|
@@ -223,7 +386,7 @@ const ProfileToggle = () => {
|
|
|
223
386
|
(async () => {
|
|
224
387
|
const token = getToken();
|
|
225
388
|
try {
|
|
226
|
-
const [
|
|
389
|
+
const [statusRes, enabledRes, meRes] = await Promise.all([
|
|
227
390
|
fetch("/strapi-identity/status", {
|
|
228
391
|
method: "GET",
|
|
229
392
|
headers: { "Content-Type": "application/json", authorization: `Bearer ${token}` },
|
|
@@ -233,18 +396,37 @@ const ProfileToggle = () => {
|
|
|
233
396
|
method: "GET",
|
|
234
397
|
headers: { "Content-Type": "application/json", authorization: `Bearer ${token}` },
|
|
235
398
|
signal: ac.signal
|
|
399
|
+
}),
|
|
400
|
+
fetch("/admin/users/me", {
|
|
401
|
+
method: "GET",
|
|
402
|
+
headers: { "Content-Type": "application/json", authorization: `Bearer ${token}` },
|
|
403
|
+
signal: ac.signal
|
|
236
404
|
})
|
|
237
405
|
]);
|
|
238
|
-
const statusBody = await
|
|
239
|
-
const enabledBody = await
|
|
240
|
-
if (!
|
|
241
|
-
throw new Error(`${
|
|
406
|
+
const statusBody = await statusRes.json();
|
|
407
|
+
const enabledBody = await enabledRes.json();
|
|
408
|
+
if (!statusRes.ok) {
|
|
409
|
+
throw new Error(`${statusRes.status} - ${statusBody.error || "Failed to get MFA status"}`);
|
|
242
410
|
}
|
|
243
|
-
if (!
|
|
244
|
-
throw new Error(`${
|
|
411
|
+
if (!enabledRes.ok) {
|
|
412
|
+
throw new Error(`${enabledRes.status} - ${enabledBody.error || "Failed to get MFA config"}`);
|
|
245
413
|
}
|
|
246
414
|
setMfaEnabled(enabledBody.data);
|
|
247
415
|
setEnabled(statusBody.data?.status || null);
|
|
416
|
+
setMfaType(statusBody.data?.type || null);
|
|
417
|
+
if (meRes.ok) {
|
|
418
|
+
const meBody = await meRes.json();
|
|
419
|
+
setUserEmail(meBody.data?.email || "");
|
|
420
|
+
const configRes = await fetch("/strapi-identity/config", {
|
|
421
|
+
method: "GET",
|
|
422
|
+
headers: { "Content-Type": "application/json", authorization: `Bearer ${token}` },
|
|
423
|
+
signal: ac.signal
|
|
424
|
+
});
|
|
425
|
+
if (configRes.ok) {
|
|
426
|
+
const configBody = await configRes.json();
|
|
427
|
+
setEmailConfigured(!!configBody.data?.email_enabled);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
248
430
|
} catch (error) {
|
|
249
431
|
if (error.name === "AbortError") return;
|
|
250
432
|
console.error("Failed to fetch MFA status:", error);
|
|
@@ -253,6 +435,10 @@ const ProfileToggle = () => {
|
|
|
253
435
|
return () => ac.abort();
|
|
254
436
|
}, []);
|
|
255
437
|
if (!mfaEnabled) return null;
|
|
438
|
+
const totpChecked = enabled !== null && mfaType === "totp";
|
|
439
|
+
const emailChecked = enabled !== null && mfaType === "email";
|
|
440
|
+
const totpDisabled = enabled !== null && mfaType === "email";
|
|
441
|
+
const emailDisabled = enabled !== null && mfaType === "totp";
|
|
256
442
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
257
443
|
/* @__PURE__ */ jsx(
|
|
258
444
|
Box,
|
|
@@ -275,36 +461,78 @@ const ProfileToggle = () => {
|
|
|
275
461
|
defaultMessage: "Add an additional layer of security to your account."
|
|
276
462
|
}) })
|
|
277
463
|
] }),
|
|
278
|
-
/* @__PURE__ */
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
id: getTranslation("profile.
|
|
287
|
-
defaultMessage: "
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
464
|
+
/* @__PURE__ */ jsxs(Grid.Root, { tag: "div", gap: 5, children: [
|
|
465
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, alignItems: "stretch", children: /* @__PURE__ */ jsxs(
|
|
466
|
+
Field.Root,
|
|
467
|
+
{
|
|
468
|
+
width: "100%",
|
|
469
|
+
name: "two-factor-authentication",
|
|
470
|
+
id: "two-factor-authentication",
|
|
471
|
+
hint: totpDisabled ? formatMessage({
|
|
472
|
+
id: getTranslation("profile.totp_disabled_hint"),
|
|
473
|
+
defaultMessage: "Disable Email OTP first to enable the authenticator app."
|
|
474
|
+
}) : void 0,
|
|
475
|
+
children: [
|
|
476
|
+
/* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
|
|
477
|
+
id: getTranslation("profile.toggle_label"),
|
|
478
|
+
defaultMessage: "Enable Two-Factor Authentication"
|
|
479
|
+
}) }),
|
|
480
|
+
/* @__PURE__ */ jsx(
|
|
481
|
+
Toggle,
|
|
482
|
+
{
|
|
483
|
+
offLabel: formatMessage({
|
|
484
|
+
id: "app.components.ToggleCheckbox.off-label",
|
|
485
|
+
defaultMessage: "False"
|
|
486
|
+
}),
|
|
487
|
+
onLabel: formatMessage({
|
|
488
|
+
id: "app.components.ToggleCheckbox.on-label",
|
|
489
|
+
defaultMessage: "True"
|
|
490
|
+
}),
|
|
491
|
+
checked: totpChecked,
|
|
492
|
+
onChange: handleToggle,
|
|
493
|
+
disabled: totpDisabled
|
|
494
|
+
}
|
|
495
|
+
),
|
|
496
|
+
/* @__PURE__ */ jsx(Field.Hint, {})
|
|
497
|
+
]
|
|
498
|
+
}
|
|
499
|
+
) }),
|
|
500
|
+
emailConfigured && /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, alignItems: "stretch", children: /* @__PURE__ */ jsxs(
|
|
501
|
+
Field.Root,
|
|
502
|
+
{
|
|
503
|
+
width: "100%",
|
|
504
|
+
name: "email-otp",
|
|
505
|
+
id: "email-otp",
|
|
506
|
+
hint: emailDisabled ? formatMessage({
|
|
507
|
+
id: getTranslation("profile.email_otp_disabled_hint"),
|
|
508
|
+
defaultMessage: "Disable the authenticator app first to enable Email OTP."
|
|
509
|
+
}) : void 0,
|
|
510
|
+
children: [
|
|
511
|
+
/* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
|
|
512
|
+
id: getTranslation("profile.email_otp_label"),
|
|
513
|
+
defaultMessage: "Enable Email OTP"
|
|
514
|
+
}) }),
|
|
515
|
+
/* @__PURE__ */ jsx(
|
|
516
|
+
Toggle,
|
|
517
|
+
{
|
|
518
|
+
offLabel: formatMessage({
|
|
519
|
+
id: "app.components.ToggleCheckbox.off-label",
|
|
520
|
+
defaultMessage: "False"
|
|
521
|
+
}),
|
|
522
|
+
onLabel: formatMessage({
|
|
523
|
+
id: "app.components.ToggleCheckbox.on-label",
|
|
524
|
+
defaultMessage: "True"
|
|
525
|
+
}),
|
|
526
|
+
checked: emailChecked,
|
|
527
|
+
onChange: handleEmailToggle,
|
|
528
|
+
disabled: emailDisabled
|
|
529
|
+
}
|
|
530
|
+
),
|
|
531
|
+
/* @__PURE__ */ jsx(Field.Hint, {})
|
|
532
|
+
]
|
|
533
|
+
}
|
|
534
|
+
) })
|
|
535
|
+
] })
|
|
308
536
|
] })
|
|
309
537
|
}
|
|
310
538
|
),
|
|
@@ -326,6 +554,38 @@ const ProfileToggle = () => {
|
|
|
326
554
|
onOpenChange: setDisableDialogOpen,
|
|
327
555
|
onSubmit: handleDisable
|
|
328
556
|
}
|
|
557
|
+
),
|
|
558
|
+
/* @__PURE__ */ jsx(
|
|
559
|
+
EmailOTPModal,
|
|
560
|
+
{
|
|
561
|
+
mode: "setup",
|
|
562
|
+
open: emailSetupOpen,
|
|
563
|
+
email: userEmail,
|
|
564
|
+
onOpenChange: (open) => {
|
|
565
|
+
if (!open) setEmailSetupOpen(false);
|
|
566
|
+
},
|
|
567
|
+
onSuccess: () => {
|
|
568
|
+
setEnabled("full");
|
|
569
|
+
setMfaType("email");
|
|
570
|
+
setEmailSetupOpen(false);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
),
|
|
574
|
+
/* @__PURE__ */ jsx(
|
|
575
|
+
EmailOTPModal,
|
|
576
|
+
{
|
|
577
|
+
mode: "disable",
|
|
578
|
+
open: emailDisableOpen,
|
|
579
|
+
email: userEmail,
|
|
580
|
+
onOpenChange: (open) => {
|
|
581
|
+
if (!open) setEmailDisableOpen(false);
|
|
582
|
+
},
|
|
583
|
+
onSuccess: () => {
|
|
584
|
+
setEnabled(null);
|
|
585
|
+
setMfaType(null);
|
|
586
|
+
setEmailDisableOpen(false);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
329
589
|
)
|
|
330
590
|
] });
|
|
331
591
|
};
|