strapi-identity 0.2.0 → 0.4.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.
Files changed (84) hide show
  1. package/README.md +5 -2
  2. package/dist/admin/{AdminReset-CqHhVBS_.js → AdminReset-BiWQDTRv.js} +3 -4
  3. package/dist/admin/{AdminReset-B-WGECOX.mjs → AdminReset-DOmsyqwQ.mjs} +1 -2
  4. package/dist/admin/{ProfileToggle-BRYjt5Lu.js → ProfileToggle-BUqs_hxZ.js} +8 -218
  5. package/dist/admin/{ProfileToggle-BCtCsOvj.mjs → ProfileToggle-k0d-caPC.mjs} +2 -210
  6. package/dist/admin/{SettingsPage-DAxGIv_E.js → SettingsPage-DVVkN1xw.js} +3 -6
  7. package/dist/admin/{SettingsPage-7Ytl01jH.mjs → SettingsPage-Dm_llkYv.mjs} +1 -4
  8. package/dist/admin/{ar-DwZqj0qM.mjs → ar-B4yBU4m7.mjs} +7 -0
  9. package/dist/admin/{ar-BYnI7Tsa.js → ar-wjkCnUTi.js} +7 -0
  10. package/dist/admin/{ca-sBRHuaFU.js → ca-BHz1SCoK.js} +7 -0
  11. package/dist/admin/{ca-aKVVc8iQ.mjs → ca-DLE8GCgI.mjs} +7 -0
  12. package/dist/admin/{cs--prflMHS.mjs → cs-3kxvJ5GN.mjs} +7 -0
  13. package/dist/admin/{cs-gU7KP3Lx.js → cs-Echs10hb.js} +7 -0
  14. package/dist/admin/{de-BT25lv_6.mjs → de-BTldhzPN.mjs} +7 -0
  15. package/dist/admin/{de-CrlCAUuf.js → de-p5oK0g4T.js} +7 -0
  16. package/dist/admin/{dk-Ck3AQYU7.mjs → dk-CCNCrmIK.mjs} +7 -0
  17. package/dist/admin/{dk-BNC3WUzY.js → dk-Z6BhrTeh.js} +7 -0
  18. package/dist/admin/{en-9qzlpde0.mjs → en-1anycEwN.mjs} +7 -0
  19. package/dist/admin/{en-DBj0AD5g.js → en-CLnZaoOA.js} +7 -0
  20. package/dist/admin/{es-D5Sn41_H.js → es-C-4sXZ_R.js} +7 -0
  21. package/dist/admin/{es-lh6XoPb7.mjs → es-DMANTUCL.mjs} +7 -0
  22. package/dist/admin/{eu-Cuz6ijBX.mjs → eu-BITeCOIE.mjs} +7 -0
  23. package/dist/admin/{eu-Qr3RvDPW.js → eu-CHWReAeU.js} +7 -0
  24. package/dist/admin/{fr-ChlDcZsG.mjs → fr-C81x3RP3.mjs} +7 -0
  25. package/dist/admin/{fr-C4pmkPYn.js → fr-DH-kRY27.js} +7 -0
  26. package/dist/admin/{gu-BMZL76zM.js → gu-CDocz32V.js} +7 -0
  27. package/dist/admin/{gu-B6zyD1bW.mjs → gu-DDR6O_Dp.mjs} +7 -0
  28. package/dist/admin/{he-H6iBa45A.js → he-CKFM5685.js} +7 -0
  29. package/dist/admin/{he-C5V-qZCX.mjs → he-TPBr5x3o.mjs} +7 -0
  30. package/dist/admin/{hi-Be8rPk7I.js → hi-DqJ11ApQ.js} +7 -0
  31. package/dist/admin/{hi-czhOWo6-.mjs → hi-Pwt1EMiO.mjs} +7 -0
  32. package/dist/admin/{hu-NbZ3aiYV.mjs → hu-CTKkJzwl.mjs} +7 -0
  33. package/dist/admin/{hu-DKp6kOmc.js → hu-DFGcSNo0.js} +7 -0
  34. package/dist/admin/{id-NH9PvcR5.mjs → id-Bq3jNpUL.mjs} +7 -0
  35. package/dist/admin/{id-DO0bwFgY.js → id-ChXHR8aw.js} +7 -0
  36. package/dist/admin/{index-D03zlFnm.js → index-B9P8S4CX.js} +428 -9
  37. package/dist/admin/{index-BfC6z9N5.mjs → index-DpIJdETG.mjs} +439 -20
  38. package/dist/admin/index.js +1 -1
  39. package/dist/admin/index.mjs +1 -1
  40. package/dist/admin/{it-Cmrey6tg.mjs → it-CgRQVAPr.mjs} +7 -0
  41. package/dist/admin/{it-Df6-7-M7.js → it-r0rbu0x0.js} +7 -0
  42. package/dist/admin/{ja-HuAq9ZwT.js → ja-27vNq46V.js} +7 -0
  43. package/dist/admin/{ja-DH3KMqOL.mjs → ja-2xaH1Qf2.mjs} +7 -0
  44. package/dist/admin/{ko-DPN28RE8.mjs → ko-CGgmfI4W.mjs} +7 -0
  45. package/dist/admin/{ko-S9k8KA8K.js → ko-Ci0l5h1b.js} +7 -0
  46. package/dist/admin/{ml-Bh9GGqcW.js → ml-Augll2or.js} +7 -0
  47. package/dist/admin/{ml-MsHNacm6.mjs → ml-aXIja392.mjs} +7 -0
  48. package/dist/admin/{ms-hO5YeEg4.js → ms-B8pBYl9n.js} +7 -0
  49. package/dist/admin/{ms-TjHAaxTd.mjs → ms-CLGR4CKx.mjs} +7 -0
  50. package/dist/admin/{nl-BLILZU8-.mjs → nl-CGFOqn_t.mjs} +7 -0
  51. package/dist/admin/{nl-BF98NBwL.js → nl-Cqn_nYD8.js} +7 -0
  52. package/dist/admin/{no-BtVZ-siy.mjs → no-BIbR3s2A.mjs} +7 -0
  53. package/dist/admin/{no-bl1OXlfa.js → no-DPF_xI-b.js} +7 -0
  54. package/dist/admin/{pl-DCSB6LwZ.mjs → pl-BBEIjPVT.mjs} +7 -0
  55. package/dist/admin/{pl-DCnOWIDw.js → pl-BjIa9TiI.js} +7 -0
  56. package/dist/admin/{pt-BR-D2_UrxTp.js → pt-BR-BbPay13q.js} +7 -0
  57. package/dist/admin/{pt-BR-CeLqmj88.mjs → pt-BR-C0S_4PYn.mjs} +7 -0
  58. package/dist/admin/{pt-DIu8RT_X.js → pt-DDhcHCz6.js} +7 -0
  59. package/dist/admin/{pt-fgjdOyW5.mjs → pt-DwDWDT_T.mjs} +7 -0
  60. package/dist/admin/{ru-BccMCf0l.js → ru-BzQ0SoFG.js} +7 -0
  61. package/dist/admin/{ru-B_hlpAyP.mjs → ru-DuxM9hFK.mjs} +7 -0
  62. package/dist/admin/{sa-D3A-fo85.js → sa-Cwsmxq_x.js} +7 -0
  63. package/dist/admin/{sa-BtuJ_I1t.mjs → sa-DfqNZDgh.mjs} +7 -0
  64. package/dist/admin/{sk-mmuTFlCK.mjs → sk-BcYzeG4F.mjs} +7 -0
  65. package/dist/admin/{sk-uSLC6KhO.js → sk-Coqlt4Kq.js} +7 -0
  66. package/dist/admin/{sv-CuKk5tE-.js → sv-9zwaCIfo.js} +7 -0
  67. package/dist/admin/{sv-BlaHc5ax.mjs → sv-CkoFHi6o.mjs} +7 -0
  68. package/dist/admin/{th-BwyhFaeE.mjs → th-C4FBlfLA.mjs} +7 -0
  69. package/dist/admin/{th-Bv3NKkYO.js → th-CFkjhGd6.js} +7 -0
  70. package/dist/admin/{tr-Bmvs-Hx-.js → tr-D0g7vqL1.js} +7 -0
  71. package/dist/admin/{tr-BLocNlbZ.mjs → tr-Djsa55Fh.mjs} +7 -0
  72. package/dist/admin/{uk-CyZ10xtq.mjs → uk-BDoDjhO2.mjs} +7 -0
  73. package/dist/admin/{uk-BDxn-EZU.js → uk-Dw1MGmom.js} +7 -0
  74. package/dist/admin/{vi-Bx_UJ8up.mjs → vi-D977KjlZ.mjs} +7 -0
  75. package/dist/admin/{vi-F_mqQCme.js → vi-DNZKFaOu.js} +7 -0
  76. package/dist/admin/{zh-CFZJPG5N.js → zh-C2aozMiZ.js} +7 -0
  77. package/dist/admin/{zh-CjJdRa3l.mjs → zh-CKYKCaVd.mjs} +7 -0
  78. package/dist/admin/{zh-Hans-s7G2GUHU.mjs → zh-Hans-DNgRcEC-.mjs} +7 -0
  79. package/dist/admin/{zh-Hans-4BhSwSQw.js → zh-Hans-ellQkyo7.js} +7 -0
  80. package/dist/server/index.js +88 -16
  81. package/dist/server/index.mjs +88 -16
  82. package/package.json +3 -3
  83. package/dist/admin/tokenHelpers-DagDzpso.mjs +0 -22
  84. package/dist/admin/tokenHelpers-jtoRu0q5.js +0 -21
@@ -7,6 +7,7 @@ const inputOtp = require("input-otp");
7
7
  const designSystem = require("@strapi/design-system");
8
8
  const admin = require("@strapi/strapi/admin");
9
9
  const reactIntl = require("react-intl");
10
+ const qrcode_react = require("qrcode.react");
10
11
  const strapiAdminPortal = require("strapi-admin-portal");
11
12
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
12
13
  function _interopNamespace(e) {
@@ -1526,6 +1527,422 @@ const VerifyPage = ({ fallbackIcon }) => {
1526
1527
  )
1527
1528
  ] });
1528
1529
  };
1530
+ function ConfirmModal({
1531
+ open,
1532
+ onOpenChange,
1533
+ onSubmit,
1534
+ qrCodeUri,
1535
+ secret,
1536
+ passcodes
1537
+ }) {
1538
+ const { formatMessage } = reactIntl.useIntl();
1539
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit, children: [
1540
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: formatMessage({
1541
+ id: getTranslation("profile.setup"),
1542
+ defaultMessage: "Set up Two-Factor Authentication"
1543
+ }) }) }),
1544
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: passcodes ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 4, marginTop: 4, marginBottom: 4, children: [
1545
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textAlign: "center", children: formatMessage({
1546
+ id: getTranslation("profile.recovery_codes"),
1547
+ defaultMessage: "Please save the following recovery codes in a safe place. Each code can only be used once to access your account if you lose access to your authenticator app."
1548
+ }) }),
1549
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Root, { gap: 4, marginTop: 4, marginBottom: 4, children: passcodes.map((code) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: code }) }, code)) }),
1550
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textAlign: "center", children: formatMessage({
1551
+ id: getTranslation("profile.recovery_codes_warning"),
1552
+ defaultMessage: "If you lose both your authenticator app and your recovery codes, you will need to contact an administrator to regain access to your account."
1553
+ }) })
1554
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", children: [
1555
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 4, marginTop: 4, marginBottom: 4, children: [
1556
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
1557
+ id: getTranslation("profile.scan_qr"),
1558
+ defaultMessage: "You will need an authenticator app to scan the QR code below."
1559
+ }) }),
1560
+ qrCodeUri && /* @__PURE__ */ jsxRuntime.jsx(qrcode_react.QRCodeCanvas, { value: qrCodeUri, size: 256 }),
1561
+ secret && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: secret || "" })
1562
+ ] }),
1563
+ /* @__PURE__ */ jsxRuntime.jsx(Rule, {}),
1564
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 4, marginTop: 4, marginBottom: 4, children: [
1565
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
1566
+ id: getTranslation("profile.enter_otp"),
1567
+ defaultMessage: "Enter the 6-digit code from your authenticator app to confirm."
1568
+ }) }),
1569
+ /* @__PURE__ */ jsxRuntime.jsxs(InputOTP, { maxLength: 6, name: "otp", id: "otp", autoFocus: true, children: [
1570
+ /* @__PURE__ */ jsxRuntime.jsxs(InputOTPGroup, { children: [
1571
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 0 }),
1572
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 1 }),
1573
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 2 })
1574
+ ] }),
1575
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSeparator, {}),
1576
+ /* @__PURE__ */ jsxRuntime.jsxs(InputOTPGroup, { children: [
1577
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 3 }),
1578
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 4 }),
1579
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 5 })
1580
+ ] })
1581
+ ] })
1582
+ ] })
1583
+ ] }) }),
1584
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
1585
+ passcodes && /* @__PURE__ */ jsxRuntime.jsx("span", {}),
1586
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Close, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: passcodes ? void 0 : "tertiary", children: passcodes ? formatMessage({ id: "global.close", defaultMessage: "Close" }) : formatMessage({ id: "app.components.Button.cancel", defaultMessage: "Cancel" }) }) }),
1587
+ !passcodes && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { type: "submit", children: formatMessage({ id: "app.components.Button.confirm", defaultMessage: "Confirm" }) })
1588
+ ] })
1589
+ ] }) }) });
1590
+ }
1591
+ const Rule = styled__default.default.hr`
1592
+ height: 1px;
1593
+ border: 0;
1594
+ background-color: #e5e5e5;
1595
+ `;
1596
+ const getCookieValue = (name) => {
1597
+ const cookieArray = document.cookie.split(";");
1598
+ return cookieArray.reduce((result, cookie) => {
1599
+ const [key, value] = cookie.split("=").map((item) => item.trim());
1600
+ return key === name ? decodeURIComponent(value) : result;
1601
+ }, null);
1602
+ };
1603
+ const getToken = () => {
1604
+ const fromLocalStorage = localStorage.getItem("jwtToken");
1605
+ if (fromLocalStorage) return JSON.parse(fromLocalStorage);
1606
+ const fromCookie = getCookieValue("jwtToken");
1607
+ return fromCookie ?? null;
1608
+ };
1609
+ function EmailOTPModal({
1610
+ mode,
1611
+ open,
1612
+ email,
1613
+ onOpenChange,
1614
+ onSuccess
1615
+ }) {
1616
+ const { formatMessage } = reactIntl.useIntl();
1617
+ const [step, setStep] = React.useState("send");
1618
+ const [loading, setLoading] = React.useState(false);
1619
+ const [error, setError] = React.useState(null);
1620
+ const handleOpenChange = (nextOpen) => {
1621
+ if (!nextOpen) {
1622
+ setStep("send");
1623
+ setError(null);
1624
+ }
1625
+ onOpenChange(nextOpen);
1626
+ };
1627
+ const handleSend = async () => {
1628
+ const token = getToken();
1629
+ setLoading(true);
1630
+ setError(null);
1631
+ try {
1632
+ const endpoint = mode === "setup" ? "/strapi-identity/enable-email" : "/strapi-identity/disable-email/request";
1633
+ const response = await fetch(endpoint, {
1634
+ method: "POST",
1635
+ headers: { "Content-Type": "application/json", authorization: `Bearer ${token}` }
1636
+ });
1637
+ const body = await response.json();
1638
+ if (!response.ok) {
1639
+ throw new Error(body.error || "Failed to send verification email");
1640
+ }
1641
+ setStep("confirm");
1642
+ } catch (err) {
1643
+ setError(err.message);
1644
+ } finally {
1645
+ setLoading(false);
1646
+ }
1647
+ };
1648
+ const handleConfirm = async (e) => {
1649
+ e.preventDefault();
1650
+ const token = getToken();
1651
+ const formData = new FormData(e.target);
1652
+ const code = formData.get("otp");
1653
+ setLoading(true);
1654
+ setError(null);
1655
+ try {
1656
+ const endpoint = mode === "setup" ? "/strapi-identity/setup-email" : "/strapi-identity/disable";
1657
+ const response = await fetch(endpoint, {
1658
+ method: "POST",
1659
+ headers: { "Content-Type": "application/json", authorization: `Bearer ${token}` },
1660
+ body: JSON.stringify({ code })
1661
+ });
1662
+ const body = await response.json();
1663
+ if (!response.ok) {
1664
+ throw new Error(body.error || "Invalid or expired code");
1665
+ }
1666
+ handleOpenChange(false);
1667
+ onSuccess();
1668
+ } catch (err) {
1669
+ setError(err.message);
1670
+ } finally {
1671
+ setLoading(false);
1672
+ }
1673
+ };
1674
+ const title = mode === "setup" ? formatMessage({
1675
+ id: getTranslation("email_otp.setup_title"),
1676
+ defaultMessage: "Enable Email OTP"
1677
+ }) : formatMessage({
1678
+ id: getTranslation("email_otp.disable_title"),
1679
+ defaultMessage: "Disable Email OTP"
1680
+ });
1681
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
1682
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: title }) }),
1683
+ step === "send" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1684
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 4, marginTop: 4, marginBottom: 4, children: [
1685
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textAlign: "center", children: mode === "setup" ? formatMessage(
1686
+ {
1687
+ id: getTranslation("email_otp.setup_description"),
1688
+ defaultMessage: "We'll send a 6-digit verification code to {email}. Enter it to enable Email OTP."
1689
+ },
1690
+ { email: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: email }) }
1691
+ ) : formatMessage(
1692
+ {
1693
+ id: getTranslation("email_otp.disable_description"),
1694
+ defaultMessage: "We'll send a 6-digit verification code to {email}. Enter it to disable Email OTP."
1695
+ },
1696
+ { email: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: email }) }
1697
+ ) }),
1698
+ error ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { role: "alert", textColor: "danger600", textAlign: "center", children: error }) : null
1699
+ ] }) }),
1700
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
1701
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Close, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: formatMessage({ id: "app.components.Button.cancel", defaultMessage: "Cancel" }) }) }),
1702
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleSend, loading, children: formatMessage({
1703
+ id: getTranslation("email_otp.send_code"),
1704
+ defaultMessage: "Send verification email"
1705
+ }) })
1706
+ ] })
1707
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleConfirm, children: [
1708
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 4, marginTop: 4, marginBottom: 4, children: [
1709
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textAlign: "center", children: formatMessage(
1710
+ {
1711
+ id: getTranslation("email_otp.confirm_description"),
1712
+ defaultMessage: "Enter the 6-digit code sent to {email}."
1713
+ },
1714
+ { email: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: email }) }
1715
+ ) }),
1716
+ /* @__PURE__ */ jsxRuntime.jsxs(InputOTP, { maxLength: 6, name: "otp", id: "otp", autoFocus: true, children: [
1717
+ /* @__PURE__ */ jsxRuntime.jsxs(InputOTPGroup, { children: [
1718
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 0 }),
1719
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 1 }),
1720
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 2 })
1721
+ ] }),
1722
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSeparator, {}),
1723
+ /* @__PURE__ */ jsxRuntime.jsxs(InputOTPGroup, { children: [
1724
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 3 }),
1725
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 4 }),
1726
+ /* @__PURE__ */ jsxRuntime.jsx(InputOTPSlot, { index: 5 })
1727
+ ] })
1728
+ ] }),
1729
+ error ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { role: "alert", textColor: "danger600", textAlign: "center", children: error }) : null,
1730
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", type: "button", onClick: () => handleSend(), children: formatMessage({
1731
+ id: getTranslation("email_otp.resend_code"),
1732
+ defaultMessage: "Resend code"
1733
+ }) })
1734
+ ] }) }),
1735
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Footer, { children: [
1736
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Close, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", children: formatMessage({
1737
+ id: "app.components.Button.cancel",
1738
+ defaultMessage: "Cancel"
1739
+ }) }) }),
1740
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { type: "submit", loading, children: formatMessage({
1741
+ id: "app.components.Button.confirm",
1742
+ defaultMessage: "Confirm"
1743
+ }) })
1744
+ ] })
1745
+ ] })
1746
+ ] }) });
1747
+ }
1748
+ const InjectEnforced = async (app) => {
1749
+ const _router = app.router;
1750
+ while (!_router.router) await new Promise((resolve) => setTimeout(resolve, 10));
1751
+ _router.router.routes?.[0].children?.push({
1752
+ path: "strapi-identity/enforced",
1753
+ element: /* @__PURE__ */ jsxRuntime.jsx(EnforcedPage, {})
1754
+ });
1755
+ };
1756
+ const EnforcedPage = () => {
1757
+ const { formatMessage } = reactIntl.useIntl();
1758
+ const [loading, setLoading] = React.useState(true);
1759
+ const [emailConfigured, setEmailConfigured] = React.useState(false);
1760
+ const [userEmail, setUserEmail] = React.useState("");
1761
+ const [totpModalOpen, setTotpModalOpen] = React.useState(false);
1762
+ const [uri, setUri] = React.useState(null);
1763
+ const [secret, setSecret] = React.useState(null);
1764
+ const [passcodes, setPasscodes] = React.useState(null);
1765
+ const [emailModalOpen, setEmailModalOpen] = React.useState(false);
1766
+ React.useEffect(() => {
1767
+ const ac = new AbortController();
1768
+ (async () => {
1769
+ const token = getToken();
1770
+ try {
1771
+ const [statusRes, configRes, meRes] = await Promise.all([
1772
+ fetch("/strapi-identity/status", {
1773
+ headers: { authorization: `Bearer ${token}` },
1774
+ signal: ac.signal
1775
+ }),
1776
+ fetch("/strapi-identity/config", {
1777
+ headers: { authorization: `Bearer ${token}` },
1778
+ signal: ac.signal
1779
+ }),
1780
+ fetch("/admin/users/me", {
1781
+ headers: { authorization: `Bearer ${token}` },
1782
+ signal: ac.signal
1783
+ })
1784
+ ]);
1785
+ if (statusRes.ok) {
1786
+ const statusBody = await statusRes.json();
1787
+ if (statusBody.data?.status === "full") {
1788
+ window.location.replace("/admin");
1789
+ return;
1790
+ }
1791
+ }
1792
+ if (configRes.ok) {
1793
+ const configBody = await configRes.json();
1794
+ setEmailConfigured(!!configBody.data?.email_enabled);
1795
+ }
1796
+ if (meRes.ok) {
1797
+ const meBody = await meRes.json();
1798
+ setUserEmail(meBody.data?.email || "");
1799
+ }
1800
+ } catch (error) {
1801
+ if (error.name === "AbortError") return;
1802
+ console.error("Failed to check MFA status:", error);
1803
+ } finally {
1804
+ setLoading(false);
1805
+ }
1806
+ })();
1807
+ return () => ac.abort();
1808
+ }, []);
1809
+ const handleEnableTOTP = async () => {
1810
+ const token = getToken();
1811
+ try {
1812
+ const response = await fetch("/strapi-identity/enable", {
1813
+ method: "POST",
1814
+ headers: { "Content-Type": "application/json", authorization: `Bearer ${token}` },
1815
+ body: JSON.stringify({ enable: true })
1816
+ });
1817
+ const body = await response.json();
1818
+ if (!response.ok) {
1819
+ throw new Error(body.error || "Failed to initiate TOTP setup");
1820
+ }
1821
+ setUri(body.data?.uri || null);
1822
+ setSecret(body.data?.secret || null);
1823
+ setTotpModalOpen(true);
1824
+ } catch (error) {
1825
+ console.error(error);
1826
+ }
1827
+ };
1828
+ const handleConfirmTOTP = async (e) => {
1829
+ e.preventDefault();
1830
+ const formData = new FormData(e.target);
1831
+ const code = formData.get("otp");
1832
+ const token = getToken();
1833
+ try {
1834
+ const response = await fetch("/strapi-identity/setup", {
1835
+ method: "POST",
1836
+ headers: { "Content-Type": "application/json", authorization: `Bearer ${token}` },
1837
+ body: JSON.stringify({ code })
1838
+ });
1839
+ const body = await response.json();
1840
+ if (!response.ok) {
1841
+ throw new Error(body.error || "Failed to set up MFA");
1842
+ }
1843
+ if (body.data?.recoveryCodes) {
1844
+ setPasscodes(body.data.recoveryCodes);
1845
+ } else {
1846
+ window.location.replace("/admin");
1847
+ }
1848
+ setUri(null);
1849
+ setSecret(null);
1850
+ } catch (error) {
1851
+ console.error(error);
1852
+ }
1853
+ };
1854
+ const handleCloseTOTP = () => {
1855
+ if (passcodes) {
1856
+ window.location.replace("/admin");
1857
+ return;
1858
+ }
1859
+ setTotpModalOpen(false);
1860
+ setUri(null);
1861
+ setSecret(null);
1862
+ setPasscodes(null);
1863
+ };
1864
+ if (loading) return null;
1865
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1866
+ /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Title, { children: formatMessage({
1867
+ id: getTranslation("enforced.page_title"),
1868
+ defaultMessage: "MFA Required"
1869
+ }) }),
1870
+ /* @__PURE__ */ jsxRuntime.jsxs(admin.Page.Main, { children: [
1871
+ /* @__PURE__ */ jsxRuntime.jsx(
1872
+ admin.Layouts.Header,
1873
+ {
1874
+ title: formatMessage({
1875
+ id: getTranslation("enforced.title"),
1876
+ defaultMessage: "Multi-Factor Authentication Required"
1877
+ }),
1878
+ subtitle: formatMessage({
1879
+ id: getTranslation("enforced.subtitle"),
1880
+ defaultMessage: "Your administrator requires MFA to be configured before you can access the CMS."
1881
+ })
1882
+ }
1883
+ ),
1884
+ /* @__PURE__ */ jsxRuntime.jsx(admin.Layouts.Content, { children: /* @__PURE__ */ jsxRuntime.jsx(
1885
+ designSystem.Box,
1886
+ {
1887
+ background: "neutral0",
1888
+ hasRadius: true,
1889
+ shadow: "tableShadow",
1890
+ paddingTop: 6,
1891
+ paddingBottom: 6,
1892
+ paddingLeft: 7,
1893
+ paddingRight: 7,
1894
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
1895
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 1, children: [
1896
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", tag: "h2", children: formatMessage({
1897
+ id: getTranslation("enforced.card_title"),
1898
+ defaultMessage: "Set Up Two-Factor Authentication"
1899
+ }) }),
1900
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { children: formatMessage({
1901
+ id: getTranslation("enforced.description"),
1902
+ defaultMessage: "To continue using the CMS, you must enable at least one Multi-Factor Authentication method. Choose an option below to get started."
1903
+ }) })
1904
+ ] }),
1905
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, wrap: "wrap", children: [
1906
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "L", onClick: handleEnableTOTP, children: formatMessage({
1907
+ id: getTranslation("enforced.setup_totp"),
1908
+ defaultMessage: "Set up Authenticator App"
1909
+ }) }),
1910
+ emailConfigured && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { size: "L", variant: "secondary", onClick: () => setEmailModalOpen(true), children: formatMessage({
1911
+ id: getTranslation("enforced.setup_email"),
1912
+ defaultMessage: "Set up Email OTP"
1913
+ }) })
1914
+ ] })
1915
+ ] })
1916
+ }
1917
+ ) })
1918
+ ] }),
1919
+ /* @__PURE__ */ jsxRuntime.jsx(
1920
+ ConfirmModal,
1921
+ {
1922
+ open: totpModalOpen,
1923
+ onOpenChange: handleCloseTOTP,
1924
+ qrCodeUri: uri,
1925
+ secret,
1926
+ passcodes,
1927
+ onSubmit: handleConfirmTOTP
1928
+ }
1929
+ ),
1930
+ /* @__PURE__ */ jsxRuntime.jsx(
1931
+ EmailOTPModal,
1932
+ {
1933
+ mode: "setup",
1934
+ open: emailModalOpen,
1935
+ email: userEmail,
1936
+ onOpenChange: (open) => {
1937
+ if (!open) setEmailModalOpen(false);
1938
+ },
1939
+ onSuccess: () => {
1940
+ window.location.replace("/admin");
1941
+ }
1942
+ }
1943
+ )
1944
+ ] });
1945
+ };
1529
1946
  const plugin = {
1530
1947
  register(app) {
1531
1948
  app.registerPlugin({
@@ -1540,12 +1957,9 @@ const plugin = {
1540
1957
  defaultMessage: "Strapi Identity Settings"
1541
1958
  },
1542
1959
  id: "strapi-identity-settings",
1543
- to: `/settings/${PLUGIN_ID}`,
1544
- Component: async () => Promise.resolve().then(() => require("./SettingsPage-DAxGIv_E.js")),
1545
- permissions: [
1546
- { action: "plugin::strapi-identity.settings.read" },
1547
- { action: "plugin::strapi-identity.settings.read" }
1548
- ]
1960
+ to: `/${PLUGIN_ID}`,
1961
+ Component: () => Promise.resolve().then(() => require("./SettingsPage-DVVkN1xw.js")),
1962
+ permissions: [{ action: "plugin::strapi-identity.settings.update" }]
1549
1963
  });
1550
1964
  app.addMiddlewares([mfaRedirect]);
1551
1965
  const injections = strapiAdminPortal.initialiseInjections(app);
@@ -1553,21 +1967,23 @@ const plugin = {
1553
1967
  id: "profile-toggle",
1554
1968
  route: "/admin/me",
1555
1969
  selector: '#main-content form[method="put"] > :nth-child(2) > div > div > div:nth-child(2)',
1556
- Component: async () => Promise.resolve().then(() => require("./ProfileToggle-BRYjt5Lu.js"))
1970
+ Component: () => Promise.resolve().then(() => require("./ProfileToggle-BUqs_hxZ.js"))
1557
1971
  });
1558
1972
  injections.registerRoute({
1559
1973
  id: "admin-reset",
1560
1974
  route: "/admin/settings/users/:id",
1561
1975
  selector: '#main-content form[method="put"] > :nth-child(2) > div > div:nth-child(2)',
1562
- Component: async () => Promise.resolve().then(() => require("./AdminReset-CqHhVBS_.js"))
1976
+ permissions: [{ action: "plugin::strapi-identity.settings.update" }],
1977
+ Component: () => Promise.resolve().then(() => require("./AdminReset-BiWQDTRv.js"))
1563
1978
  });
1564
1979
  InjectVerify(app);
1980
+ InjectEnforced(app);
1565
1981
  },
1566
1982
  registerTrads({ locales }) {
1567
1983
  return Promise.all(
1568
1984
  locales.map(async (locale) => {
1569
1985
  try {
1570
- const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-BYnI7Tsa.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-sBRHuaFU.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-gU7KP3Lx.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-CrlCAUuf.js")), "./translations/dk.json": () => Promise.resolve().then(() => require("./dk-BNC3WUzY.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-DBj0AD5g.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-D5Sn41_H.js")), "./translations/eu.json": () => Promise.resolve().then(() => require("./eu-Qr3RvDPW.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-C4pmkPYn.js")), "./translations/gu.json": () => Promise.resolve().then(() => require("./gu-BMZL76zM.js")), "./translations/he.json": () => Promise.resolve().then(() => require("./he-H6iBa45A.js")), "./translations/hi.json": () => Promise.resolve().then(() => require("./hi-Be8rPk7I.js")), "./translations/hu.json": () => Promise.resolve().then(() => require("./hu-DKp6kOmc.js")), "./translations/id.json": () => Promise.resolve().then(() => require("./id-DO0bwFgY.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-Df6-7-M7.js")), "./translations/ja.json": () => Promise.resolve().then(() => require("./ja-HuAq9ZwT.js")), "./translations/ko.json": () => Promise.resolve().then(() => require("./ko-S9k8KA8K.js")), "./translations/ml.json": () => Promise.resolve().then(() => require("./ml-Bh9GGqcW.js")), "./translations/ms.json": () => Promise.resolve().then(() => require("./ms-hO5YeEg4.js")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-BF98NBwL.js")), "./translations/no.json": () => Promise.resolve().then(() => require("./no-bl1OXlfa.js")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-DCnOWIDw.js")), "./translations/pt-BR.json": () => Promise.resolve().then(() => require("./pt-BR-D2_UrxTp.js")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-DIu8RT_X.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("./ru-BccMCf0l.js")), "./translations/sa.json": () => Promise.resolve().then(() => require("./sa-D3A-fo85.js")), "./translations/sk.json": () => Promise.resolve().then(() => require("./sk-uSLC6KhO.js")), "./translations/sv.json": () => Promise.resolve().then(() => require("./sv-CuKk5tE-.js")), "./translations/th.json": () => Promise.resolve().then(() => require("./th-Bv3NKkYO.js")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-Bmvs-Hx-.js")), "./translations/uk.json": () => Promise.resolve().then(() => require("./uk-BDxn-EZU.js")), "./translations/vi.json": () => Promise.resolve().then(() => require("./vi-F_mqQCme.js")), "./translations/zh-Hans.json": () => Promise.resolve().then(() => require("./zh-Hans-4BhSwSQw.js")), "./translations/zh.json": () => Promise.resolve().then(() => require("./zh-CFZJPG5N.js")) }), `./translations/${locale}.json`, 3);
1986
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-wjkCnUTi.js")), "./translations/ca.json": () => Promise.resolve().then(() => require("./ca-BHz1SCoK.js")), "./translations/cs.json": () => Promise.resolve().then(() => require("./cs-Echs10hb.js")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-p5oK0g4T.js")), "./translations/dk.json": () => Promise.resolve().then(() => require("./dk-Z6BhrTeh.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-CLnZaoOA.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-C-4sXZ_R.js")), "./translations/eu.json": () => Promise.resolve().then(() => require("./eu-CHWReAeU.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-DH-kRY27.js")), "./translations/gu.json": () => Promise.resolve().then(() => require("./gu-CDocz32V.js")), "./translations/he.json": () => Promise.resolve().then(() => require("./he-CKFM5685.js")), "./translations/hi.json": () => Promise.resolve().then(() => require("./hi-DqJ11ApQ.js")), "./translations/hu.json": () => Promise.resolve().then(() => require("./hu-DFGcSNo0.js")), "./translations/id.json": () => Promise.resolve().then(() => require("./id-ChXHR8aw.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-r0rbu0x0.js")), "./translations/ja.json": () => Promise.resolve().then(() => require("./ja-27vNq46V.js")), "./translations/ko.json": () => Promise.resolve().then(() => require("./ko-Ci0l5h1b.js")), "./translations/ml.json": () => Promise.resolve().then(() => require("./ml-Augll2or.js")), "./translations/ms.json": () => Promise.resolve().then(() => require("./ms-B8pBYl9n.js")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-Cqn_nYD8.js")), "./translations/no.json": () => Promise.resolve().then(() => require("./no-DPF_xI-b.js")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-BjIa9TiI.js")), "./translations/pt-BR.json": () => Promise.resolve().then(() => require("./pt-BR-BbPay13q.js")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-DDhcHCz6.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("./ru-BzQ0SoFG.js")), "./translations/sa.json": () => Promise.resolve().then(() => require("./sa-Cwsmxq_x.js")), "./translations/sk.json": () => Promise.resolve().then(() => require("./sk-Coqlt4Kq.js")), "./translations/sv.json": () => Promise.resolve().then(() => require("./sv-9zwaCIfo.js")), "./translations/th.json": () => Promise.resolve().then(() => require("./th-CFkjhGd6.js")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-D0g7vqL1.js")), "./translations/uk.json": () => Promise.resolve().then(() => require("./uk-Dw1MGmom.js")), "./translations/vi.json": () => Promise.resolve().then(() => require("./vi-DNZKFaOu.js")), "./translations/zh-Hans.json": () => Promise.resolve().then(() => require("./zh-Hans-ellQkyo7.js")), "./translations/zh.json": () => Promise.resolve().then(() => require("./zh-C2aozMiZ.js")) }), `./translations/${locale}.json`, 3);
1571
1987
  const newData = {};
1572
1988
  const keys = Object.keys(data);
1573
1989
  for (const key of keys) {
@@ -1590,9 +2006,12 @@ const mfaRedirect = () => {
1590
2006
  return next(action);
1591
2007
  };
1592
2008
  };
2009
+ exports.ConfirmModal = ConfirmModal;
2010
+ exports.EmailOTPModal = EmailOTPModal;
1593
2011
  exports.InputOTP = InputOTP;
1594
2012
  exports.InputOTPGroup = InputOTPGroup;
1595
2013
  exports.InputOTPSeparator = InputOTPSeparator;
1596
2014
  exports.InputOTPSlot = InputOTPSlot;
2015
+ exports.getToken = getToken;
1597
2016
  exports.getTranslation = getTranslation;
1598
2017
  exports.plugin = plugin;