@forge-connect/react 1.0.1 → 1.0.3
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/dist/index.cjs +1639 -1593
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +1655 -1609
- package/dist/index.js.map +1 -1
- package/package.json +1 -5
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/provider.tsx
|
|
2
|
-
import { useState as useState16, useCallback as
|
|
2
|
+
import { useState as useState16, useCallback as useCallback9, useEffect as useEffect12, useRef as useRef9, useMemo as useMemo3 } from "react";
|
|
3
3
|
|
|
4
4
|
// src/context.ts
|
|
5
5
|
import { createContext } from "react";
|
|
@@ -812,1677 +812,1689 @@ function EmailOtpForm() {
|
|
|
812
812
|
|
|
813
813
|
// src/components/tabs/wallet-connect.tsx
|
|
814
814
|
import { useState as useState3, useMemo, useRef as useRef3, useCallback } from "react";
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
815
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
816
|
+
var MOBILE_WALLETS = [
|
|
817
|
+
{
|
|
818
|
+
name: "Phantom",
|
|
819
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTA4IiBoZWlnaHQ9IjEwOCIgdmlld0JveD0iMCAwIDEwOCAxMDgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMDgiIGhlaWdodD0iMTA4IiByeD0iMjYiIGZpbGw9IiNBQjlGRjIiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00Ni41MjY3IDY5LjkyMjlDNDIuMDA1NCA3Ni44NTA5IDM0LjQyOTIgODUuNjE4MiAyNC4zNDggODUuNjE4MkMxOS41ODI0IDg1LjYxODIgMTUgODMuNjU2MyAxNSA3NS4xMzQyQzE1IDUzLjQzMDUgNDQuNjMyNiAxOS44MzI3IDcyLjEyNjggMTkuODMyN0M4Ny43NjggMTkuODMyNyA5NCAzMC42ODQ2IDk0IDQzLjAwNzlDOTQgNTguODI1OCA4My43MzU1IDc2LjkxMjIgNzMuNTMyMSA3Ni45MTIyQzcwLjI5MzkgNzYuOTEyMiA2OC43MDUzIDc1LjEzNDIgNjguNzA1MyA3Mi4zMTRDNjguNzA1MyA3MS41NzgzIDY4LjgyNzUgNzAuNzgxMiA2OS4wNzE5IDY5LjkyMjlDNjUuNTg5MyA3NS44Njk5IDU4Ljg2ODUgODEuMzg3OCA1Mi41NzU0IDgxLjM4NzhDNDcuOTkzIDgxLjM4NzggNDUuNjcxMyA3OC41MDYzIDQ1LjY3MTMgNzQuNDU5OEM0NS42NzEzIDcyLjk4ODQgNDUuOTc2OCA3MS40NTU2IDQ2LjUyNjcgNjkuOTIyOVpNODMuNjc2MSA0Mi41Nzk0QzgzLjY3NjEgNDYuMTcwNCA4MS41NTc1IDQ3Ljk2NTggNzkuMTg3NSA0Ny45NjU4Qzc2Ljc4MTYgNDcuOTY1OCA3NC42OTg5IDQ2LjE3MDQgNzQuNjk4OSA0Mi41Nzk0Qzc0LjY5ODkgMzguOTg4NSA3Ni43ODE2IDM3LjE5MzEgNzkuMTg3NSAzNy4xOTMxQzgxLjU1NzUgMzcuMTkzMSA4My42NzYxIDM4Ljk4ODUgODMuNjc2MSA0Mi41Nzk0Wk03MC4yMTAzIDQyLjU3OTVDNzAuMjEwMyA0Ni4xNzA0IDY4LjA5MTYgNDcuOTY1OCA2NS43MjE2IDQ3Ljk2NThDNjMuMzE1NyA0Ny45NjU4IDYxLjIzMyA0Ni4xNzA0IDYxLjIzMyA0Mi41Nzk1QzYxLjIzMyAzOC45ODg1IDYzLjMxNTcgMzcuMTkzMSA2NS43MjE2IDM3LjE5MzFDNjguMDkxNiAzNy4xOTMxIDcwLjIxMDMgMzguOTg4NSA3MC4yMTAzIDQyLjU3OTVaIiBmaWxsPSIjRkZGREY4Ii8+Cjwvc3ZnPgo=",
|
|
820
|
+
buildUrl: (url) => `https://phantom.app/ul/browse/${encodeURIComponent(url)}?ref=${encodeURIComponent(url)}`
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
name: "Solflare",
|
|
824
|
+
icon: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIGlkPSJTIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MCA1MCI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMwMjA1MGE7c3Ryb2tlOiNmZmVmNDY7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOi41cHg7fS5jbHMtMntmaWxsOiNmZmVmNDY7fTwvc3R5bGU+PC9kZWZzPjxyZWN0IGNsYXNzPSJjbHMtMiIgeD0iMCIgd2lkdGg9IjUwIiBoZWlnaHQ9IjUwIiByeD0iMTIiIHJ5PSIxMiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTI0LjIzLDI2LjQybDIuNDYtMi4zOCw0LjU5LDEuNWMzLjAxLDEsNC41MSwyLjg0LDQuNTEsNS40MywwLDEuOTYtLjc1LDMuMjYtMi4yNSw0LjkzbC0uNDYuNS4xNy0xLjE3Yy42Ny00LjI2LS41OC02LjA5LTQuNzItNy40M2wtNC4zLTEuMzhoMFpNMTguMDUsMTEuODVsMTIuNTIsNC4xNy0yLjcxLDIuNTktNi41MS0yLjE3Yy0yLjI1LS43NS0zLjAxLTEuOTYtMy4zLTQuNTF2LS4wOGgwWk0xNy4zLDMzLjA2bDIuODQtMi43MSw1LjM0LDEuNzVjMi44LjkyLDMuNzYsMi4xMywzLjQ2LDUuMThsLTExLjY1LTQuMjJoMFpNMTMuNzEsMjAuOTVjMC0uNzkuNDItMS41NCwxLjEzLTIuMTcuNzUsMS4wOSwyLjA1LDIuMDUsNC4wOSwyLjcxbDQuNDIsMS40Ni0yLjQ2LDIuMzgtNC4zNC0xLjQyYy0yLS42Ny0yLjg0LTEuNjctMi44NC0yLjk2TTI2LjgyLDQyLjg3YzkuMTgtNi4wOSwxNC4xMS0xMC4yMywxNC4xMS0xNS4zMiwwLTMuMzgtMi01LjI2LTYuNDMtNi43MmwtMy4zNC0xLjEzLDkuMTQtOC43Ny0xLjg0LTEuOTYtMi43MSwyLjM4LTEyLjgxLTQuMjJjLTMuOTcsMS4yOS04Ljk3LDUuMDktOC45Nyw4Ljg5LDAsLjQyLjA0LjgzLjE3LDEuMjktMy4zLDEuODgtNC42MywzLjYzLTQuNjMsNS44LDAsMi4wNSwxLjA5LDQuMDksNC41NSw1LjIybDIuNzUuOTItOS41Miw5LjE0LDEuODQsMS45NiwyLjk2LTIuNzEsMTQuNzMsNS4yMmgwWiIvPjwvc3ZnPg==",
|
|
825
|
+
buildUrl: (url) => `https://solflare.com/ul/v1/browse/${encodeURIComponent(url)}?ref=${encodeURIComponent(url)}`
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
name: "Backpack",
|
|
829
|
+
icon: "data:image/webp;base64,UklGRlgCAABXRUJQVlA4WAoAAAAQAAAAOwAAOwAAQUxQSC8AAAABL6CmbQOGP75e/cti0oiIOI0KBto2+a8ACZjAAQ7wL2gCSCL6PwER2t+KmZpFDwBWUDggAgIAAPALAJ0BKjwAPAA+SSCMRKKiIRYKrTQoBISyAGp7nL12mKuwHiZ/pX71XoA8m7rXfQA8q39lfhJ/cL0ZimIeNSGxOaSWtqEDDdJ++ARG5/LVm6OquItoAu4rpz8VksUr1NEDJ0CeZucwAP7+VdYX7J5e8V3cJZ9QoY9vj8KsNGOvZSm+khDZOvwCeiDFjGweq/8KIUmVQk3T/qW3ONyMcepN2uwyEXpZZiidST0x705r7ZnNuWX42r8oi+FEshvXGWm6DabjfrMbiaxJ15irl2Gj3nKWbZ2v3k/jd172plSBejBs7dNt90Re/m+nmv5kNjM/SPfa4EpzvOdu/8BR+hTnR4qe4eWhTsh/oecEfN31OExdi/6AYrsth4nlx/qGEP70b1/TNKmgaB/18+c9ntTfhku9wYpy17/wN3r+B/1geZPzth+AtNsMDXE99PaPLWdFP9MOIMD6ME+MpZu2H7WSfzY7MkWs/N/NNzAZ0P/9vMvxgcdzFHvs/doeUU9WyVv+Ll8QDJbn9NZl86ZZNYtfP4Ol1hiJHWosWyj/66vhMUeFIcDheXIOSRTzvKwj5ffEl7fn/QwflNU556SEjM8MriwSFJ/nrFBrr8O1/pqdrYGqPijG6tjsBVBHp+EDAbtNLL375I2jnG9i3Xb1ZphsFYNjdRaknYdFf4g5iJBoAAA=",
|
|
830
|
+
buildUrl: (url) => `https://backpack.app/ul/browse/${encodeURIComponent(url)}`
|
|
822
831
|
}
|
|
823
|
-
|
|
824
|
-
|
|
832
|
+
];
|
|
833
|
+
function isMobile() {
|
|
834
|
+
if (typeof navigator === "undefined") return false;
|
|
835
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
825
836
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
const
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
const binary = atob(padded);
|
|
833
|
-
const buffer = new ArrayBuffer(binary.length);
|
|
834
|
-
const bytes = new Uint8Array(buffer);
|
|
835
|
-
for (let i = 0; i < binary.length; i++) {
|
|
836
|
-
bytes[i] = binary.charCodeAt(i);
|
|
837
|
+
function WalletConnectForm() {
|
|
838
|
+
const { walletAdapter, setModalStep, config } = useForgeConnect();
|
|
839
|
+
const methods = resolveLoginMethods(config);
|
|
840
|
+
const showBack = methods.length > 1;
|
|
841
|
+
if (!walletAdapter && isMobile()) {
|
|
842
|
+
return /* @__PURE__ */ jsx4(MobileWalletFlow, {});
|
|
837
843
|
}
|
|
838
|
-
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
+
if (walletAdapter) {
|
|
845
|
+
return /* @__PURE__ */ jsx4(WalletAdapterFlow, {});
|
|
846
|
+
}
|
|
847
|
+
return /* @__PURE__ */ jsxs4("div", { className: "fc-tab", children: [
|
|
848
|
+
/* @__PURE__ */ jsx4("h3", { className: "fc-tab-title", children: "Connect wallet" }),
|
|
849
|
+
/* @__PURE__ */ jsxs4("p", { className: "fc-text", children: [
|
|
850
|
+
"No wallet adapter detected. Pass ",
|
|
851
|
+
/* @__PURE__ */ jsx4("code", { children: "walletAdapter" }),
|
|
852
|
+
" to",
|
|
853
|
+
" ",
|
|
854
|
+
/* @__PURE__ */ jsx4("code", { children: "<ForgeConnectProvider>" }),
|
|
855
|
+
" to enable wallet login."
|
|
856
|
+
] }),
|
|
857
|
+
showBack && /* @__PURE__ */ jsx4("p", { className: "fc-switch", children: /* @__PURE__ */ jsx4("button", { type: "button", className: "fc-link", onClick: () => setModalStep("method-select"), children: "Back" }) })
|
|
858
|
+
] });
|
|
844
859
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
860
|
+
function MobileWalletFlow() {
|
|
861
|
+
const { setModalStep, config } = useForgeConnect();
|
|
862
|
+
const methods = resolveLoginMethods(config);
|
|
863
|
+
const showBack = methods.length > 1;
|
|
864
|
+
const walletConfig = config.walletConfig;
|
|
865
|
+
const preferred = walletConfig?.preferredWallets ?? [];
|
|
866
|
+
const onlyPreferred = walletConfig?.onlyPreferred ?? false;
|
|
867
|
+
const walletsToShow = useMemo(() => {
|
|
868
|
+
if (preferred.length > 0) {
|
|
869
|
+
const prefList = preferred.map((name) => MOBILE_WALLETS.find((mw) => mw.name === name)).filter(Boolean);
|
|
870
|
+
if (onlyPreferred) return prefList;
|
|
871
|
+
const prefNames = new Set(preferred);
|
|
872
|
+
const others = MOBILE_WALLETS.filter((mw) => !prefNames.has(mw.name));
|
|
873
|
+
return [...prefList, ...others];
|
|
874
|
+
}
|
|
875
|
+
return MOBILE_WALLETS;
|
|
876
|
+
}, [preferred, onlyPreferred]);
|
|
877
|
+
const handleOpen = (mw) => {
|
|
878
|
+
const pageUrl = window.location.href;
|
|
879
|
+
window.location.href = mw.buildUrl(pageUrl);
|
|
861
880
|
};
|
|
881
|
+
return /* @__PURE__ */ jsxs4("div", { className: "fc-tab", children: [
|
|
882
|
+
/* @__PURE__ */ jsx4("div", { className: "fc-wallet-list", children: walletsToShow.map((mw) => /* @__PURE__ */ jsxs4(
|
|
883
|
+
"button",
|
|
884
|
+
{
|
|
885
|
+
type: "button",
|
|
886
|
+
className: "fc-btn fc-btn-wallet",
|
|
887
|
+
onClick: () => handleOpen(mw),
|
|
888
|
+
children: [
|
|
889
|
+
/* @__PURE__ */ jsx4("span", { style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: mw.icon, alt: "", className: "fc-wallet-icon" }) }),
|
|
890
|
+
/* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: mw.name }),
|
|
891
|
+
/* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Open app" })
|
|
892
|
+
]
|
|
893
|
+
},
|
|
894
|
+
mw.name
|
|
895
|
+
)) }),
|
|
896
|
+
/* @__PURE__ */ jsx4("p", { className: "fc-text", style: { textAlign: "center", fontSize: 12, opacity: 0.6 }, children: "You'll be redirected to the wallet app to sign in." }),
|
|
897
|
+
showBack && /* @__PURE__ */ jsx4("p", { className: "fc-switch", children: /* @__PURE__ */ jsx4("button", { type: "button", className: "fc-link", onClick: () => setModalStep("method-select"), children: "Back" }) })
|
|
898
|
+
] });
|
|
862
899
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
);
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
};
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
});
|
|
900
|
-
}
|
|
901
|
-
} else if (error.name === "ConstraintError") {
|
|
902
|
-
if (publicKey.authenticatorSelection?.requireResidentKey === true) {
|
|
903
|
-
return new WebAuthnError({
|
|
904
|
-
message: "Discoverable credentials were required but no available authenticator supported it",
|
|
905
|
-
code: "ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",
|
|
906
|
-
cause: error
|
|
907
|
-
});
|
|
908
|
-
} else if (
|
|
909
|
-
// @ts-ignore: `mediation` doesn't yet exist on CredentialCreationOptions but it's possible as of Sept 2024
|
|
910
|
-
options.mediation === "conditional" && publicKey.authenticatorSelection?.userVerification === "required"
|
|
911
|
-
) {
|
|
912
|
-
return new WebAuthnError({
|
|
913
|
-
message: "User verification was required during automatic registration but it could not be performed",
|
|
914
|
-
code: "ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE",
|
|
915
|
-
cause: error
|
|
916
|
-
});
|
|
917
|
-
} else if (publicKey.authenticatorSelection?.userVerification === "required") {
|
|
918
|
-
return new WebAuthnError({
|
|
919
|
-
message: "User verification was required but no available authenticator supported it",
|
|
920
|
-
code: "ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",
|
|
921
|
-
cause: error
|
|
922
|
-
});
|
|
900
|
+
function WalletAdapterFlow() {
|
|
901
|
+
const { walletAdapter, loginWithWallet, setModalStep, config } = useForgeConnect();
|
|
902
|
+
const wallet = walletAdapter;
|
|
903
|
+
const walletConfig = config.walletConfig;
|
|
904
|
+
const methods = resolveLoginMethods(config);
|
|
905
|
+
const showBack = methods.length > 1;
|
|
906
|
+
const [error, setError] = useState3("");
|
|
907
|
+
const [loading, setLoading] = useState3(false);
|
|
908
|
+
const [showOther, setShowOther] = useState3(false);
|
|
909
|
+
const [coldWallet, setColdWallet] = useState3(false);
|
|
910
|
+
const mobile = useMemo(() => isMobile(), []);
|
|
911
|
+
const coldWalletRef = useRef3(coldWallet);
|
|
912
|
+
coldWalletRef.current = coldWallet;
|
|
913
|
+
const buildSignTxFnForAdapter = useCallback((adapter) => {
|
|
914
|
+
if (!adapter.signTransaction) return void 0;
|
|
915
|
+
const TxClass = walletConfig?.Transaction;
|
|
916
|
+
if (!TxClass) return void 0;
|
|
917
|
+
return async (txBase64) => {
|
|
918
|
+
const bytes = Uint8Array.from(atob(txBase64), (c) => c.charCodeAt(0));
|
|
919
|
+
const tx = TxClass.from(bytes);
|
|
920
|
+
const signedTx = await adapter.signTransaction(tx);
|
|
921
|
+
return btoa(String.fromCharCode(...new Uint8Array(signedTx.serialize())));
|
|
922
|
+
};
|
|
923
|
+
}, [walletConfig?.Transaction]);
|
|
924
|
+
const handleConnect = async (w) => {
|
|
925
|
+
if (w.readyState !== "Installed") {
|
|
926
|
+
if (mobile) {
|
|
927
|
+
const mw = MOBILE_WALLETS.find((m) => m.name === w.adapter.name);
|
|
928
|
+
if (mw) {
|
|
929
|
+
window.location.href = mw.buildUrl(window.location.href);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
const url = w.adapter.url;
|
|
934
|
+
if (url) window.open(url, "_blank");
|
|
935
|
+
return;
|
|
923
936
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
937
|
+
setError("");
|
|
938
|
+
setLoading(true);
|
|
939
|
+
try {
|
|
940
|
+
if (!w.adapter.connected) {
|
|
941
|
+
await w.adapter.connect();
|
|
942
|
+
}
|
|
943
|
+
const pk = w.adapter.publicKey;
|
|
944
|
+
if (!pk) throw new Error("Wallet did not provide a public key.");
|
|
945
|
+
const address = pk.toBase58();
|
|
946
|
+
const useCold = coldWalletRef.current;
|
|
947
|
+
const adapterSignMessage = w.adapter.signMessage ? (msg) => w.adapter.signMessage(msg) : void 0;
|
|
948
|
+
await loginWithWallet(
|
|
949
|
+
address,
|
|
950
|
+
useCold ? void 0 : adapterSignMessage,
|
|
951
|
+
"solana",
|
|
952
|
+
useCold ? buildSignTxFnForAdapter(w.adapter) : void 0
|
|
953
|
+
);
|
|
954
|
+
} catch (err) {
|
|
955
|
+
setError(err instanceof Error ? err.message : "Could not verify your wallet. Please try again.");
|
|
956
|
+
setLoading(false);
|
|
944
957
|
}
|
|
945
|
-
return new WebAuthnError({
|
|
946
|
-
message: "No available authenticator supported any of the specified pubKeyCredParams algorithms",
|
|
947
|
-
code: "ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",
|
|
948
|
-
cause: error
|
|
949
|
-
});
|
|
950
|
-
} else if (error.name === "SecurityError") {
|
|
951
|
-
const effectiveDomain = globalThis.location.hostname;
|
|
952
|
-
if (!isValidDomain(effectiveDomain)) {
|
|
953
|
-
return new WebAuthnError({
|
|
954
|
-
message: `${globalThis.location.hostname} is an invalid domain`,
|
|
955
|
-
code: "ERROR_INVALID_DOMAIN",
|
|
956
|
-
cause: error
|
|
957
|
-
});
|
|
958
|
-
} else if (publicKey.rp.id !== effectiveDomain) {
|
|
959
|
-
return new WebAuthnError({
|
|
960
|
-
message: `The RP ID "${publicKey.rp.id}" is invalid for this domain`,
|
|
961
|
-
code: "ERROR_INVALID_RP_ID",
|
|
962
|
-
cause: error
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
} else if (error.name === "TypeError") {
|
|
966
|
-
if (publicKey.user.id.byteLength < 1 || publicKey.user.id.byteLength > 64) {
|
|
967
|
-
return new WebAuthnError({
|
|
968
|
-
message: "User ID was not between 1 and 64 characters",
|
|
969
|
-
code: "ERROR_INVALID_USER_ID_LENGTH",
|
|
970
|
-
cause: error
|
|
971
|
-
});
|
|
972
|
-
}
|
|
973
|
-
} else if (error.name === "UnknownError") {
|
|
974
|
-
return new WebAuthnError({
|
|
975
|
-
message: "The authenticator was unable to process the specified options, or could not create a new credential",
|
|
976
|
-
code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
|
|
977
|
-
cause: error
|
|
978
|
-
});
|
|
979
|
-
}
|
|
980
|
-
return error;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/webAuthnAbortService.js
|
|
984
|
-
var BaseWebAuthnAbortService = class {
|
|
985
|
-
constructor() {
|
|
986
|
-
Object.defineProperty(this, "controller", {
|
|
987
|
-
enumerable: true,
|
|
988
|
-
configurable: true,
|
|
989
|
-
writable: true,
|
|
990
|
-
value: void 0
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
createNewAbortSignal() {
|
|
994
|
-
if (this.controller) {
|
|
995
|
-
const abortError = new Error("Cancelling existing WebAuthn API call for new one");
|
|
996
|
-
abortError.name = "AbortError";
|
|
997
|
-
this.controller.abort(abortError);
|
|
998
|
-
}
|
|
999
|
-
const newController = new AbortController();
|
|
1000
|
-
this.controller = newController;
|
|
1001
|
-
return newController.signal;
|
|
1002
|
-
}
|
|
1003
|
-
cancelCeremony() {
|
|
1004
|
-
if (this.controller) {
|
|
1005
|
-
const abortError = new Error("Manually cancelling existing WebAuthn API call");
|
|
1006
|
-
abortError.name = "AbortError";
|
|
1007
|
-
this.controller.abort(abortError);
|
|
1008
|
-
this.controller = void 0;
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
1012
|
-
var WebAuthnAbortService = new BaseWebAuthnAbortService();
|
|
1013
|
-
|
|
1014
|
-
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/toAuthenticatorAttachment.js
|
|
1015
|
-
var attachments = ["cross-platform", "platform"];
|
|
1016
|
-
function toAuthenticatorAttachment(attachment) {
|
|
1017
|
-
if (!attachment) {
|
|
1018
|
-
return;
|
|
1019
|
-
}
|
|
1020
|
-
if (attachments.indexOf(attachment) < 0) {
|
|
1021
|
-
return;
|
|
1022
|
-
}
|
|
1023
|
-
return attachment;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/methods/startRegistration.js
|
|
1027
|
-
async function startRegistration(options) {
|
|
1028
|
-
if (!options.optionsJSON && options.challenge) {
|
|
1029
|
-
console.warn("startRegistration() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
|
|
1030
|
-
options = { optionsJSON: options };
|
|
1031
|
-
}
|
|
1032
|
-
const { optionsJSON, useAutoRegister = false } = options;
|
|
1033
|
-
if (!browserSupportsWebAuthn()) {
|
|
1034
|
-
throw new Error("WebAuthn is not supported in this browser");
|
|
1035
|
-
}
|
|
1036
|
-
const publicKey = {
|
|
1037
|
-
...optionsJSON,
|
|
1038
|
-
challenge: base64URLStringToBuffer(optionsJSON.challenge),
|
|
1039
|
-
user: {
|
|
1040
|
-
...optionsJSON.user,
|
|
1041
|
-
id: base64URLStringToBuffer(optionsJSON.user.id)
|
|
1042
|
-
},
|
|
1043
|
-
excludeCredentials: optionsJSON.excludeCredentials?.map(toPublicKeyCredentialDescriptor)
|
|
1044
958
|
};
|
|
1045
|
-
const
|
|
1046
|
-
|
|
1047
|
-
createOptions.mediation = "conditional";
|
|
1048
|
-
}
|
|
1049
|
-
createOptions.publicKey = publicKey;
|
|
1050
|
-
createOptions.signal = WebAuthnAbortService.createNewAbortSignal();
|
|
1051
|
-
let credential;
|
|
1052
|
-
try {
|
|
1053
|
-
credential = await navigator.credentials.create(createOptions);
|
|
1054
|
-
} catch (err) {
|
|
1055
|
-
throw identifyRegistrationError({ error: err, options: createOptions });
|
|
1056
|
-
}
|
|
1057
|
-
if (!credential) {
|
|
1058
|
-
throw new Error("Registration was not completed");
|
|
1059
|
-
}
|
|
1060
|
-
const { id, rawId, response, type } = credential;
|
|
1061
|
-
let transports = void 0;
|
|
1062
|
-
if (typeof response.getTransports === "function") {
|
|
1063
|
-
transports = response.getTransports();
|
|
1064
|
-
}
|
|
1065
|
-
let responsePublicKeyAlgorithm = void 0;
|
|
1066
|
-
if (typeof response.getPublicKeyAlgorithm === "function") {
|
|
1067
|
-
try {
|
|
1068
|
-
responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm();
|
|
1069
|
-
} catch (error) {
|
|
1070
|
-
warnOnBrokenImplementation("getPublicKeyAlgorithm()", error);
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
let responsePublicKey = void 0;
|
|
1074
|
-
if (typeof response.getPublicKey === "function") {
|
|
1075
|
-
try {
|
|
1076
|
-
const _publicKey = response.getPublicKey();
|
|
1077
|
-
if (_publicKey !== null) {
|
|
1078
|
-
responsePublicKey = bufferToBase64URLString(_publicKey);
|
|
1079
|
-
}
|
|
1080
|
-
} catch (error) {
|
|
1081
|
-
warnOnBrokenImplementation("getPublicKey()", error);
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
let responseAuthenticatorData;
|
|
1085
|
-
if (typeof response.getAuthenticatorData === "function") {
|
|
1086
|
-
try {
|
|
1087
|
-
responseAuthenticatorData = bufferToBase64URLString(response.getAuthenticatorData());
|
|
1088
|
-
} catch (error) {
|
|
1089
|
-
warnOnBrokenImplementation("getAuthenticatorData()", error);
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
return {
|
|
1093
|
-
id,
|
|
1094
|
-
rawId: bufferToBase64URLString(rawId),
|
|
1095
|
-
response: {
|
|
1096
|
-
attestationObject: bufferToBase64URLString(response.attestationObject),
|
|
1097
|
-
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
|
|
1098
|
-
transports,
|
|
1099
|
-
publicKeyAlgorithm: responsePublicKeyAlgorithm,
|
|
1100
|
-
publicKey: responsePublicKey,
|
|
1101
|
-
authenticatorData: responseAuthenticatorData
|
|
1102
|
-
},
|
|
1103
|
-
type,
|
|
1104
|
-
clientExtensionResults: credential.getClientExtensionResults(),
|
|
1105
|
-
authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
|
|
959
|
+
const handleMobileOpen = (mw) => {
|
|
960
|
+
window.location.href = mw.buildUrl(window.location.href);
|
|
1106
961
|
};
|
|
962
|
+
const preferred = walletConfig?.preferredWallets ?? [];
|
|
963
|
+
const onlyPreferred = walletConfig?.onlyPreferred ?? false;
|
|
964
|
+
const preferredSet = new Set(preferred);
|
|
965
|
+
const connectedWalletName = wallet.publicKey ? wallet.wallets.find((w) => w.adapter.connected)?.adapter.name ?? null : null;
|
|
966
|
+
const { preferredWallets, otherWallets } = useMemo(() => {
|
|
967
|
+
const all = wallet.wallets;
|
|
968
|
+
const prefList = preferred.map((name) => all.find((w) => w.adapter.name === name)).filter(Boolean);
|
|
969
|
+
if (onlyPreferred && preferred.length > 0) {
|
|
970
|
+
return { preferredWallets: prefList, otherWallets: [] };
|
|
971
|
+
}
|
|
972
|
+
const others = all.filter(
|
|
973
|
+
(w) => !preferredSet.has(w.adapter.name) && w.readyState === "Installed"
|
|
974
|
+
);
|
|
975
|
+
return { preferredWallets: prefList, otherWallets: others };
|
|
976
|
+
}, [wallet.wallets, walletConfig]);
|
|
977
|
+
const mobileExtraWallets = useMemo(() => {
|
|
978
|
+
if (!mobile) return [];
|
|
979
|
+
const adapterNames = new Set(wallet.wallets.map((w) => w.adapter.name));
|
|
980
|
+
return MOBILE_WALLETS.filter((mw) => !adapterNames.has(mw.name));
|
|
981
|
+
}, [mobile, wallet.wallets]);
|
|
982
|
+
return /* @__PURE__ */ jsxs4("div", { className: "fc-tab", children: [
|
|
983
|
+
loading ? /* @__PURE__ */ jsxs4("div", { style: { textAlign: "center", padding: "24px 0" }, children: [
|
|
984
|
+
/* @__PURE__ */ jsx4("p", { className: "fc-tab-title", children: "Connecting..." }),
|
|
985
|
+
/* @__PURE__ */ jsx4("p", { className: "fc-text", children: coldWallet ? "Confirm the transaction on your device" : "Approve the connection, then sign the verification request in your wallet" }),
|
|
986
|
+
error && /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
987
|
+
/* @__PURE__ */ jsx4("p", { className: "fc-error", children: error }),
|
|
988
|
+
/* @__PURE__ */ jsx4(
|
|
989
|
+
"button",
|
|
990
|
+
{
|
|
991
|
+
type: "button",
|
|
992
|
+
className: "fc-btn fc-btn-secondary",
|
|
993
|
+
onClick: () => {
|
|
994
|
+
setLoading(false);
|
|
995
|
+
setError("");
|
|
996
|
+
},
|
|
997
|
+
style: { marginTop: 8 },
|
|
998
|
+
children: "Try again"
|
|
999
|
+
}
|
|
1000
|
+
)
|
|
1001
|
+
] })
|
|
1002
|
+
] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1003
|
+
/* @__PURE__ */ jsxs4("div", { className: "fc-wallet-list", children: [
|
|
1004
|
+
preferredWallets.map((w) => {
|
|
1005
|
+
const installed = w.readyState === "Installed";
|
|
1006
|
+
const isConnected = w.adapter.name === connectedWalletName;
|
|
1007
|
+
return /* @__PURE__ */ jsxs4(
|
|
1008
|
+
"button",
|
|
1009
|
+
{
|
|
1010
|
+
type: "button",
|
|
1011
|
+
className: "fc-btn fc-btn-wallet",
|
|
1012
|
+
onClick: () => handleConnect(w),
|
|
1013
|
+
children: [
|
|
1014
|
+
/* @__PURE__ */ jsx4("span", { className: installed ? "fc-installed-dot" : "", style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: w.adapter.icon, alt: "", className: "fc-wallet-icon" }) }),
|
|
1015
|
+
/* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: w.adapter.name }),
|
|
1016
|
+
isConnected ? /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Last used" }) : /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Preferred" }),
|
|
1017
|
+
!installed && mobile && MOBILE_WALLETS.some((mw) => mw.name === w.adapter.name) ? /* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Open app" }) : !installed && /* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Install" })
|
|
1018
|
+
]
|
|
1019
|
+
},
|
|
1020
|
+
w.adapter.name
|
|
1021
|
+
);
|
|
1022
|
+
}),
|
|
1023
|
+
mobileExtraWallets.map((mw) => /* @__PURE__ */ jsxs4(
|
|
1024
|
+
"button",
|
|
1025
|
+
{
|
|
1026
|
+
type: "button",
|
|
1027
|
+
className: "fc-btn fc-btn-wallet",
|
|
1028
|
+
onClick: () => handleMobileOpen(mw),
|
|
1029
|
+
children: [
|
|
1030
|
+
/* @__PURE__ */ jsx4("span", { style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: mw.icon, alt: "", className: "fc-wallet-icon" }) }),
|
|
1031
|
+
/* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: mw.name }),
|
|
1032
|
+
/* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Open app" })
|
|
1033
|
+
]
|
|
1034
|
+
},
|
|
1035
|
+
mw.name
|
|
1036
|
+
)),
|
|
1037
|
+
otherWallets.length > 0 && !showOther && /* @__PURE__ */ jsxs4(
|
|
1038
|
+
"button",
|
|
1039
|
+
{
|
|
1040
|
+
type: "button",
|
|
1041
|
+
className: "fc-btn fc-btn-wallet",
|
|
1042
|
+
onClick: () => setShowOther(true),
|
|
1043
|
+
children: [
|
|
1044
|
+
/* @__PURE__ */ jsx4("span", { className: "fc-other-wallets-icon", children: /* @__PURE__ */ jsxs4("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [
|
|
1045
|
+
/* @__PURE__ */ jsx4("rect", { x: "2", y: "3", width: "16", height: "14", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1046
|
+
/* @__PURE__ */ jsx4("path", { d: "M2 7h16", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1047
|
+
/* @__PURE__ */ jsx4("rect", { x: "11", y: "10", width: "7", height: "4", rx: "1", stroke: "currentColor", strokeWidth: "1.5" })
|
|
1048
|
+
] }) }),
|
|
1049
|
+
/* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: "Other wallets" })
|
|
1050
|
+
]
|
|
1051
|
+
}
|
|
1052
|
+
),
|
|
1053
|
+
showOther && otherWallets.map((w) => {
|
|
1054
|
+
const isConnected = w.adapter.name === connectedWalletName;
|
|
1055
|
+
return /* @__PURE__ */ jsxs4(
|
|
1056
|
+
"button",
|
|
1057
|
+
{
|
|
1058
|
+
type: "button",
|
|
1059
|
+
className: "fc-btn fc-btn-wallet",
|
|
1060
|
+
onClick: () => handleConnect(w),
|
|
1061
|
+
children: [
|
|
1062
|
+
/* @__PURE__ */ jsx4("span", { className: "fc-installed-dot", style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: w.adapter.icon, alt: "", className: "fc-wallet-icon" }) }),
|
|
1063
|
+
/* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: w.adapter.name }),
|
|
1064
|
+
isConnected && /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Last used" })
|
|
1065
|
+
]
|
|
1066
|
+
},
|
|
1067
|
+
w.adapter.name
|
|
1068
|
+
);
|
|
1069
|
+
})
|
|
1070
|
+
] }),
|
|
1071
|
+
preferredWallets.length === 0 && otherWallets.length === 0 && mobileExtraWallets.length === 0 && /* @__PURE__ */ jsx4("p", { className: "fc-text", children: "No wallet found. Please install a Solana wallet (like Phantom) to continue." }),
|
|
1072
|
+
walletConfig?.Transaction && /* @__PURE__ */ jsxs4("label", { className: "fc-cold-wallet-toggle", children: [
|
|
1073
|
+
/* @__PURE__ */ jsx4(
|
|
1074
|
+
"input",
|
|
1075
|
+
{
|
|
1076
|
+
type: "checkbox",
|
|
1077
|
+
checked: coldWallet,
|
|
1078
|
+
onChange: (e) => setColdWallet(e.target.checked)
|
|
1079
|
+
}
|
|
1080
|
+
),
|
|
1081
|
+
/* @__PURE__ */ jsx4("span", { className: "fc-toggle-track" }),
|
|
1082
|
+
/* @__PURE__ */ jsxs4("span", { className: "fc-cold-wallet-label", children: [
|
|
1083
|
+
/* @__PURE__ */ jsx4("span", { children: "Hardware wallet" }),
|
|
1084
|
+
/* @__PURE__ */ jsx4("span", { children: "Ledger, Trezor, Keystone..." })
|
|
1085
|
+
] })
|
|
1086
|
+
] }),
|
|
1087
|
+
error && /* @__PURE__ */ jsx4("p", { className: "fc-error", children: error })
|
|
1088
|
+
] }),
|
|
1089
|
+
showBack && !loading && /* @__PURE__ */ jsx4("p", { className: "fc-switch", children: /* @__PURE__ */ jsx4("button", { type: "button", className: "fc-link", onClick: () => setModalStep("method-select"), children: "Back" }) })
|
|
1090
|
+
] });
|
|
1107
1091
|
}
|
|
1108
|
-
function warnOnBrokenImplementation(methodName, cause) {
|
|
1109
|
-
console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${methodName}. You should report this error to them.
|
|
1110
|
-
`, cause);
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthnAutofill.js
|
|
1114
|
-
function browserSupportsWebAuthnAutofill() {
|
|
1115
|
-
if (!browserSupportsWebAuthn()) {
|
|
1116
|
-
return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
|
|
1117
|
-
}
|
|
1118
|
-
const globalPublicKeyCredential = globalThis.PublicKeyCredential;
|
|
1119
|
-
if (globalPublicKeyCredential?.isConditionalMediationAvailable === void 0) {
|
|
1120
|
-
return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
|
|
1121
|
-
}
|
|
1122
|
-
return _browserSupportsWebAuthnAutofillInternals.stubThis(globalPublicKeyCredential.isConditionalMediationAvailable());
|
|
1123
|
-
}
|
|
1124
|
-
var _browserSupportsWebAuthnAutofillInternals = {
|
|
1125
|
-
stubThis: (value) => value
|
|
1126
|
-
};
|
|
1127
1092
|
|
|
1128
|
-
//
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1093
|
+
// src/components/tabs/forgot-password.tsx
|
|
1094
|
+
import { useState as useState4 } from "react";
|
|
1095
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1096
|
+
function ForgotPasswordForm() {
|
|
1097
|
+
const { forgotPassword, resetPassword, setModalStep } = useForgeConnect();
|
|
1098
|
+
const [step, setStep] = useState4("email");
|
|
1099
|
+
const [email, setEmail] = useState4("");
|
|
1100
|
+
const [token, setToken] = useState4("");
|
|
1101
|
+
const [password, setPassword] = useState4("");
|
|
1102
|
+
const [error, setError] = useState4("");
|
|
1103
|
+
const [loading, setLoading] = useState4(false);
|
|
1104
|
+
const handleSendCode = async (e) => {
|
|
1105
|
+
e.preventDefault();
|
|
1106
|
+
setError("");
|
|
1107
|
+
setLoading(true);
|
|
1108
|
+
try {
|
|
1109
|
+
await forgotPassword(email);
|
|
1110
|
+
setStep("reset");
|
|
1111
|
+
} catch (err) {
|
|
1112
|
+
setError(err instanceof Error ? err.message : "Could not send the reset code. Please try again.");
|
|
1113
|
+
} finally {
|
|
1114
|
+
setLoading(false);
|
|
1141
1115
|
}
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
cause: error
|
|
1155
|
-
});
|
|
1156
|
-
} else if (publicKey.rpId !== effectiveDomain) {
|
|
1157
|
-
return new WebAuthnError({
|
|
1158
|
-
message: `The RP ID "${publicKey.rpId}" is invalid for this domain`,
|
|
1159
|
-
code: "ERROR_INVALID_RP_ID",
|
|
1160
|
-
cause: error
|
|
1161
|
-
});
|
|
1116
|
+
};
|
|
1117
|
+
const handleReset = async (e) => {
|
|
1118
|
+
e.preventDefault();
|
|
1119
|
+
setError("");
|
|
1120
|
+
setLoading(true);
|
|
1121
|
+
try {
|
|
1122
|
+
await resetPassword(token.trim(), password);
|
|
1123
|
+
setStep("done");
|
|
1124
|
+
} catch (err) {
|
|
1125
|
+
setError(err instanceof Error ? err.message : "Could not reset your password. Please try again.");
|
|
1126
|
+
} finally {
|
|
1127
|
+
setLoading(false);
|
|
1162
1128
|
}
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1129
|
+
};
|
|
1130
|
+
if (step === "done") {
|
|
1131
|
+
return /* @__PURE__ */ jsxs5("div", { className: "fc-tab", children: [
|
|
1132
|
+
/* @__PURE__ */ jsx5("h3", { className: "fc-tab-title", children: "Password updated" }),
|
|
1133
|
+
/* @__PURE__ */ jsx5("p", { className: "fc-text", children: "Your password has been reset. You can now sign in." }),
|
|
1134
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "fc-btn fc-btn-primary", onClick: () => setModalStep("email-login"), children: "Sign in" })
|
|
1135
|
+
] });
|
|
1169
1136
|
}
|
|
1170
|
-
|
|
1137
|
+
if (step === "reset") {
|
|
1138
|
+
return /* @__PURE__ */ jsxs5("div", { className: "fc-tab", children: [
|
|
1139
|
+
/* @__PURE__ */ jsx5("h3", { className: "fc-tab-title", children: "Set new password" }),
|
|
1140
|
+
/* @__PURE__ */ jsxs5("p", { className: "fc-text", children: [
|
|
1141
|
+
"We sent a reset code to ",
|
|
1142
|
+
/* @__PURE__ */ jsx5("strong", { children: email }),
|
|
1143
|
+
". Paste it below with your new password."
|
|
1144
|
+
] }),
|
|
1145
|
+
/* @__PURE__ */ jsxs5("form", { onSubmit: handleReset, className: "fc-form", children: [
|
|
1146
|
+
/* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
|
|
1147
|
+
"Reset code",
|
|
1148
|
+
/* @__PURE__ */ jsx5(
|
|
1149
|
+
"input",
|
|
1150
|
+
{
|
|
1151
|
+
type: "text",
|
|
1152
|
+
className: "fc-input",
|
|
1153
|
+
value: token,
|
|
1154
|
+
onChange: (e) => setToken(e.target.value),
|
|
1155
|
+
placeholder: "Paste the code from your email",
|
|
1156
|
+
required: true,
|
|
1157
|
+
autoComplete: "off"
|
|
1158
|
+
}
|
|
1159
|
+
)
|
|
1160
|
+
] }),
|
|
1161
|
+
/* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
|
|
1162
|
+
"New password",
|
|
1163
|
+
/* @__PURE__ */ jsx5(
|
|
1164
|
+
"input",
|
|
1165
|
+
{
|
|
1166
|
+
type: "password",
|
|
1167
|
+
className: "fc-input",
|
|
1168
|
+
value: password,
|
|
1169
|
+
onChange: (e) => setPassword(e.target.value),
|
|
1170
|
+
placeholder: "8+ characters",
|
|
1171
|
+
required: true,
|
|
1172
|
+
autoComplete: "new-password",
|
|
1173
|
+
minLength: 8
|
|
1174
|
+
}
|
|
1175
|
+
)
|
|
1176
|
+
] }),
|
|
1177
|
+
error && /* @__PURE__ */ jsx5("p", { className: "fc-error", children: error }),
|
|
1178
|
+
/* @__PURE__ */ jsx5("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading, children: loading ? "Resetting..." : "Reset password" })
|
|
1179
|
+
] }),
|
|
1180
|
+
/* @__PURE__ */ jsx5("p", { className: "fc-switch", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "fc-link", onClick: () => setStep("email"), children: "Resend code" }) })
|
|
1181
|
+
] });
|
|
1182
|
+
}
|
|
1183
|
+
return /* @__PURE__ */ jsxs5("div", { className: "fc-tab", children: [
|
|
1184
|
+
/* @__PURE__ */ jsx5("h3", { className: "fc-tab-title", children: "Reset your password" }),
|
|
1185
|
+
/* @__PURE__ */ jsx5("p", { className: "fc-text", children: "Enter your email and we'll send you a reset code." }),
|
|
1186
|
+
/* @__PURE__ */ jsxs5("form", { onSubmit: handleSendCode, className: "fc-form", children: [
|
|
1187
|
+
/* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
|
|
1188
|
+
"Email",
|
|
1189
|
+
/* @__PURE__ */ jsx5(
|
|
1190
|
+
"input",
|
|
1191
|
+
{
|
|
1192
|
+
type: "email",
|
|
1193
|
+
className: "fc-input",
|
|
1194
|
+
value: email,
|
|
1195
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1196
|
+
placeholder: "you@example.com",
|
|
1197
|
+
required: true,
|
|
1198
|
+
autoComplete: "email"
|
|
1199
|
+
}
|
|
1200
|
+
)
|
|
1201
|
+
] }),
|
|
1202
|
+
error && /* @__PURE__ */ jsx5("p", { className: "fc-error", children: error }),
|
|
1203
|
+
/* @__PURE__ */ jsx5("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading, children: loading ? "Sending..." : "Send reset code" })
|
|
1204
|
+
] }),
|
|
1205
|
+
/* @__PURE__ */ jsx5("p", { className: "fc-switch", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "fc-link", onClick: () => setModalStep("email-login"), children: "Back to sign in" }) })
|
|
1206
|
+
] });
|
|
1171
1207
|
}
|
|
1172
1208
|
|
|
1173
|
-
//
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
}
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1209
|
+
// src/components/tabs/verify-2fa.tsx
|
|
1210
|
+
import { useState as useState5, useRef as useRef4, useEffect as useEffect3, useCallback as useCallback2 } from "react";
|
|
1211
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1212
|
+
function Verify2FAForm() {
|
|
1213
|
+
const { verify2FA, verifyRecoveryCode, setModalStep } = useForgeConnect();
|
|
1214
|
+
const [code, setCode] = useState5("");
|
|
1215
|
+
const [loading, setLoading] = useState5(false);
|
|
1216
|
+
const [error, setError] = useState5("");
|
|
1217
|
+
const [useRecovery, setUseRecovery] = useState5(false);
|
|
1218
|
+
const inputRef = useRef4(null);
|
|
1219
|
+
const submittingRef = useRef4(false);
|
|
1220
|
+
useEffect3(() => {
|
|
1221
|
+
inputRef.current?.focus();
|
|
1222
|
+
}, [useRecovery]);
|
|
1223
|
+
const submitCode = useCallback2(async (codeValue, isRecovery) => {
|
|
1224
|
+
if (submittingRef.current || !codeValue.trim()) return;
|
|
1225
|
+
submittingRef.current = true;
|
|
1226
|
+
setLoading(true);
|
|
1227
|
+
setError("");
|
|
1228
|
+
try {
|
|
1229
|
+
if (isRecovery) {
|
|
1230
|
+
await verifyRecoveryCode(codeValue.trim());
|
|
1231
|
+
} else {
|
|
1232
|
+
await verify2FA(codeValue.trim());
|
|
1233
|
+
}
|
|
1234
|
+
} catch (err) {
|
|
1235
|
+
setError(err instanceof Error ? err.message : "Verification failed. Please try again.");
|
|
1236
|
+
} finally {
|
|
1237
|
+
setLoading(false);
|
|
1238
|
+
submittingRef.current = false;
|
|
1196
1239
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1240
|
+
}, [verify2FA, verifyRecoveryCode]);
|
|
1241
|
+
const handleSubmit = async (e) => {
|
|
1242
|
+
e?.preventDefault();
|
|
1243
|
+
await submitCode(code, useRecovery);
|
|
1244
|
+
};
|
|
1245
|
+
const handleCodeChange = (value) => {
|
|
1246
|
+
setCode(value);
|
|
1247
|
+
if (!useRecovery && value.length === 6 && /^\d{6}$/.test(value)) {
|
|
1248
|
+
submitCode(value, false);
|
|
1200
1249
|
}
|
|
1201
|
-
getOptions.mediation = "conditional";
|
|
1202
|
-
publicKey.allowCredentials = [];
|
|
1203
|
-
}
|
|
1204
|
-
getOptions.publicKey = publicKey;
|
|
1205
|
-
getOptions.signal = WebAuthnAbortService.createNewAbortSignal();
|
|
1206
|
-
let credential;
|
|
1207
|
-
try {
|
|
1208
|
-
credential = await navigator.credentials.get(getOptions);
|
|
1209
|
-
} catch (err) {
|
|
1210
|
-
throw identifyAuthenticationError({ error: err, options: getOptions });
|
|
1211
|
-
}
|
|
1212
|
-
if (!credential) {
|
|
1213
|
-
throw new Error("Authentication was not completed");
|
|
1214
|
-
}
|
|
1215
|
-
const { id, rawId, response, type } = credential;
|
|
1216
|
-
let userHandle = void 0;
|
|
1217
|
-
if (response.userHandle) {
|
|
1218
|
-
userHandle = bufferToBase64URLString(response.userHandle);
|
|
1219
|
-
}
|
|
1220
|
-
return {
|
|
1221
|
-
id,
|
|
1222
|
-
rawId: bufferToBase64URLString(rawId),
|
|
1223
|
-
response: {
|
|
1224
|
-
authenticatorData: bufferToBase64URLString(response.authenticatorData),
|
|
1225
|
-
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
|
|
1226
|
-
signature: bufferToBase64URLString(response.signature),
|
|
1227
|
-
userHandle
|
|
1228
|
-
},
|
|
1229
|
-
type,
|
|
1230
|
-
clientExtensionResults: credential.getClientExtensionResults(),
|
|
1231
|
-
authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
|
|
1232
1250
|
};
|
|
1251
|
+
return /* @__PURE__ */ jsxs6("div", { className: "fc-tab", children: [
|
|
1252
|
+
/* @__PURE__ */ jsxs6("button", { type: "button", className: "fc-back", onClick: () => setModalStep("method-select"), children: [
|
|
1253
|
+
/* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx6("path", { d: "M10 12L6 8l4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
1254
|
+
"Back"
|
|
1255
|
+
] }),
|
|
1256
|
+
/* @__PURE__ */ jsx6("p", { className: "fc-account-section-desc", style: { marginTop: 8 }, children: useRecovery ? "Enter one of your recovery codes." : "Enter the 6-digit code from your authenticator app." }),
|
|
1257
|
+
error && /* @__PURE__ */ jsx6("p", { className: "fc-error", children: error }),
|
|
1258
|
+
/* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className: "fc-form", children: [
|
|
1259
|
+
/* @__PURE__ */ jsx6(
|
|
1260
|
+
"input",
|
|
1261
|
+
{
|
|
1262
|
+
ref: inputRef,
|
|
1263
|
+
className: "fc-input fc-input-code",
|
|
1264
|
+
type: "text",
|
|
1265
|
+
inputMode: useRecovery ? "text" : "numeric",
|
|
1266
|
+
autoComplete: "one-time-code",
|
|
1267
|
+
placeholder: useRecovery ? "Recovery code" : "000000",
|
|
1268
|
+
maxLength: useRecovery ? 20 : 6,
|
|
1269
|
+
value: code,
|
|
1270
|
+
onChange: (e) => handleCodeChange(e.target.value),
|
|
1271
|
+
style: useRecovery ? {} : { textAlign: "center", letterSpacing: "0.3em", fontSize: "1.25rem" }
|
|
1272
|
+
}
|
|
1273
|
+
),
|
|
1274
|
+
/* @__PURE__ */ jsx6("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading || !code.trim(), children: loading ? "Verifying..." : "Verify" })
|
|
1275
|
+
] }),
|
|
1276
|
+
/* @__PURE__ */ jsx6(
|
|
1277
|
+
"button",
|
|
1278
|
+
{
|
|
1279
|
+
type: "button",
|
|
1280
|
+
className: "fc-link",
|
|
1281
|
+
onClick: () => {
|
|
1282
|
+
setUseRecovery(!useRecovery);
|
|
1283
|
+
setCode("");
|
|
1284
|
+
setError("");
|
|
1285
|
+
},
|
|
1286
|
+
style: { marginTop: 12 },
|
|
1287
|
+
children: useRecovery ? "Use authenticator code instead" : "Use a recovery code"
|
|
1288
|
+
}
|
|
1289
|
+
)
|
|
1290
|
+
] });
|
|
1233
1291
|
}
|
|
1234
1292
|
|
|
1235
|
-
// src/
|
|
1236
|
-
|
|
1293
|
+
// src/components/svg-icon.tsx
|
|
1294
|
+
import { useRef as useRef5, useEffect as useEffect4 } from "react";
|
|
1295
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1296
|
+
function SvgIcon({ svg, className }) {
|
|
1297
|
+
const ref = useRef5(null);
|
|
1298
|
+
useEffect4(() => {
|
|
1299
|
+
if (!ref.current || !svg) return;
|
|
1300
|
+
try {
|
|
1301
|
+
const doc = new DOMParser().parseFromString(svg, "text/html");
|
|
1302
|
+
const svgEl = doc.body.querySelector("svg");
|
|
1303
|
+
if (!svgEl) {
|
|
1304
|
+
ref.current.textContent = "";
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
const dangerous = svgEl.querySelectorAll("script,iframe,object,embed,foreignObject");
|
|
1308
|
+
dangerous.forEach((el) => el.remove());
|
|
1309
|
+
const all = svgEl.querySelectorAll("*");
|
|
1310
|
+
all.forEach((el) => {
|
|
1311
|
+
for (const attr of Array.from(el.attributes)) {
|
|
1312
|
+
if (attr.name.startsWith("on")) {
|
|
1313
|
+
el.removeAttribute(attr.name);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
ref.current.textContent = "";
|
|
1318
|
+
ref.current.appendChild(svgEl);
|
|
1319
|
+
} catch {
|
|
1320
|
+
ref.current.textContent = "";
|
|
1321
|
+
}
|
|
1322
|
+
}, [svg]);
|
|
1323
|
+
return /* @__PURE__ */ jsx7("span", { ref, className });
|
|
1324
|
+
}
|
|
1237
1325
|
|
|
1238
|
-
// src/components/tabs/
|
|
1239
|
-
import {
|
|
1240
|
-
var
|
|
1241
|
-
{
|
|
1242
|
-
|
|
1243
|
-
icon:
|
|
1244
|
-
buildUrl: (url) => `https://phantom.app/ul/browse/${encodeURIComponent(url)}?ref=${encodeURIComponent(url)}`
|
|
1326
|
+
// src/components/tabs/oauth-buttons.tsx
|
|
1327
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1328
|
+
var PROVIDER_INFO = {
|
|
1329
|
+
google: {
|
|
1330
|
+
label: "Google",
|
|
1331
|
+
icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>'
|
|
1245
1332
|
},
|
|
1246
|
-
{
|
|
1247
|
-
|
|
1248
|
-
icon: "
|
|
1249
|
-
buildUrl: (url) => `https://solflare.com/ul/v1/browse/${encodeURIComponent(url)}?ref=${encodeURIComponent(url)}`
|
|
1333
|
+
discord: {
|
|
1334
|
+
label: "Discord",
|
|
1335
|
+
icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z" fill="#5865F2"/></svg>'
|
|
1250
1336
|
},
|
|
1251
|
-
{
|
|
1252
|
-
|
|
1253
|
-
icon: "
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
const { walletAdapter, setModalStep, config } = useForgeConnect();
|
|
1263
|
-
const methods = resolveLoginMethods(config);
|
|
1264
|
-
const showBack = methods.length > 1;
|
|
1265
|
-
if (!walletAdapter && isMobile()) {
|
|
1266
|
-
return /* @__PURE__ */ jsx4(MobileWalletFlow, {});
|
|
1267
|
-
}
|
|
1268
|
-
if (walletAdapter) {
|
|
1269
|
-
return /* @__PURE__ */ jsx4(WalletAdapterFlow, {});
|
|
1337
|
+
twitter: {
|
|
1338
|
+
label: "Twitter",
|
|
1339
|
+
icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" fill="currentColor"/></svg>'
|
|
1340
|
+
},
|
|
1341
|
+
apple: {
|
|
1342
|
+
label: "Apple",
|
|
1343
|
+
icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.52-3.23 0-1.44.64-2.2.45-3.06-.4C3.79 16.17 4.36 9.02 8.8 8.78c1.27.06 2.15.72 2.91.76.93-.19 1.82-.88 2.83-.8 1.21.1 2.12.58 2.72 1.49-2.46 1.48-1.88 4.73.52 5.64-.42 1.13-.98 2.24-1.73 3.41zM12.03 8.7c-.12-2.35 1.82-4.38 4.04-4.54.29 2.56-2.34 4.68-4.04 4.54z" fill="currentColor"/></svg>'
|
|
1344
|
+
},
|
|
1345
|
+
telegram: {
|
|
1346
|
+
label: "Telegram",
|
|
1347
|
+
icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.479.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" fill="#2AABEE"/></svg>'
|
|
1270
1348
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
"
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1349
|
+
};
|
|
1350
|
+
function OAuthButton({ provider }) {
|
|
1351
|
+
const { loginWithOAuth } = useForgeConnect();
|
|
1352
|
+
const info = PROVIDER_INFO[provider];
|
|
1353
|
+
return /* @__PURE__ */ jsxs7(
|
|
1354
|
+
"button",
|
|
1355
|
+
{
|
|
1356
|
+
type: "button",
|
|
1357
|
+
className: "fc-btn fc-btn-oauth",
|
|
1358
|
+
onClick: () => loginWithOAuth(provider),
|
|
1359
|
+
children: [
|
|
1360
|
+
/* @__PURE__ */ jsx8(SvgIcon, { svg: info.icon, className: "fc-oauth-icon" }),
|
|
1361
|
+
/* @__PURE__ */ jsx8("span", { className: "fc-btn-name", children: info.label })
|
|
1362
|
+
]
|
|
1363
|
+
}
|
|
1364
|
+
);
|
|
1283
1365
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
const
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1366
|
+
|
|
1367
|
+
// src/components/login-modal.tsx
|
|
1368
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1369
|
+
function LoginModal() {
|
|
1370
|
+
const { modal, closeModal, config } = useForgeConnect();
|
|
1371
|
+
const renderStep = () => {
|
|
1372
|
+
switch (modal.step) {
|
|
1373
|
+
case "email-login":
|
|
1374
|
+
return /* @__PURE__ */ jsx9(EmailLoginForm, {});
|
|
1375
|
+
case "email-register":
|
|
1376
|
+
return /* @__PURE__ */ jsx9(EmailRegisterForm, {});
|
|
1377
|
+
case "email-otp":
|
|
1378
|
+
return /* @__PURE__ */ jsx9(EmailOtpForm, {});
|
|
1379
|
+
case "wallet-connect":
|
|
1380
|
+
return /* @__PURE__ */ jsx9(WalletConnectForm, {});
|
|
1381
|
+
case "forgot-password":
|
|
1382
|
+
return /* @__PURE__ */ jsx9(ForgotPasswordForm, {});
|
|
1383
|
+
case "verify-2fa":
|
|
1384
|
+
return /* @__PURE__ */ jsx9(Verify2FAForm, {});
|
|
1385
|
+
case "oauth":
|
|
1386
|
+
return /* @__PURE__ */ jsx9(OAuthLoadingView, {});
|
|
1387
|
+
case "success":
|
|
1388
|
+
return /* @__PURE__ */ jsx9(SuccessView, {});
|
|
1389
|
+
case "method-select":
|
|
1390
|
+
default:
|
|
1391
|
+
return /* @__PURE__ */ jsx9(MethodSelect, {});
|
|
1298
1392
|
}
|
|
1299
|
-
return MOBILE_WALLETS;
|
|
1300
|
-
}, [preferred, onlyPreferred]);
|
|
1301
|
-
const handleOpen = (mw) => {
|
|
1302
|
-
const pageUrl = window.location.href;
|
|
1303
|
-
window.location.href = mw.buildUrl(pageUrl);
|
|
1304
1393
|
};
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
"
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1394
|
+
const renderLogo = () => {
|
|
1395
|
+
if (config.appearance?.logoNode) {
|
|
1396
|
+
return /* @__PURE__ */ jsx9("div", { className: "fc-logo", children: config.appearance.logoNode });
|
|
1397
|
+
}
|
|
1398
|
+
if (config.appearance?.logo) {
|
|
1399
|
+
return /* @__PURE__ */ jsx9("img", { src: config.appearance.logo, alt: "", className: "fc-logo" });
|
|
1400
|
+
}
|
|
1401
|
+
return null;
|
|
1402
|
+
};
|
|
1403
|
+
return /* @__PURE__ */ jsx9(ModalOverlay, { isOpen: modal.isOpen, onClose: closeModal, children: /* @__PURE__ */ jsx9(
|
|
1404
|
+
"div",
|
|
1405
|
+
{
|
|
1406
|
+
className: "fc-modal-content",
|
|
1407
|
+
style: {
|
|
1408
|
+
"--fc-accent": config.appearance?.accentColor ?? "#8b5cf6"
|
|
1317
1409
|
},
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
const [loading, setLoading] = useState3(false);
|
|
1332
|
-
const [showOther, setShowOther] = useState3(false);
|
|
1333
|
-
const [coldWallet, setColdWallet] = useState3(false);
|
|
1334
|
-
const mobile = useMemo(() => isMobile(), []);
|
|
1335
|
-
const coldWalletRef = useRef3(coldWallet);
|
|
1336
|
-
coldWalletRef.current = coldWallet;
|
|
1337
|
-
const buildSignTxFnForAdapter = useCallback((adapter) => {
|
|
1338
|
-
if (!adapter.signTransaction) return void 0;
|
|
1339
|
-
return async (txBase64) => {
|
|
1340
|
-
const { Transaction } = await importSolanaWeb3();
|
|
1341
|
-
const bytes = Uint8Array.from(atob(txBase64), (c) => c.charCodeAt(0));
|
|
1342
|
-
const tx = Transaction.from(bytes);
|
|
1343
|
-
const signedTx = await adapter.signTransaction(tx);
|
|
1344
|
-
return btoa(String.fromCharCode(...new Uint8Array(signedTx.serialize())));
|
|
1345
|
-
};
|
|
1346
|
-
}, []);
|
|
1347
|
-
const handleConnect = async (w) => {
|
|
1348
|
-
if (w.readyState !== "Installed") {
|
|
1349
|
-
if (mobile) {
|
|
1350
|
-
const mw = MOBILE_WALLETS.find((m) => m.name === w.adapter.name);
|
|
1351
|
-
if (mw) {
|
|
1352
|
-
window.location.href = mw.buildUrl(window.location.href);
|
|
1353
|
-
return;
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
const url = w.adapter.url;
|
|
1357
|
-
if (url) window.open(url, "_blank");
|
|
1358
|
-
return;
|
|
1410
|
+
"data-theme": config.appearance?.theme ?? "light",
|
|
1411
|
+
children: modal.step === "success" || modal.step === "oauth" ? renderStep() : /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
1412
|
+
renderLogo(),
|
|
1413
|
+
/* @__PURE__ */ jsx9("h2", { className: "fc-modal-title", children: config.appearance?.title ?? "Log in or sign up" }),
|
|
1414
|
+
renderStep(),
|
|
1415
|
+
(config.appearance?.termsUrl || config.appearance?.privacyUrl) && /* @__PURE__ */ jsxs8("p", { className: "fc-legal", children: [
|
|
1416
|
+
"By continuing, you agree to our",
|
|
1417
|
+
" ",
|
|
1418
|
+
config.appearance.termsUrl && /* @__PURE__ */ jsx9("a", { href: config.appearance.termsUrl, target: "_blank", rel: "noopener noreferrer", className: "fc-legal-link", children: "Terms" }),
|
|
1419
|
+
config.appearance.termsUrl && config.appearance.privacyUrl && " and ",
|
|
1420
|
+
config.appearance.privacyUrl && /* @__PURE__ */ jsx9("a", { href: config.appearance.privacyUrl, target: "_blank", rel: "noopener noreferrer", className: "fc-legal-link", children: "Privacy Policy" })
|
|
1421
|
+
] })
|
|
1422
|
+
] })
|
|
1359
1423
|
}
|
|
1360
|
-
|
|
1361
|
-
|
|
1424
|
+
) });
|
|
1425
|
+
}
|
|
1426
|
+
var EmailIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
|
|
1427
|
+
/* @__PURE__ */ jsx9("rect", { x: "2", y: "4", width: "16", height: "12", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1428
|
+
/* @__PURE__ */ jsx9("path", { d: "M2 6l8 5 8-5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
1429
|
+
] }) });
|
|
1430
|
+
var OtpIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
|
|
1431
|
+
/* @__PURE__ */ jsx9("rect", { x: "3", y: "6", width: "14", height: "9", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1432
|
+
/* @__PURE__ */ jsx9("circle", { cx: "6.5", cy: "10.5", r: "1", fill: "currentColor" }),
|
|
1433
|
+
/* @__PURE__ */ jsx9("circle", { cx: "10", cy: "10.5", r: "1", fill: "currentColor" }),
|
|
1434
|
+
/* @__PURE__ */ jsx9("circle", { cx: "13.5", cy: "10.5", r: "1", fill: "currentColor" })
|
|
1435
|
+
] }) });
|
|
1436
|
+
var WalletIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
|
|
1437
|
+
/* @__PURE__ */ jsx9("rect", { x: "2", y: "5", width: "16", height: "11", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1438
|
+
/* @__PURE__ */ jsx9("rect", { x: "13", y: "9", width: "5", height: "3", rx: "1", stroke: "currentColor", strokeWidth: "1.5" })
|
|
1439
|
+
] }) });
|
|
1440
|
+
var PasskeyIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
|
|
1441
|
+
/* @__PURE__ */ jsx9("circle", { cx: "8", cy: "7", r: "3", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1442
|
+
/* @__PURE__ */ jsx9("path", { d: "M13 13.5a5 5 0 0 0-10 0", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
1443
|
+
/* @__PURE__ */ jsx9("path", { d: "M15 10v4m0 0l-1.5-1m1.5 1l1.5-1", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1444
|
+
] }) });
|
|
1445
|
+
var LoadingSpinner = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsx9("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon fc-spin", children: /* @__PURE__ */ jsx9("path", { d: "M10 2a8 8 0 0 1 8 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) });
|
|
1446
|
+
function MethodSelect() {
|
|
1447
|
+
const { setModalStep, loginWithPasskey, config } = useForgeConnect();
|
|
1448
|
+
const methods = resolveLoginMethods(config);
|
|
1449
|
+
const [passkeyLoading, setPasskeyLoading] = useState6(false);
|
|
1450
|
+
const [passkeyError, setPasskeyError] = useState6("");
|
|
1451
|
+
const handlePasskey = async () => {
|
|
1452
|
+
setPasskeyLoading(true);
|
|
1453
|
+
setPasskeyError("");
|
|
1362
1454
|
try {
|
|
1363
|
-
|
|
1364
|
-
await w.adapter.connect();
|
|
1365
|
-
}
|
|
1366
|
-
const pk = w.adapter.publicKey;
|
|
1367
|
-
if (!pk) throw new Error("Wallet did not provide a public key.");
|
|
1368
|
-
const address = pk.toBase58();
|
|
1369
|
-
const useCold = coldWalletRef.current;
|
|
1370
|
-
const adapterSignMessage = w.adapter.signMessage ? (msg) => w.adapter.signMessage(msg) : void 0;
|
|
1371
|
-
await loginWithWallet(
|
|
1372
|
-
address,
|
|
1373
|
-
useCold ? void 0 : adapterSignMessage,
|
|
1374
|
-
"solana",
|
|
1375
|
-
useCold ? buildSignTxFnForAdapter(w.adapter) : void 0
|
|
1376
|
-
);
|
|
1455
|
+
await loginWithPasskey();
|
|
1377
1456
|
} catch (err) {
|
|
1378
|
-
|
|
1379
|
-
|
|
1457
|
+
setPasskeyError(err instanceof Error ? err.message : "Passkey authentication failed.");
|
|
1458
|
+
} finally {
|
|
1459
|
+
setPasskeyLoading(false);
|
|
1380
1460
|
}
|
|
1381
1461
|
};
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
{
|
|
1414
|
-
type: "button",
|
|
1415
|
-
className: "fc-btn fc-btn-secondary",
|
|
1416
|
-
onClick: () => {
|
|
1417
|
-
setLoading(false);
|
|
1418
|
-
setError("");
|
|
1419
|
-
},
|
|
1420
|
-
style: { marginTop: 8 },
|
|
1421
|
-
children: "Try again"
|
|
1422
|
-
}
|
|
1423
|
-
)
|
|
1424
|
-
] })
|
|
1425
|
-
] }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1426
|
-
/* @__PURE__ */ jsxs4("div", { className: "fc-wallet-list", children: [
|
|
1427
|
-
preferredWallets.map((w) => {
|
|
1428
|
-
const installed = w.readyState === "Installed";
|
|
1429
|
-
const isConnected = w.adapter.name === connectedWalletName;
|
|
1430
|
-
return /* @__PURE__ */ jsxs4(
|
|
1431
|
-
"button",
|
|
1432
|
-
{
|
|
1433
|
-
type: "button",
|
|
1434
|
-
className: "fc-btn fc-btn-wallet",
|
|
1435
|
-
onClick: () => handleConnect(w),
|
|
1436
|
-
children: [
|
|
1437
|
-
/* @__PURE__ */ jsx4("span", { className: installed ? "fc-installed-dot" : "", style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: w.adapter.icon, alt: "", className: "fc-wallet-icon" }) }),
|
|
1438
|
-
/* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: w.adapter.name }),
|
|
1439
|
-
isConnected ? /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Last used" }) : /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Preferred" }),
|
|
1440
|
-
!installed && mobile && MOBILE_WALLETS.some((mw) => mw.name === w.adapter.name) ? /* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Open app" }) : !installed && /* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Install" })
|
|
1441
|
-
]
|
|
1442
|
-
},
|
|
1443
|
-
w.adapter.name
|
|
1462
|
+
const elements = [];
|
|
1463
|
+
let i = 0;
|
|
1464
|
+
while (i < methods.length) {
|
|
1465
|
+
const method = methods[i];
|
|
1466
|
+
if (isOAuthMethod(method)) {
|
|
1467
|
+
const oauthGroup = [];
|
|
1468
|
+
while (i < methods.length && isOAuthMethod(methods[i])) {
|
|
1469
|
+
oauthGroup.push(methods[i]);
|
|
1470
|
+
i++;
|
|
1471
|
+
}
|
|
1472
|
+
if (elements.length > 0) {
|
|
1473
|
+
elements.push(/* @__PURE__ */ jsx9("div", { className: "fc-divider", children: /* @__PURE__ */ jsx9("span", { children: "or" }) }, `div-before-${oauthGroup[0]}`));
|
|
1474
|
+
}
|
|
1475
|
+
elements.push(
|
|
1476
|
+
/* @__PURE__ */ jsx9("div", { className: "fc-oauth-group", children: oauthGroup.map((p) => /* @__PURE__ */ jsx9(OAuthButton, { provider: p }, p)) }, `oauth-${oauthGroup.join("-")}`)
|
|
1477
|
+
);
|
|
1478
|
+
} else {
|
|
1479
|
+
if (elements.length > 0) {
|
|
1480
|
+
const prev = methods[i - 1];
|
|
1481
|
+
if (prev && isOAuthMethod(prev)) {
|
|
1482
|
+
elements.push(/* @__PURE__ */ jsx9("div", { className: "fc-divider", children: /* @__PURE__ */ jsx9("span", { children: "or" }) }, `div-after-${prev}`));
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
switch (method) {
|
|
1486
|
+
case "email":
|
|
1487
|
+
elements.push(
|
|
1488
|
+
/* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("email-login"), children: [
|
|
1489
|
+
/* @__PURE__ */ jsx9(EmailIcon, {}),
|
|
1490
|
+
" ",
|
|
1491
|
+
/* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Password" })
|
|
1492
|
+
] }, "email")
|
|
1444
1493
|
);
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
/* @__PURE__ */ jsx4("span", { style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: mw.icon, alt: "", className: "fc-wallet-icon" }) }),
|
|
1454
|
-
/* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: mw.name }),
|
|
1455
|
-
/* @__PURE__ */ jsx4("span", { className: "fc-badge-install", children: "Open app" })
|
|
1456
|
-
]
|
|
1457
|
-
},
|
|
1458
|
-
mw.name
|
|
1459
|
-
)),
|
|
1460
|
-
otherWallets.length > 0 && !showOther && /* @__PURE__ */ jsxs4(
|
|
1461
|
-
"button",
|
|
1462
|
-
{
|
|
1463
|
-
type: "button",
|
|
1464
|
-
className: "fc-btn fc-btn-wallet",
|
|
1465
|
-
onClick: () => setShowOther(true),
|
|
1466
|
-
children: [
|
|
1467
|
-
/* @__PURE__ */ jsx4("span", { className: "fc-other-wallets-icon", children: /* @__PURE__ */ jsxs4("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [
|
|
1468
|
-
/* @__PURE__ */ jsx4("rect", { x: "2", y: "3", width: "16", height: "14", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1469
|
-
/* @__PURE__ */ jsx4("path", { d: "M2 7h16", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1470
|
-
/* @__PURE__ */ jsx4("rect", { x: "11", y: "10", width: "7", height: "4", rx: "1", stroke: "currentColor", strokeWidth: "1.5" })
|
|
1471
|
-
] }) }),
|
|
1472
|
-
/* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: "Other wallets" })
|
|
1473
|
-
]
|
|
1474
|
-
}
|
|
1475
|
-
),
|
|
1476
|
-
showOther && otherWallets.map((w) => {
|
|
1477
|
-
const isConnected = w.adapter.name === connectedWalletName;
|
|
1478
|
-
return /* @__PURE__ */ jsxs4(
|
|
1479
|
-
"button",
|
|
1480
|
-
{
|
|
1481
|
-
type: "button",
|
|
1482
|
-
className: "fc-btn fc-btn-wallet",
|
|
1483
|
-
onClick: () => handleConnect(w),
|
|
1484
|
-
children: [
|
|
1485
|
-
/* @__PURE__ */ jsx4("span", { className: "fc-installed-dot", style: { position: "relative", display: "inline-flex" }, children: /* @__PURE__ */ jsx4("img", { src: w.adapter.icon, alt: "", className: "fc-wallet-icon" }) }),
|
|
1486
|
-
/* @__PURE__ */ jsx4("span", { className: "fc-wallet-name", children: w.adapter.name }),
|
|
1487
|
-
isConnected && /* @__PURE__ */ jsx4("span", { className: "fc-badge-preferred", children: "Last used" })
|
|
1488
|
-
]
|
|
1489
|
-
},
|
|
1490
|
-
w.adapter.name
|
|
1494
|
+
break;
|
|
1495
|
+
case "otp":
|
|
1496
|
+
elements.push(
|
|
1497
|
+
/* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("email-otp"), children: [
|
|
1498
|
+
/* @__PURE__ */ jsx9(OtpIcon, {}),
|
|
1499
|
+
" ",
|
|
1500
|
+
/* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Magic code" })
|
|
1501
|
+
] }, "otp")
|
|
1491
1502
|
);
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1503
|
+
break;
|
|
1504
|
+
case "wallet":
|
|
1505
|
+
elements.push(
|
|
1506
|
+
/* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("wallet-connect"), children: [
|
|
1507
|
+
/* @__PURE__ */ jsx9(WalletIcon, {}),
|
|
1508
|
+
" ",
|
|
1509
|
+
/* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Wallet" })
|
|
1510
|
+
] }, "wallet")
|
|
1511
|
+
);
|
|
1512
|
+
break;
|
|
1513
|
+
case "passkey":
|
|
1514
|
+
elements.push(
|
|
1515
|
+
/* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: handlePasskey, disabled: passkeyLoading, children: [
|
|
1516
|
+
passkeyLoading ? /* @__PURE__ */ jsx9(LoadingSpinner, {}) : /* @__PURE__ */ jsx9(PasskeyIcon, {}),
|
|
1517
|
+
/* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: passkeyLoading ? "Waiting for passkey..." : "Passkey" })
|
|
1518
|
+
] }, "passkey")
|
|
1519
|
+
);
|
|
1520
|
+
break;
|
|
1521
|
+
}
|
|
1522
|
+
i++;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
return /* @__PURE__ */ jsxs8("div", { className: "fc-method-select", children: [
|
|
1526
|
+
passkeyError && /* @__PURE__ */ jsx9("p", { className: "fc-error", children: passkeyError }),
|
|
1527
|
+
elements
|
|
1528
|
+
] });
|
|
1529
|
+
}
|
|
1530
|
+
function OAuthLoadingView() {
|
|
1531
|
+
const { setModalStep } = useForgeConnect();
|
|
1532
|
+
return /* @__PURE__ */ jsxs8("div", { className: "fc-success", children: [
|
|
1533
|
+
/* @__PURE__ */ jsx9("div", { className: "fc-success-icon", children: /* @__PURE__ */ jsx9("svg", { width: "52", height: "52", viewBox: "0 0 52 52", className: "fc-spin", children: /* @__PURE__ */ jsx9("circle", { cx: "26", cy: "26", r: "24", fill: "none", stroke: "var(--fc-accent, #8b5cf6)", strokeWidth: "3", strokeLinecap: "round", strokeDasharray: "100", strokeDashoffset: "30" }) }) }),
|
|
1534
|
+
/* @__PURE__ */ jsx9("p", { className: "fc-success-text", children: "Completing sign in..." }),
|
|
1535
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: "fc-btn fc-btn-secondary", style: { marginTop: "16px" }, onClick: () => setModalStep("method-select"), children: "Cancel" })
|
|
1536
|
+
] });
|
|
1537
|
+
}
|
|
1538
|
+
function SuccessView() {
|
|
1539
|
+
return /* @__PURE__ */ jsxs8("div", { className: "fc-success", children: [
|
|
1540
|
+
/* @__PURE__ */ jsx9("div", { className: "fc-success-icon", children: /* @__PURE__ */ jsxs8("svg", { viewBox: "0 0 52 52", className: "fc-success-check", children: [
|
|
1541
|
+
/* @__PURE__ */ jsx9("circle", { className: "fc-success-circle", cx: "26", cy: "26", r: "24", fill: "none" }),
|
|
1542
|
+
/* @__PURE__ */ jsx9("path", { className: "fc-success-tick", fill: "none", d: "M15 26l7.5 7.5L37 19" })
|
|
1543
|
+
] }) }),
|
|
1544
|
+
/* @__PURE__ */ jsx9("p", { className: "fc-success-text", children: "You're in" })
|
|
1513
1545
|
] });
|
|
1514
1546
|
}
|
|
1515
1547
|
|
|
1516
|
-
// src/components/
|
|
1517
|
-
import { useState as
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
const
|
|
1523
|
-
const [
|
|
1524
|
-
const [
|
|
1525
|
-
const
|
|
1526
|
-
const
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1548
|
+
// src/components/account-modal.tsx
|
|
1549
|
+
import { useState as useState14, useEffect as useEffect10, useRef as useRef7, useCallback as useCallback7 } from "react";
|
|
1550
|
+
|
|
1551
|
+
// src/hooks/use-user.ts
|
|
1552
|
+
import { useState as useState7, useCallback as useCallback3, useEffect as useEffect5, useRef as useRef6 } from "react";
|
|
1553
|
+
function useUser() {
|
|
1554
|
+
const { auth, api, config, getAccessToken } = useForgeConnect();
|
|
1555
|
+
const [authMethods, setAuthMethods] = useState7(null);
|
|
1556
|
+
const [loading, setLoading] = useState7(false);
|
|
1557
|
+
const pendingRefreshRef = useRef6(false);
|
|
1558
|
+
const updateProfile = useCallback3(
|
|
1559
|
+
async (data) => {
|
|
1560
|
+
const token = getAccessToken();
|
|
1561
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1562
|
+
return api.updateMe(token, data);
|
|
1563
|
+
},
|
|
1564
|
+
[api, getAccessToken]
|
|
1565
|
+
);
|
|
1566
|
+
const fetchAuthMethods = useCallback3(async () => {
|
|
1567
|
+
const token = getAccessToken();
|
|
1568
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1530
1569
|
setLoading(true);
|
|
1531
1570
|
try {
|
|
1532
|
-
await
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
setError(err instanceof Error ? err.message : "Could not send the reset code. Please try again.");
|
|
1571
|
+
const methods = await api.getAuthMethods(token);
|
|
1572
|
+
setAuthMethods(methods);
|
|
1573
|
+
return methods;
|
|
1536
1574
|
} finally {
|
|
1537
1575
|
setLoading(false);
|
|
1538
1576
|
}
|
|
1577
|
+
}, [api, getAccessToken]);
|
|
1578
|
+
const linkAuthMethod = useCallback3(
|
|
1579
|
+
async (data) => {
|
|
1580
|
+
const token = getAccessToken();
|
|
1581
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1582
|
+
await api.linkAuthMethod(token, data);
|
|
1583
|
+
await fetchAuthMethods();
|
|
1584
|
+
},
|
|
1585
|
+
[api, getAccessToken, fetchAuthMethods]
|
|
1586
|
+
);
|
|
1587
|
+
const linkOtpSend = useCallback3(
|
|
1588
|
+
async (email) => {
|
|
1589
|
+
const token = getAccessToken();
|
|
1590
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1591
|
+
await api.linkOtpSend(token, email);
|
|
1592
|
+
},
|
|
1593
|
+
[api, getAccessToken]
|
|
1594
|
+
);
|
|
1595
|
+
const linkOtpVerify = useCallback3(
|
|
1596
|
+
async (email, code) => {
|
|
1597
|
+
const token = getAccessToken();
|
|
1598
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1599
|
+
await api.linkOtpVerify(token, email, code);
|
|
1600
|
+
await fetchAuthMethods();
|
|
1601
|
+
},
|
|
1602
|
+
[api, getAccessToken, fetchAuthMethods]
|
|
1603
|
+
);
|
|
1604
|
+
const unlinkAuthMethod = useCallback3(
|
|
1605
|
+
async (id) => {
|
|
1606
|
+
const token = getAccessToken();
|
|
1607
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1608
|
+
await api.unlinkAuthMethod(token, id);
|
|
1609
|
+
await fetchAuthMethods();
|
|
1610
|
+
},
|
|
1611
|
+
[api, getAccessToken, fetchAuthMethods]
|
|
1612
|
+
);
|
|
1613
|
+
const linkOAuth = useCallback3(
|
|
1614
|
+
async (provider) => {
|
|
1615
|
+
const token = getAccessToken();
|
|
1616
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1617
|
+
const { intentToken } = await api.createLinkIntent(token);
|
|
1618
|
+
const redirectUri = encodeURIComponent(window.location.origin + "/fc-oauth-callback");
|
|
1619
|
+
const url = `${config.apiUrl}/auth/oauth/${provider}/link?intent=${encodeURIComponent(intentToken)}&redirect_uri=${redirectUri}`;
|
|
1620
|
+
const width = 500;
|
|
1621
|
+
const height = 600;
|
|
1622
|
+
const left = window.screenX + (window.innerWidth - width) / 2;
|
|
1623
|
+
const top = window.screenY + (window.innerHeight - height) / 2;
|
|
1624
|
+
window.open(url, "fc_oauth_link", `width=${width},height=${height},left=${left},top=${top}`);
|
|
1625
|
+
pendingRefreshRef.current = true;
|
|
1626
|
+
},
|
|
1627
|
+
[api, config.apiUrl, getAccessToken]
|
|
1628
|
+
);
|
|
1629
|
+
useEffect5(() => {
|
|
1630
|
+
const handleMessage = (event) => {
|
|
1631
|
+
if (event.origin !== window.location.origin) return;
|
|
1632
|
+
if (event.data?.type === "fc_oauth_link_success" && pendingRefreshRef.current) {
|
|
1633
|
+
pendingRefreshRef.current = false;
|
|
1634
|
+
fetchAuthMethods().catch(() => {
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
window.addEventListener("message", handleMessage);
|
|
1639
|
+
return () => window.removeEventListener("message", handleMessage);
|
|
1640
|
+
}, [fetchAuthMethods]);
|
|
1641
|
+
return {
|
|
1642
|
+
user: auth.user,
|
|
1643
|
+
authMethods,
|
|
1644
|
+
loading,
|
|
1645
|
+
updateProfile,
|
|
1646
|
+
fetchAuthMethods,
|
|
1647
|
+
linkAuthMethod,
|
|
1648
|
+
linkOtpSend,
|
|
1649
|
+
linkOtpVerify,
|
|
1650
|
+
unlinkAuthMethod,
|
|
1651
|
+
linkOAuth
|
|
1539
1652
|
};
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// src/hooks/use-wallets.ts
|
|
1656
|
+
import { useState as useState8, useCallback as useCallback4 } from "react";
|
|
1657
|
+
|
|
1658
|
+
// src/lib/utils.ts
|
|
1659
|
+
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
1660
|
+
function uint8ArrayToBase58(bytes) {
|
|
1661
|
+
const digits = [0];
|
|
1662
|
+
for (const byte of bytes) {
|
|
1663
|
+
let carry = byte;
|
|
1664
|
+
for (let j = 0; j < digits.length; j++) {
|
|
1665
|
+
carry += digits[j] << 8;
|
|
1666
|
+
digits[j] = carry % 58;
|
|
1667
|
+
carry = carry / 58 | 0;
|
|
1668
|
+
}
|
|
1669
|
+
while (carry > 0) {
|
|
1670
|
+
digits.push(carry % 58);
|
|
1671
|
+
carry = carry / 58 | 0;
|
|
1551
1672
|
}
|
|
1552
|
-
};
|
|
1553
|
-
if (step === "done") {
|
|
1554
|
-
return /* @__PURE__ */ jsxs5("div", { className: "fc-tab", children: [
|
|
1555
|
-
/* @__PURE__ */ jsx5("h3", { className: "fc-tab-title", children: "Password updated" }),
|
|
1556
|
-
/* @__PURE__ */ jsx5("p", { className: "fc-text", children: "Your password has been reset. You can now sign in." }),
|
|
1557
|
-
/* @__PURE__ */ jsx5("button", { type: "button", className: "fc-btn fc-btn-primary", onClick: () => setModalStep("email-login"), children: "Sign in" })
|
|
1558
|
-
] });
|
|
1559
1673
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
/* @__PURE__ */ jsxs5("p", { className: "fc-text", children: [
|
|
1564
|
-
"We sent a reset code to ",
|
|
1565
|
-
/* @__PURE__ */ jsx5("strong", { children: email }),
|
|
1566
|
-
". Paste it below with your new password."
|
|
1567
|
-
] }),
|
|
1568
|
-
/* @__PURE__ */ jsxs5("form", { onSubmit: handleReset, className: "fc-form", children: [
|
|
1569
|
-
/* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
|
|
1570
|
-
"Reset code",
|
|
1571
|
-
/* @__PURE__ */ jsx5(
|
|
1572
|
-
"input",
|
|
1573
|
-
{
|
|
1574
|
-
type: "text",
|
|
1575
|
-
className: "fc-input",
|
|
1576
|
-
value: token,
|
|
1577
|
-
onChange: (e) => setToken(e.target.value),
|
|
1578
|
-
placeholder: "Paste the code from your email",
|
|
1579
|
-
required: true,
|
|
1580
|
-
autoComplete: "off"
|
|
1581
|
-
}
|
|
1582
|
-
)
|
|
1583
|
-
] }),
|
|
1584
|
-
/* @__PURE__ */ jsxs5("label", { className: "fc-label", children: [
|
|
1585
|
-
"New password",
|
|
1586
|
-
/* @__PURE__ */ jsx5(
|
|
1587
|
-
"input",
|
|
1588
|
-
{
|
|
1589
|
-
type: "password",
|
|
1590
|
-
className: "fc-input",
|
|
1591
|
-
value: password,
|
|
1592
|
-
onChange: (e) => setPassword(e.target.value),
|
|
1593
|
-
placeholder: "8+ characters",
|
|
1594
|
-
required: true,
|
|
1595
|
-
autoComplete: "new-password",
|
|
1596
|
-
minLength: 8
|
|
1597
|
-
}
|
|
1598
|
-
)
|
|
1599
|
-
] }),
|
|
1600
|
-
error && /* @__PURE__ */ jsx5("p", { className: "fc-error", children: error }),
|
|
1601
|
-
/* @__PURE__ */ jsx5("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading, children: loading ? "Resetting..." : "Reset password" })
|
|
1602
|
-
] }),
|
|
1603
|
-
/* @__PURE__ */ jsx5("p", { className: "fc-switch", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "fc-link", onClick: () => setStep("email"), children: "Resend code" }) })
|
|
1604
|
-
] });
|
|
1674
|
+
let str = "";
|
|
1675
|
+
for (let i = 0; i < bytes.length && bytes[i] === 0; i++) {
|
|
1676
|
+
str += "1";
|
|
1605
1677
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
)
|
|
1624
|
-
] }),
|
|
1625
|
-
error && /* @__PURE__ */ jsx5("p", { className: "fc-error", children: error }),
|
|
1626
|
-
/* @__PURE__ */ jsx5("button", { type: "submit", className: "fc-btn fc-btn-primary", disabled: loading, children: loading ? "Sending..." : "Send reset code" })
|
|
1627
|
-
] }),
|
|
1628
|
-
/* @__PURE__ */ jsx5("p", { className: "fc-switch", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "fc-link", onClick: () => setModalStep("email-login"), children: "Back to sign in" }) })
|
|
1629
|
-
] });
|
|
1678
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
1679
|
+
str += BASE58_ALPHABET[digits[i]];
|
|
1680
|
+
}
|
|
1681
|
+
return str;
|
|
1682
|
+
}
|
|
1683
|
+
function timeAgo(dateStr) {
|
|
1684
|
+
const now = Date.now();
|
|
1685
|
+
const then = new Date(dateStr).getTime();
|
|
1686
|
+
const diff = now - then;
|
|
1687
|
+
const mins = Math.floor(diff / 6e4);
|
|
1688
|
+
if (mins < 1) return "just now";
|
|
1689
|
+
if (mins < 60) return `${mins}m ago`;
|
|
1690
|
+
const hours = Math.floor(mins / 60);
|
|
1691
|
+
if (hours < 24) return `${hours}h ago`;
|
|
1692
|
+
const days = Math.floor(hours / 24);
|
|
1693
|
+
if (days < 30) return `${days}d ago`;
|
|
1694
|
+
return new Date(dateStr).toLocaleDateString();
|
|
1630
1695
|
}
|
|
1631
1696
|
|
|
1632
|
-
// src/
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
const
|
|
1637
|
-
const
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
const [useRecovery, setUseRecovery] = useState5(false);
|
|
1641
|
-
const inputRef = useRef4(null);
|
|
1642
|
-
const submittingRef = useRef4(false);
|
|
1643
|
-
useEffect3(() => {
|
|
1644
|
-
inputRef.current?.focus();
|
|
1645
|
-
}, [useRecovery]);
|
|
1646
|
-
const submitCode = useCallback2(async (codeValue, isRecovery) => {
|
|
1647
|
-
if (submittingRef.current || !codeValue.trim()) return;
|
|
1648
|
-
submittingRef.current = true;
|
|
1697
|
+
// src/hooks/use-wallets.ts
|
|
1698
|
+
function useWallets() {
|
|
1699
|
+
const { api, getAccessToken } = useForgeConnect();
|
|
1700
|
+
const [wallets, setWallets] = useState8(null);
|
|
1701
|
+
const [loading, setLoading] = useState8(false);
|
|
1702
|
+
const fetchWallets = useCallback4(async () => {
|
|
1703
|
+
const token = getAccessToken();
|
|
1704
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1649
1705
|
setLoading(true);
|
|
1650
|
-
setError("");
|
|
1651
1706
|
try {
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
await verify2FA(codeValue.trim());
|
|
1656
|
-
}
|
|
1657
|
-
} catch (err) {
|
|
1658
|
-
setError(err instanceof Error ? err.message : "Verification failed. Please try again.");
|
|
1707
|
+
const data = await api.getWallets(token);
|
|
1708
|
+
setWallets(data);
|
|
1709
|
+
return data;
|
|
1659
1710
|
} finally {
|
|
1660
1711
|
setLoading(false);
|
|
1661
|
-
submittingRef.current = false;
|
|
1662
|
-
}
|
|
1663
|
-
}, [verify2FA, verifyRecoveryCode]);
|
|
1664
|
-
const handleSubmit = async (e) => {
|
|
1665
|
-
e?.preventDefault();
|
|
1666
|
-
await submitCode(code, useRecovery);
|
|
1667
|
-
};
|
|
1668
|
-
const handleCodeChange = (value) => {
|
|
1669
|
-
setCode(value);
|
|
1670
|
-
if (!useRecovery && value.length === 6 && /^\d{6}$/.test(value)) {
|
|
1671
|
-
submitCode(value, false);
|
|
1672
1712
|
}
|
|
1673
|
-
};
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
"
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1713
|
+
}, [api, getAccessToken]);
|
|
1714
|
+
const updateWallet = useCallback4(
|
|
1715
|
+
async (id, data) => {
|
|
1716
|
+
const token = getAccessToken();
|
|
1717
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1718
|
+
await api.updateWallet(token, id, data);
|
|
1719
|
+
await fetchWallets();
|
|
1720
|
+
},
|
|
1721
|
+
[api, getAccessToken, fetchWallets]
|
|
1722
|
+
);
|
|
1723
|
+
const linkWallet = useCallback4(
|
|
1724
|
+
async (walletAddress, signMessage, chain = "solana", signTransaction) => {
|
|
1725
|
+
const token = getAccessToken();
|
|
1726
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1727
|
+
if (signMessage) {
|
|
1728
|
+
const { challengeId, message: challengeMessage } = await api.walletChallenge(walletAddress, chain);
|
|
1729
|
+
const encoded = new TextEncoder().encode(challengeMessage);
|
|
1730
|
+
const signatureBytes = await signMessage(encoded);
|
|
1731
|
+
const signature = chain === "solana" ? uint8ArrayToBase58(signatureBytes) : Array.from(signatureBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1732
|
+
await api.linkAuthMethod(token, {
|
|
1733
|
+
provider: `${chain}_wallet`,
|
|
1734
|
+
challengeId,
|
|
1735
|
+
signature,
|
|
1736
|
+
walletAddress
|
|
1737
|
+
});
|
|
1738
|
+
} else {
|
|
1739
|
+
if (!signTransaction) {
|
|
1740
|
+
throw new Error("Wallet does not support message signing or transaction signing.");
|
|
1695
1741
|
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
onClick: () => {
|
|
1705
|
-
setUseRecovery(!useRecovery);
|
|
1706
|
-
setCode("");
|
|
1707
|
-
setError("");
|
|
1708
|
-
},
|
|
1709
|
-
style: { marginTop: 12 },
|
|
1710
|
-
children: useRecovery ? "Use authenticator code instead" : "Use a recovery code"
|
|
1742
|
+
const { challengeId, transaction: txBase64 } = await api.walletChallengeTx(walletAddress, chain);
|
|
1743
|
+
const signedTxBase64 = await signTransaction(txBase64);
|
|
1744
|
+
await api.linkAuthMethod(token, {
|
|
1745
|
+
provider: `${chain}_wallet_tx`,
|
|
1746
|
+
challengeId,
|
|
1747
|
+
signedTransaction: signedTxBase64,
|
|
1748
|
+
walletAddress
|
|
1749
|
+
});
|
|
1711
1750
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1751
|
+
await fetchWallets();
|
|
1752
|
+
},
|
|
1753
|
+
[api, getAccessToken, fetchWallets]
|
|
1754
|
+
);
|
|
1755
|
+
return {
|
|
1756
|
+
wallets,
|
|
1757
|
+
loading,
|
|
1758
|
+
fetchWallets,
|
|
1759
|
+
updateWallet,
|
|
1760
|
+
linkWallet
|
|
1761
|
+
};
|
|
1714
1762
|
}
|
|
1715
1763
|
|
|
1716
|
-
// src/
|
|
1717
|
-
import {
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
const
|
|
1721
|
-
|
|
1722
|
-
|
|
1764
|
+
// src/hooks/use-sessions.ts
|
|
1765
|
+
import { useState as useState9, useCallback as useCallback5 } from "react";
|
|
1766
|
+
function useSessions() {
|
|
1767
|
+
const { api, getAccessToken } = useForgeConnect();
|
|
1768
|
+
const [sessions, setSessions] = useState9(null);
|
|
1769
|
+
const [loading, setLoading] = useState9(false);
|
|
1770
|
+
const fetchSessions = useCallback5(async () => {
|
|
1771
|
+
const token = getAccessToken();
|
|
1772
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1773
|
+
setLoading(true);
|
|
1723
1774
|
try {
|
|
1724
|
-
const
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
}
|
|
1730
|
-
const dangerous = svgEl.querySelectorAll("script,iframe,object,embed,foreignObject");
|
|
1731
|
-
dangerous.forEach((el) => el.remove());
|
|
1732
|
-
const all = svgEl.querySelectorAll("*");
|
|
1733
|
-
all.forEach((el) => {
|
|
1734
|
-
for (const attr of Array.from(el.attributes)) {
|
|
1735
|
-
if (attr.name.startsWith("on")) {
|
|
1736
|
-
el.removeAttribute(attr.name);
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
});
|
|
1740
|
-
ref.current.textContent = "";
|
|
1741
|
-
ref.current.appendChild(svgEl);
|
|
1742
|
-
} catch {
|
|
1743
|
-
ref.current.textContent = "";
|
|
1744
|
-
}
|
|
1745
|
-
}, [svg]);
|
|
1746
|
-
return /* @__PURE__ */ jsx7("span", { ref, className });
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
|
-
// src/components/tabs/oauth-buttons.tsx
|
|
1750
|
-
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1751
|
-
var PROVIDER_INFO = {
|
|
1752
|
-
google: {
|
|
1753
|
-
label: "Google",
|
|
1754
|
-
icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>'
|
|
1755
|
-
},
|
|
1756
|
-
discord: {
|
|
1757
|
-
label: "Discord",
|
|
1758
|
-
icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z" fill="#5865F2"/></svg>'
|
|
1759
|
-
},
|
|
1760
|
-
twitter: {
|
|
1761
|
-
label: "Twitter",
|
|
1762
|
-
icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" fill="currentColor"/></svg>'
|
|
1763
|
-
},
|
|
1764
|
-
apple: {
|
|
1765
|
-
label: "Apple",
|
|
1766
|
-
icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.52-3.23 0-1.44.64-2.2.45-3.06-.4C3.79 16.17 4.36 9.02 8.8 8.78c1.27.06 2.15.72 2.91.76.93-.19 1.82-.88 2.83-.8 1.21.1 2.12.58 2.72 1.49-2.46 1.48-1.88 4.73.52 5.64-.42 1.13-.98 2.24-1.73 3.41zM12.03 8.7c-.12-2.35 1.82-4.38 4.04-4.54.29 2.56-2.34 4.68-4.04 4.54z" fill="currentColor"/></svg>'
|
|
1767
|
-
},
|
|
1768
|
-
telegram: {
|
|
1769
|
-
label: "Telegram",
|
|
1770
|
-
icon: '<svg viewBox="0 0 24 24" width="20" height="20"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.479.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" fill="#2AABEE"/></svg>'
|
|
1771
|
-
}
|
|
1772
|
-
};
|
|
1773
|
-
function OAuthButton({ provider }) {
|
|
1774
|
-
const { loginWithOAuth } = useForgeConnect();
|
|
1775
|
-
const info = PROVIDER_INFO[provider];
|
|
1776
|
-
return /* @__PURE__ */ jsxs7(
|
|
1777
|
-
"button",
|
|
1778
|
-
{
|
|
1779
|
-
type: "button",
|
|
1780
|
-
className: "fc-btn fc-btn-oauth",
|
|
1781
|
-
onClick: () => loginWithOAuth(provider),
|
|
1782
|
-
children: [
|
|
1783
|
-
/* @__PURE__ */ jsx8(SvgIcon, { svg: info.icon, className: "fc-oauth-icon" }),
|
|
1784
|
-
/* @__PURE__ */ jsx8("span", { className: "fc-btn-name", children: info.label })
|
|
1785
|
-
]
|
|
1775
|
+
const data = await api.getSessions(token);
|
|
1776
|
+
setSessions(data);
|
|
1777
|
+
return data;
|
|
1778
|
+
} finally {
|
|
1779
|
+
setLoading(false);
|
|
1786
1780
|
}
|
|
1781
|
+
}, [api, getAccessToken]);
|
|
1782
|
+
const revokeSession = useCallback5(
|
|
1783
|
+
async (id) => {
|
|
1784
|
+
const token = getAccessToken();
|
|
1785
|
+
if (!token) throw new Error("Please sign in to continue.");
|
|
1786
|
+
await api.revokeSession(token, id);
|
|
1787
|
+
await fetchSessions();
|
|
1788
|
+
},
|
|
1789
|
+
[api, getAccessToken, fetchSessions]
|
|
1787
1790
|
);
|
|
1791
|
+
return {
|
|
1792
|
+
sessions,
|
|
1793
|
+
loading,
|
|
1794
|
+
fetchSessions,
|
|
1795
|
+
revokeSession
|
|
1796
|
+
};
|
|
1788
1797
|
}
|
|
1789
1798
|
|
|
1790
|
-
// src/components/
|
|
1791
|
-
import {
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
const
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1799
|
+
// src/components/two-factor-modal.tsx
|
|
1800
|
+
import { useState as useState10, useEffect as useEffect6 } from "react";
|
|
1801
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1802
|
+
function TwoFactorModal({ isOpen, onClose, initialEnabled, onStatusChange }) {
|
|
1803
|
+
const { api, getAccessToken, config } = useForgeConnect();
|
|
1804
|
+
const theme = config.appearance?.theme ?? "light";
|
|
1805
|
+
const [step, setStep] = useState10(initialEnabled ? "manage" : "setup");
|
|
1806
|
+
const [setupData, setSetupData] = useState10(null);
|
|
1807
|
+
const [code, setCode] = useState10("");
|
|
1808
|
+
const [loading, setLoading] = useState10(false);
|
|
1809
|
+
const [msg, setMsg] = useState10(null);
|
|
1810
|
+
const [showSecret, setShowSecret] = useState10(false);
|
|
1811
|
+
const [recoveryCodes, setRecoveryCodes] = useState10([]);
|
|
1812
|
+
useEffect6(() => {
|
|
1813
|
+
if (isOpen) {
|
|
1814
|
+
setStep(initialEnabled ? "manage" : "setup");
|
|
1815
|
+
setSetupData(null);
|
|
1816
|
+
setCode("");
|
|
1817
|
+
setMsg(null);
|
|
1818
|
+
setLoading(false);
|
|
1819
|
+
setShowSecret(false);
|
|
1820
|
+
setRecoveryCodes([]);
|
|
1821
|
+
if (!initialEnabled) {
|
|
1822
|
+
const token = getAccessToken();
|
|
1823
|
+
if (token) {
|
|
1824
|
+
setLoading(true);
|
|
1825
|
+
api.setup2FA(token).then((data) => {
|
|
1826
|
+
setSetupData(data);
|
|
1827
|
+
setRecoveryCodes(data.recoveryCodes);
|
|
1828
|
+
}).catch((err) => {
|
|
1829
|
+
setMsg({ text: err instanceof Error ? err.message : "Could not set up 2FA.", ok: false });
|
|
1830
|
+
}).finally(() => setLoading(false));
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
}, [isOpen, initialEnabled, api, getAccessToken]);
|
|
1835
|
+
const handleEnable = async () => {
|
|
1836
|
+
const token = getAccessToken();
|
|
1837
|
+
if (!token || !code) return;
|
|
1838
|
+
setLoading(true);
|
|
1839
|
+
setMsg(null);
|
|
1840
|
+
try {
|
|
1841
|
+
await api.enable2FA(token, code);
|
|
1842
|
+
onStatusChange(true);
|
|
1843
|
+
setStep("recovery-codes");
|
|
1844
|
+
setCode("");
|
|
1845
|
+
} catch (err) {
|
|
1846
|
+
setMsg({ text: err instanceof Error ? err.message : "Invalid code.", ok: false });
|
|
1847
|
+
} finally {
|
|
1848
|
+
setLoading(false);
|
|
1815
1849
|
}
|
|
1816
1850
|
};
|
|
1817
|
-
const
|
|
1818
|
-
|
|
1819
|
-
|
|
1851
|
+
const handleDisable = async () => {
|
|
1852
|
+
const token = getAccessToken();
|
|
1853
|
+
if (!token || !code) return;
|
|
1854
|
+
setLoading(true);
|
|
1855
|
+
setMsg(null);
|
|
1856
|
+
try {
|
|
1857
|
+
await api.disable2FA(token, code);
|
|
1858
|
+
onStatusChange(false);
|
|
1859
|
+
setCode("");
|
|
1860
|
+
onClose();
|
|
1861
|
+
} catch (err) {
|
|
1862
|
+
setMsg({ text: err instanceof Error ? err.message : "Invalid code.", ok: false });
|
|
1863
|
+
} finally {
|
|
1864
|
+
setLoading(false);
|
|
1820
1865
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1866
|
+
};
|
|
1867
|
+
const handleRegenerate = async () => {
|
|
1868
|
+
const token = getAccessToken();
|
|
1869
|
+
if (!token || !code) return;
|
|
1870
|
+
setLoading(true);
|
|
1871
|
+
setMsg(null);
|
|
1872
|
+
try {
|
|
1873
|
+
const { recoveryCodes: codes } = await api.regenerateRecoveryCodes(token, code);
|
|
1874
|
+
setRecoveryCodes(codes);
|
|
1875
|
+
setStep("recovery-codes");
|
|
1876
|
+
setCode("");
|
|
1877
|
+
} catch (err) {
|
|
1878
|
+
setMsg({ text: err instanceof Error ? err.message : "Invalid code.", ok: false });
|
|
1879
|
+
} finally {
|
|
1880
|
+
setLoading(false);
|
|
1823
1881
|
}
|
|
1824
|
-
return null;
|
|
1825
1882
|
};
|
|
1826
|
-
|
|
1883
|
+
const handleCopyAll = () => {
|
|
1884
|
+
navigator.clipboard.writeText(recoveryCodes.join("\n")).catch(() => {
|
|
1885
|
+
});
|
|
1886
|
+
};
|
|
1887
|
+
const goBack = () => {
|
|
1888
|
+
setCode("");
|
|
1889
|
+
setMsg(null);
|
|
1890
|
+
setStep("manage");
|
|
1891
|
+
};
|
|
1892
|
+
if (!isOpen) return null;
|
|
1893
|
+
return /* @__PURE__ */ jsx10(ModalOverlay, { isOpen, onClose, children: /* @__PURE__ */ jsxs9(
|
|
1827
1894
|
"div",
|
|
1828
1895
|
{
|
|
1829
1896
|
className: "fc-modal-content",
|
|
1830
|
-
style: {
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1897
|
+
style: { "--fc-accent": config.appearance?.accentColor ?? "#8b5cf6" },
|
|
1898
|
+
"data-theme": theme,
|
|
1899
|
+
children: [
|
|
1900
|
+
step === "setup" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
1901
|
+
/* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Set up 2FA" }),
|
|
1902
|
+
msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
|
|
1903
|
+
loading && !setupData && /* @__PURE__ */ jsx10("p", { className: "fc-text", children: "Loading..." }),
|
|
1904
|
+
setupData && /* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
|
|
1905
|
+
/* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Scan this QR code with your authenticator app" }),
|
|
1906
|
+
/* @__PURE__ */ jsx10("div", { style: { display: "flex", justifyContent: "center", margin: "12px 0" }, children: /* @__PURE__ */ jsx10(
|
|
1907
|
+
"img",
|
|
1908
|
+
{
|
|
1909
|
+
src: setupData.qrCodeDataUrl,
|
|
1910
|
+
alt: "TOTP QR code",
|
|
1911
|
+
style: { width: 180, height: 180, borderRadius: 12 }
|
|
1912
|
+
}
|
|
1913
|
+
) }),
|
|
1914
|
+
/* @__PURE__ */ jsxs9("div", { style: { textAlign: "center", marginBottom: 12 }, children: [
|
|
1915
|
+
/* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: () => setShowSecret(!showSecret), children: showSecret ? "Hide code" : "Can't scan? Enter manually" }),
|
|
1916
|
+
showSecret && /* @__PURE__ */ jsx10("p", { style: { margin: "8px 0 0", fontFamily: "monospace", fontSize: 12, opacity: 0.7, wordBreak: "break-all" }, children: setupData.secret })
|
|
1917
|
+
] }),
|
|
1918
|
+
/* @__PURE__ */ jsx10(
|
|
1919
|
+
"input",
|
|
1920
|
+
{
|
|
1921
|
+
className: "fc-input",
|
|
1922
|
+
type: "text",
|
|
1923
|
+
inputMode: "numeric",
|
|
1924
|
+
placeholder: "Enter 6-digit code",
|
|
1925
|
+
maxLength: 6,
|
|
1926
|
+
value: code,
|
|
1927
|
+
onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
|
|
1928
|
+
style: { textAlign: "center", letterSpacing: "0.2em" },
|
|
1929
|
+
autoFocus: true
|
|
1930
|
+
}
|
|
1931
|
+
),
|
|
1932
|
+
/* @__PURE__ */ jsx10(
|
|
1933
|
+
"button",
|
|
1934
|
+
{
|
|
1935
|
+
type: "button",
|
|
1936
|
+
className: "fc-btn fc-btn-primary",
|
|
1937
|
+
onClick: handleEnable,
|
|
1938
|
+
disabled: loading || code.length !== 6,
|
|
1939
|
+
style: { marginTop: 10 },
|
|
1940
|
+
children: loading ? "Verifying..." : "Verify & Enable"
|
|
1941
|
+
}
|
|
1942
|
+
)
|
|
1943
|
+
] })
|
|
1944
|
+
] }),
|
|
1945
|
+
step === "manage" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
1946
|
+
/* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Two-factor authentication" }),
|
|
1947
|
+
/* @__PURE__ */ jsx10("div", { style: { textAlign: "center", marginBottom: 20 }, children: /* @__PURE__ */ jsx10("span", { className: "fc-badge-verified", style: { fontSize: 12, padding: "4px 12px" }, children: "Enabled" }) }),
|
|
1948
|
+
/* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
|
|
1949
|
+
/* @__PURE__ */ jsxs9(
|
|
1950
|
+
"button",
|
|
1951
|
+
{
|
|
1952
|
+
type: "button",
|
|
1953
|
+
className: "fc-security-card",
|
|
1954
|
+
onClick: () => {
|
|
1955
|
+
setCode("");
|
|
1956
|
+
setMsg(null);
|
|
1957
|
+
setStep("confirm-regenerate");
|
|
1958
|
+
},
|
|
1959
|
+
children: [
|
|
1960
|
+
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-icon", children: /* @__PURE__ */ jsxs9("svg", { viewBox: "0 0 20 20", fill: "none", children: [
|
|
1961
|
+
/* @__PURE__ */ jsx10("path", { d: "M3 10a7 7 0 0 1 7-7m0 14a7 7 0 0 1-7-7m14 0a7 7 0 0 1-7 7m0-14a7 7 0 0 1 7 7", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
1962
|
+
/* @__PURE__ */ jsx10("path", { d: "M14 3l-1 3h3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1963
|
+
] }) }),
|
|
1964
|
+
/* @__PURE__ */ jsxs9("span", { className: "fc-security-card-info", children: [
|
|
1965
|
+
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-label", children: "Regenerate recovery codes" }),
|
|
1966
|
+
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-detail", children: "Get new backup codes" })
|
|
1967
|
+
] }),
|
|
1968
|
+
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-chevron", children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M6 4l4 4-4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
|
|
1969
|
+
]
|
|
1970
|
+
}
|
|
1971
|
+
),
|
|
1972
|
+
/* @__PURE__ */ jsxs9(
|
|
1973
|
+
"button",
|
|
1974
|
+
{
|
|
1975
|
+
type: "button",
|
|
1976
|
+
className: "fc-security-card",
|
|
1977
|
+
onClick: () => {
|
|
1978
|
+
setCode("");
|
|
1979
|
+
setMsg(null);
|
|
1980
|
+
setStep("confirm-disable");
|
|
1981
|
+
},
|
|
1982
|
+
children: [
|
|
1983
|
+
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-icon", style: { color: "#dc2626" }, children: /* @__PURE__ */ jsxs9("svg", { viewBox: "0 0 20 20", fill: "none", children: [
|
|
1984
|
+
/* @__PURE__ */ jsx10("path", { d: "M10 2l6 3v4c0 4.42-2.56 8.22-6 9.5C6.56 17.22 4 13.42 4 9V5l6-3z", stroke: "currentColor", strokeWidth: "1.5", strokeLinejoin: "round" }),
|
|
1985
|
+
/* @__PURE__ */ jsx10("path", { d: "M7.5 7.5l5 5M12.5 7.5l-5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
1986
|
+
] }) }),
|
|
1987
|
+
/* @__PURE__ */ jsxs9("span", { className: "fc-security-card-info", children: [
|
|
1988
|
+
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-label", style: { color: "#dc2626" }, children: "Disable 2FA" }),
|
|
1989
|
+
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-detail", children: "Remove two-factor authentication" })
|
|
1990
|
+
] }),
|
|
1991
|
+
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-chevron", children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M6 4l4 4-4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
|
|
1992
|
+
]
|
|
1993
|
+
}
|
|
1994
|
+
)
|
|
1995
|
+
] })
|
|
1996
|
+
] }),
|
|
1997
|
+
step === "confirm-regenerate" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
1998
|
+
/* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Regenerate recovery codes" }),
|
|
1999
|
+
/* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Enter your 2FA code to generate new recovery codes. Your old codes will stop working." }),
|
|
2000
|
+
msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
|
|
2001
|
+
/* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
|
|
2002
|
+
/* @__PURE__ */ jsx10(
|
|
2003
|
+
"input",
|
|
2004
|
+
{
|
|
2005
|
+
className: "fc-input",
|
|
2006
|
+
type: "text",
|
|
2007
|
+
inputMode: "numeric",
|
|
2008
|
+
placeholder: "Enter 6-digit code",
|
|
2009
|
+
maxLength: 6,
|
|
2010
|
+
value: code,
|
|
2011
|
+
onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
|
|
2012
|
+
style: { textAlign: "center", letterSpacing: "0.2em" },
|
|
2013
|
+
autoFocus: true
|
|
2014
|
+
}
|
|
2015
|
+
),
|
|
2016
|
+
/* @__PURE__ */ jsx10(
|
|
2017
|
+
"button",
|
|
2018
|
+
{
|
|
2019
|
+
type: "button",
|
|
2020
|
+
className: "fc-btn fc-btn-primary",
|
|
2021
|
+
onClick: handleRegenerate,
|
|
2022
|
+
disabled: loading || code.length !== 6,
|
|
2023
|
+
style: { marginTop: 10 },
|
|
2024
|
+
children: loading ? "Generating..." : "Regenerate codes"
|
|
2025
|
+
}
|
|
2026
|
+
),
|
|
2027
|
+
/* @__PURE__ */ jsx10("div", { className: "fc-switch", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: goBack, children: "Back" }) })
|
|
2028
|
+
] })
|
|
2029
|
+
] }),
|
|
2030
|
+
step === "confirm-disable" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2031
|
+
/* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Disable 2FA" }),
|
|
2032
|
+
/* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Enter your 2FA code to disable two-factor authentication." }),
|
|
2033
|
+
msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
|
|
2034
|
+
/* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
|
|
2035
|
+
/* @__PURE__ */ jsx10(
|
|
2036
|
+
"input",
|
|
2037
|
+
{
|
|
2038
|
+
className: "fc-input",
|
|
2039
|
+
type: "text",
|
|
2040
|
+
inputMode: "numeric",
|
|
2041
|
+
placeholder: "Enter 6-digit code",
|
|
2042
|
+
maxLength: 6,
|
|
2043
|
+
value: code,
|
|
2044
|
+
onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
|
|
2045
|
+
style: { textAlign: "center", letterSpacing: "0.2em" },
|
|
2046
|
+
autoFocus: true
|
|
2047
|
+
}
|
|
2048
|
+
),
|
|
2049
|
+
/* @__PURE__ */ jsx10(
|
|
2050
|
+
"button",
|
|
2051
|
+
{
|
|
2052
|
+
type: "button",
|
|
2053
|
+
className: "fc-btn fc-btn-secondary",
|
|
2054
|
+
onClick: handleDisable,
|
|
2055
|
+
disabled: loading || code.length !== 6,
|
|
2056
|
+
style: { marginTop: 10, background: "rgba(220, 38, 38, 0.1)", color: "#dc2626", borderColor: "rgba(220, 38, 38, 0.2)" },
|
|
2057
|
+
children: loading ? "Disabling..." : "Disable 2FA"
|
|
2058
|
+
}
|
|
2059
|
+
),
|
|
2060
|
+
/* @__PURE__ */ jsx10("div", { className: "fc-switch", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: goBack, children: "Back" }) })
|
|
2061
|
+
] })
|
|
2062
|
+
] }),
|
|
2063
|
+
step === "recovery-codes" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2064
|
+
/* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Save your recovery codes" }),
|
|
2065
|
+
/* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Store these codes somewhere safe. Each code can only be used once." }),
|
|
2066
|
+
/* @__PURE__ */ jsx10("div", { className: "fc-recovery-grid", children: recoveryCodes.map((c, i) => /* @__PURE__ */ jsx10("div", { className: "fc-recovery-code", children: c }, i)) }),
|
|
2067
|
+
/* @__PURE__ */ jsx10("div", { style: { textAlign: "center", margin: "8px 0 12px" }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: handleCopyAll, children: "Copy all" }) }),
|
|
2068
|
+
/* @__PURE__ */ jsx10("button", { type: "button", className: "fc-btn fc-btn-primary", onClick: onClose, children: "Done" })
|
|
1844
2069
|
] })
|
|
1845
|
-
]
|
|
2070
|
+
]
|
|
1846
2071
|
}
|
|
1847
2072
|
) });
|
|
1848
2073
|
}
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
var WalletIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
|
|
1860
|
-
/* @__PURE__ */ jsx9("rect", { x: "2", y: "5", width: "16", height: "11", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1861
|
-
/* @__PURE__ */ jsx9("rect", { x: "13", y: "9", width: "5", height: "3", rx: "1", stroke: "currentColor", strokeWidth: "1.5" })
|
|
1862
|
-
] }) });
|
|
1863
|
-
var PasskeyIcon = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsxs8("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon", children: [
|
|
1864
|
-
/* @__PURE__ */ jsx9("circle", { cx: "8", cy: "7", r: "3", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1865
|
-
/* @__PURE__ */ jsx9("path", { d: "M13 13.5a5 5 0 0 0-10 0", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
1866
|
-
/* @__PURE__ */ jsx9("path", { d: "M15 10v4m0 0l-1.5-1m1.5 1l1.5-1", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1867
|
-
] }) });
|
|
1868
|
-
var LoadingSpinner = () => /* @__PURE__ */ jsx9("div", { className: "fc-method-icon-wrap", children: /* @__PURE__ */ jsx9("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", className: "fc-method-icon fc-spin", children: /* @__PURE__ */ jsx9("path", { d: "M10 2a8 8 0 0 1 8 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) });
|
|
1869
|
-
function MethodSelect() {
|
|
1870
|
-
const { setModalStep, loginWithPasskey, config } = useForgeConnect();
|
|
1871
|
-
const methods = resolveLoginMethods(config);
|
|
1872
|
-
const [passkeyLoading, setPasskeyLoading] = useState6(false);
|
|
1873
|
-
const [passkeyError, setPasskeyError] = useState6("");
|
|
1874
|
-
const handlePasskey = async () => {
|
|
1875
|
-
setPasskeyLoading(true);
|
|
1876
|
-
setPasskeyError("");
|
|
1877
|
-
try {
|
|
1878
|
-
await loginWithPasskey();
|
|
1879
|
-
} catch (err) {
|
|
1880
|
-
setPasskeyError(err instanceof Error ? err.message : "Passkey authentication failed.");
|
|
1881
|
-
} finally {
|
|
1882
|
-
setPasskeyLoading(false);
|
|
1883
|
-
}
|
|
1884
|
-
};
|
|
1885
|
-
const elements = [];
|
|
1886
|
-
let i = 0;
|
|
1887
|
-
while (i < methods.length) {
|
|
1888
|
-
const method = methods[i];
|
|
1889
|
-
if (isOAuthMethod(method)) {
|
|
1890
|
-
const oauthGroup = [];
|
|
1891
|
-
while (i < methods.length && isOAuthMethod(methods[i])) {
|
|
1892
|
-
oauthGroup.push(methods[i]);
|
|
1893
|
-
i++;
|
|
1894
|
-
}
|
|
1895
|
-
if (elements.length > 0) {
|
|
1896
|
-
elements.push(/* @__PURE__ */ jsx9("div", { className: "fc-divider", children: /* @__PURE__ */ jsx9("span", { children: "or" }) }, `div-before-${oauthGroup[0]}`));
|
|
1897
|
-
}
|
|
1898
|
-
elements.push(
|
|
1899
|
-
/* @__PURE__ */ jsx9("div", { className: "fc-oauth-group", children: oauthGroup.map((p) => /* @__PURE__ */ jsx9(OAuthButton, { provider: p }, p)) }, `oauth-${oauthGroup.join("-")}`)
|
|
1900
|
-
);
|
|
1901
|
-
} else {
|
|
1902
|
-
if (elements.length > 0) {
|
|
1903
|
-
const prev = methods[i - 1];
|
|
1904
|
-
if (prev && isOAuthMethod(prev)) {
|
|
1905
|
-
elements.push(/* @__PURE__ */ jsx9("div", { className: "fc-divider", children: /* @__PURE__ */ jsx9("span", { children: "or" }) }, `div-after-${prev}`));
|
|
1906
|
-
}
|
|
1907
|
-
}
|
|
1908
|
-
switch (method) {
|
|
1909
|
-
case "email":
|
|
1910
|
-
elements.push(
|
|
1911
|
-
/* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("email-login"), children: [
|
|
1912
|
-
/* @__PURE__ */ jsx9(EmailIcon, {}),
|
|
1913
|
-
" ",
|
|
1914
|
-
/* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Password" })
|
|
1915
|
-
] }, "email")
|
|
1916
|
-
);
|
|
1917
|
-
break;
|
|
1918
|
-
case "otp":
|
|
1919
|
-
elements.push(
|
|
1920
|
-
/* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("email-otp"), children: [
|
|
1921
|
-
/* @__PURE__ */ jsx9(OtpIcon, {}),
|
|
1922
|
-
" ",
|
|
1923
|
-
/* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Magic code" })
|
|
1924
|
-
] }, "otp")
|
|
1925
|
-
);
|
|
1926
|
-
break;
|
|
1927
|
-
case "wallet":
|
|
1928
|
-
elements.push(
|
|
1929
|
-
/* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: () => setModalStep("wallet-connect"), children: [
|
|
1930
|
-
/* @__PURE__ */ jsx9(WalletIcon, {}),
|
|
1931
|
-
" ",
|
|
1932
|
-
/* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: "Wallet" })
|
|
1933
|
-
] }, "wallet")
|
|
1934
|
-
);
|
|
1935
|
-
break;
|
|
1936
|
-
case "passkey":
|
|
1937
|
-
elements.push(
|
|
1938
|
-
/* @__PURE__ */ jsxs8("button", { type: "button", className: "fc-btn fc-btn-method", onClick: handlePasskey, disabled: passkeyLoading, children: [
|
|
1939
|
-
passkeyLoading ? /* @__PURE__ */ jsx9(LoadingSpinner, {}) : /* @__PURE__ */ jsx9(PasskeyIcon, {}),
|
|
1940
|
-
/* @__PURE__ */ jsx9("span", { className: "fc-btn-name", children: passkeyLoading ? "Waiting for passkey..." : "Passkey" })
|
|
1941
|
-
] }, "passkey")
|
|
1942
|
-
);
|
|
1943
|
-
break;
|
|
1944
|
-
}
|
|
1945
|
-
i++;
|
|
1946
|
-
}
|
|
2074
|
+
|
|
2075
|
+
// src/components/passkeys-modal.tsx
|
|
2076
|
+
import { useState as useState11, useEffect as useEffect7, useCallback as useCallback6 } from "react";
|
|
2077
|
+
|
|
2078
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/bufferToBase64URLString.js
|
|
2079
|
+
function bufferToBase64URLString(buffer) {
|
|
2080
|
+
const bytes = new Uint8Array(buffer);
|
|
2081
|
+
let str = "";
|
|
2082
|
+
for (const charCode of bytes) {
|
|
2083
|
+
str += String.fromCharCode(charCode);
|
|
1947
2084
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
elements
|
|
1951
|
-
] });
|
|
1952
|
-
}
|
|
1953
|
-
function OAuthLoadingView() {
|
|
1954
|
-
const { setModalStep } = useForgeConnect();
|
|
1955
|
-
return /* @__PURE__ */ jsxs8("div", { className: "fc-success", children: [
|
|
1956
|
-
/* @__PURE__ */ jsx9("div", { className: "fc-success-icon", children: /* @__PURE__ */ jsx9("svg", { width: "52", height: "52", viewBox: "0 0 52 52", className: "fc-spin", children: /* @__PURE__ */ jsx9("circle", { cx: "26", cy: "26", r: "24", fill: "none", stroke: "var(--fc-accent, #8b5cf6)", strokeWidth: "3", strokeLinecap: "round", strokeDasharray: "100", strokeDashoffset: "30" }) }) }),
|
|
1957
|
-
/* @__PURE__ */ jsx9("p", { className: "fc-success-text", children: "Completing sign in..." }),
|
|
1958
|
-
/* @__PURE__ */ jsx9("button", { type: "button", className: "fc-btn fc-btn-secondary", style: { marginTop: "16px" }, onClick: () => setModalStep("method-select"), children: "Cancel" })
|
|
1959
|
-
] });
|
|
1960
|
-
}
|
|
1961
|
-
function SuccessView() {
|
|
1962
|
-
return /* @__PURE__ */ jsxs8("div", { className: "fc-success", children: [
|
|
1963
|
-
/* @__PURE__ */ jsx9("div", { className: "fc-success-icon", children: /* @__PURE__ */ jsxs8("svg", { viewBox: "0 0 52 52", className: "fc-success-check", children: [
|
|
1964
|
-
/* @__PURE__ */ jsx9("circle", { className: "fc-success-circle", cx: "26", cy: "26", r: "24", fill: "none" }),
|
|
1965
|
-
/* @__PURE__ */ jsx9("path", { className: "fc-success-tick", fill: "none", d: "M15 26l7.5 7.5L37 19" })
|
|
1966
|
-
] }) }),
|
|
1967
|
-
/* @__PURE__ */ jsx9("p", { className: "fc-success-text", children: "You're in" })
|
|
1968
|
-
] });
|
|
2085
|
+
const base64String = btoa(str);
|
|
2086
|
+
return base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1969
2087
|
}
|
|
1970
2088
|
|
|
1971
|
-
//
|
|
1972
|
-
|
|
2089
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/base64URLStringToBuffer.js
|
|
2090
|
+
function base64URLStringToBuffer(base64URLString) {
|
|
2091
|
+
const base64 = base64URLString.replace(/-/g, "+").replace(/_/g, "/");
|
|
2092
|
+
const padLength = (4 - base64.length % 4) % 4;
|
|
2093
|
+
const padded = base64.padEnd(base64.length + padLength, "=");
|
|
2094
|
+
const binary = atob(padded);
|
|
2095
|
+
const buffer = new ArrayBuffer(binary.length);
|
|
2096
|
+
const bytes = new Uint8Array(buffer);
|
|
2097
|
+
for (let i = 0; i < binary.length; i++) {
|
|
2098
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2099
|
+
}
|
|
2100
|
+
return buffer;
|
|
2101
|
+
}
|
|
1973
2102
|
|
|
1974
|
-
//
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
return api.updateMe(token, data);
|
|
1986
|
-
},
|
|
1987
|
-
[api, getAccessToken]
|
|
1988
|
-
);
|
|
1989
|
-
const fetchAuthMethods = useCallback3(async () => {
|
|
1990
|
-
const token = getAccessToken();
|
|
1991
|
-
if (!token) throw new Error("Please sign in to continue.");
|
|
1992
|
-
setLoading(true);
|
|
1993
|
-
try {
|
|
1994
|
-
const methods = await api.getAuthMethods(token);
|
|
1995
|
-
setAuthMethods(methods);
|
|
1996
|
-
return methods;
|
|
1997
|
-
} finally {
|
|
1998
|
-
setLoading(false);
|
|
1999
|
-
}
|
|
2000
|
-
}, [api, getAccessToken]);
|
|
2001
|
-
const linkAuthMethod = useCallback3(
|
|
2002
|
-
async (data) => {
|
|
2003
|
-
const token = getAccessToken();
|
|
2004
|
-
if (!token) throw new Error("Please sign in to continue.");
|
|
2005
|
-
await api.linkAuthMethod(token, data);
|
|
2006
|
-
await fetchAuthMethods();
|
|
2007
|
-
},
|
|
2008
|
-
[api, getAccessToken, fetchAuthMethods]
|
|
2009
|
-
);
|
|
2010
|
-
const linkOtpSend = useCallback3(
|
|
2011
|
-
async (email) => {
|
|
2012
|
-
const token = getAccessToken();
|
|
2013
|
-
if (!token) throw new Error("Please sign in to continue.");
|
|
2014
|
-
await api.linkOtpSend(token, email);
|
|
2015
|
-
},
|
|
2016
|
-
[api, getAccessToken]
|
|
2017
|
-
);
|
|
2018
|
-
const linkOtpVerify = useCallback3(
|
|
2019
|
-
async (email, code) => {
|
|
2020
|
-
const token = getAccessToken();
|
|
2021
|
-
if (!token) throw new Error("Please sign in to continue.");
|
|
2022
|
-
await api.linkOtpVerify(token, email, code);
|
|
2023
|
-
await fetchAuthMethods();
|
|
2024
|
-
},
|
|
2025
|
-
[api, getAccessToken, fetchAuthMethods]
|
|
2026
|
-
);
|
|
2027
|
-
const unlinkAuthMethod = useCallback3(
|
|
2028
|
-
async (id) => {
|
|
2029
|
-
const token = getAccessToken();
|
|
2030
|
-
if (!token) throw new Error("Please sign in to continue.");
|
|
2031
|
-
await api.unlinkAuthMethod(token, id);
|
|
2032
|
-
await fetchAuthMethods();
|
|
2033
|
-
},
|
|
2034
|
-
[api, getAccessToken, fetchAuthMethods]
|
|
2035
|
-
);
|
|
2036
|
-
const linkOAuth = useCallback3(
|
|
2037
|
-
async (provider) => {
|
|
2038
|
-
const token = getAccessToken();
|
|
2039
|
-
if (!token) throw new Error("Please sign in to continue.");
|
|
2040
|
-
const { intentToken } = await api.createLinkIntent(token);
|
|
2041
|
-
const redirectUri = encodeURIComponent(window.location.origin + "/fc-oauth-callback");
|
|
2042
|
-
const url = `${config.apiUrl}/auth/oauth/${provider}/link?intent=${encodeURIComponent(intentToken)}&redirect_uri=${redirectUri}`;
|
|
2043
|
-
const width = 500;
|
|
2044
|
-
const height = 600;
|
|
2045
|
-
const left = window.screenX + (window.innerWidth - width) / 2;
|
|
2046
|
-
const top = window.screenY + (window.innerHeight - height) / 2;
|
|
2047
|
-
window.open(url, "fc_oauth_link", `width=${width},height=${height},left=${left},top=${top}`);
|
|
2048
|
-
pendingRefreshRef.current = true;
|
|
2049
|
-
},
|
|
2050
|
-
[api, config.apiUrl, getAccessToken]
|
|
2051
|
-
);
|
|
2052
|
-
useEffect5(() => {
|
|
2053
|
-
const handleMessage = (event) => {
|
|
2054
|
-
if (event.origin !== window.location.origin) return;
|
|
2055
|
-
if (event.data?.type === "fc_oauth_link_success" && pendingRefreshRef.current) {
|
|
2056
|
-
pendingRefreshRef.current = false;
|
|
2057
|
-
fetchAuthMethods().catch(() => {
|
|
2058
|
-
});
|
|
2059
|
-
}
|
|
2060
|
-
};
|
|
2061
|
-
window.addEventListener("message", handleMessage);
|
|
2062
|
-
return () => window.removeEventListener("message", handleMessage);
|
|
2063
|
-
}, [fetchAuthMethods]);
|
|
2103
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthn.js
|
|
2104
|
+
function browserSupportsWebAuthn() {
|
|
2105
|
+
return _browserSupportsWebAuthnInternals.stubThis(globalThis?.PublicKeyCredential !== void 0 && typeof globalThis.PublicKeyCredential === "function");
|
|
2106
|
+
}
|
|
2107
|
+
var _browserSupportsWebAuthnInternals = {
|
|
2108
|
+
stubThis: (value) => value
|
|
2109
|
+
};
|
|
2110
|
+
|
|
2111
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/toPublicKeyCredentialDescriptor.js
|
|
2112
|
+
function toPublicKeyCredentialDescriptor(descriptor) {
|
|
2113
|
+
const { id } = descriptor;
|
|
2064
2114
|
return {
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
unlinkAuthMethod,
|
|
2074
|
-
linkOAuth
|
|
2115
|
+
...descriptor,
|
|
2116
|
+
id: base64URLStringToBuffer(id),
|
|
2117
|
+
/**
|
|
2118
|
+
* `descriptor.transports` is an array of our `AuthenticatorTransportFuture` that includes newer
|
|
2119
|
+
* transports that TypeScript's DOM lib is ignorant of. Convince TS that our list of transports
|
|
2120
|
+
* are fine to pass to WebAuthn since browsers will recognize the new value.
|
|
2121
|
+
*/
|
|
2122
|
+
transports: descriptor.transports
|
|
2075
2123
|
};
|
|
2076
2124
|
}
|
|
2077
2125
|
|
|
2078
|
-
//
|
|
2079
|
-
|
|
2126
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/isValidDomain.js
|
|
2127
|
+
function isValidDomain(hostname) {
|
|
2128
|
+
return (
|
|
2129
|
+
// Consider localhost valid as well since it's okay wrt Secure Contexts
|
|
2130
|
+
hostname === "localhost" || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname)
|
|
2131
|
+
);
|
|
2132
|
+
}
|
|
2080
2133
|
|
|
2081
|
-
//
|
|
2082
|
-
var
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2134
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/webAuthnError.js
|
|
2135
|
+
var WebAuthnError = class extends Error {
|
|
2136
|
+
constructor({ message, code, cause, name }) {
|
|
2137
|
+
super(message, { cause });
|
|
2138
|
+
Object.defineProperty(this, "code", {
|
|
2139
|
+
enumerable: true,
|
|
2140
|
+
configurable: true,
|
|
2141
|
+
writable: true,
|
|
2142
|
+
value: void 0
|
|
2143
|
+
});
|
|
2144
|
+
this.name = name ?? cause.name;
|
|
2145
|
+
this.code = code;
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
|
|
2149
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/identifyRegistrationError.js
|
|
2150
|
+
function identifyRegistrationError({ error, options }) {
|
|
2151
|
+
const { publicKey } = options;
|
|
2152
|
+
if (!publicKey) {
|
|
2153
|
+
throw Error("options was missing required publicKey property");
|
|
2154
|
+
}
|
|
2155
|
+
if (error.name === "AbortError") {
|
|
2156
|
+
if (options.signal instanceof AbortSignal) {
|
|
2157
|
+
return new WebAuthnError({
|
|
2158
|
+
message: "Registration ceremony was sent an abort signal",
|
|
2159
|
+
code: "ERROR_CEREMONY_ABORTED",
|
|
2160
|
+
cause: error
|
|
2161
|
+
});
|
|
2091
2162
|
}
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2163
|
+
} else if (error.name === "ConstraintError") {
|
|
2164
|
+
if (publicKey.authenticatorSelection?.requireResidentKey === true) {
|
|
2165
|
+
return new WebAuthnError({
|
|
2166
|
+
message: "Discoverable credentials were required but no available authenticator supported it",
|
|
2167
|
+
code: "ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",
|
|
2168
|
+
cause: error
|
|
2169
|
+
});
|
|
2170
|
+
} else if (
|
|
2171
|
+
// @ts-ignore: `mediation` doesn't yet exist on CredentialCreationOptions but it's possible as of Sept 2024
|
|
2172
|
+
options.mediation === "conditional" && publicKey.authenticatorSelection?.userVerification === "required"
|
|
2173
|
+
) {
|
|
2174
|
+
return new WebAuthnError({
|
|
2175
|
+
message: "User verification was required during automatic registration but it could not be performed",
|
|
2176
|
+
code: "ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE",
|
|
2177
|
+
cause: error
|
|
2178
|
+
});
|
|
2179
|
+
} else if (publicKey.authenticatorSelection?.userVerification === "required") {
|
|
2180
|
+
return new WebAuthnError({
|
|
2181
|
+
message: "User verification was required but no available authenticator supported it",
|
|
2182
|
+
code: "ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",
|
|
2183
|
+
cause: error
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
} else if (error.name === "InvalidStateError") {
|
|
2187
|
+
return new WebAuthnError({
|
|
2188
|
+
message: "The authenticator was previously registered",
|
|
2189
|
+
code: "ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",
|
|
2190
|
+
cause: error
|
|
2191
|
+
});
|
|
2192
|
+
} else if (error.name === "NotAllowedError") {
|
|
2193
|
+
return new WebAuthnError({
|
|
2194
|
+
message: error.message,
|
|
2195
|
+
code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
|
|
2196
|
+
cause: error
|
|
2197
|
+
});
|
|
2198
|
+
} else if (error.name === "NotSupportedError") {
|
|
2199
|
+
const validPubKeyCredParams = publicKey.pubKeyCredParams.filter((param) => param.type === "public-key");
|
|
2200
|
+
if (validPubKeyCredParams.length === 0) {
|
|
2201
|
+
return new WebAuthnError({
|
|
2202
|
+
message: 'No entry in pubKeyCredParams was of type "public-key"',
|
|
2203
|
+
code: "ERROR_MALFORMED_PUBKEYCREDPARAMS",
|
|
2204
|
+
cause: error
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
return new WebAuthnError({
|
|
2208
|
+
message: "No available authenticator supported any of the specified pubKeyCredParams algorithms",
|
|
2209
|
+
code: "ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",
|
|
2210
|
+
cause: error
|
|
2211
|
+
});
|
|
2212
|
+
} else if (error.name === "SecurityError") {
|
|
2213
|
+
const effectiveDomain = globalThis.location.hostname;
|
|
2214
|
+
if (!isValidDomain(effectiveDomain)) {
|
|
2215
|
+
return new WebAuthnError({
|
|
2216
|
+
message: `${globalThis.location.hostname} is an invalid domain`,
|
|
2217
|
+
code: "ERROR_INVALID_DOMAIN",
|
|
2218
|
+
cause: error
|
|
2219
|
+
});
|
|
2220
|
+
} else if (publicKey.rp.id !== effectiveDomain) {
|
|
2221
|
+
return new WebAuthnError({
|
|
2222
|
+
message: `The RP ID "${publicKey.rp.id}" is invalid for this domain`,
|
|
2223
|
+
code: "ERROR_INVALID_RP_ID",
|
|
2224
|
+
cause: error
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
} else if (error.name === "TypeError") {
|
|
2228
|
+
if (publicKey.user.id.byteLength < 1 || publicKey.user.id.byteLength > 64) {
|
|
2229
|
+
return new WebAuthnError({
|
|
2230
|
+
message: "User ID was not between 1 and 64 characters",
|
|
2231
|
+
code: "ERROR_INVALID_USER_ID_LENGTH",
|
|
2232
|
+
cause: error
|
|
2233
|
+
});
|
|
2095
2234
|
}
|
|
2235
|
+
} else if (error.name === "UnknownError") {
|
|
2236
|
+
return new WebAuthnError({
|
|
2237
|
+
message: "The authenticator was unable to process the specified options, or could not create a new credential",
|
|
2238
|
+
code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
|
|
2239
|
+
cause: error
|
|
2240
|
+
});
|
|
2096
2241
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2242
|
+
return error;
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/webAuthnAbortService.js
|
|
2246
|
+
var BaseWebAuthnAbortService = class {
|
|
2247
|
+
constructor() {
|
|
2248
|
+
Object.defineProperty(this, "controller", {
|
|
2249
|
+
enumerable: true,
|
|
2250
|
+
configurable: true,
|
|
2251
|
+
writable: true,
|
|
2252
|
+
value: void 0
|
|
2253
|
+
});
|
|
2100
2254
|
}
|
|
2101
|
-
|
|
2102
|
-
|
|
2255
|
+
createNewAbortSignal() {
|
|
2256
|
+
if (this.controller) {
|
|
2257
|
+
const abortError = new Error("Cancelling existing WebAuthn API call for new one");
|
|
2258
|
+
abortError.name = "AbortError";
|
|
2259
|
+
this.controller.abort(abortError);
|
|
2260
|
+
}
|
|
2261
|
+
const newController = new AbortController();
|
|
2262
|
+
this.controller = newController;
|
|
2263
|
+
return newController.signal;
|
|
2103
2264
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2265
|
+
cancelCeremony() {
|
|
2266
|
+
if (this.controller) {
|
|
2267
|
+
const abortError = new Error("Manually cancelling existing WebAuthn API call");
|
|
2268
|
+
abortError.name = "AbortError";
|
|
2269
|
+
this.controller.abort(abortError);
|
|
2270
|
+
this.controller = void 0;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
};
|
|
2274
|
+
var WebAuthnAbortService = new BaseWebAuthnAbortService();
|
|
2275
|
+
|
|
2276
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/toAuthenticatorAttachment.js
|
|
2277
|
+
var attachments = ["cross-platform", "platform"];
|
|
2278
|
+
function toAuthenticatorAttachment(attachment) {
|
|
2279
|
+
if (!attachment) {
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
if (attachments.indexOf(attachment) < 0) {
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
return attachment;
|
|
2118
2286
|
}
|
|
2119
2287
|
|
|
2120
|
-
//
|
|
2121
|
-
function
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
}, [api, getAccessToken]);
|
|
2137
|
-
const updateWallet = useCallback4(
|
|
2138
|
-
async (id, data) => {
|
|
2139
|
-
const token = getAccessToken();
|
|
2140
|
-
if (!token) throw new Error("Please sign in to continue.");
|
|
2141
|
-
await api.updateWallet(token, id, data);
|
|
2142
|
-
await fetchWallets();
|
|
2143
|
-
},
|
|
2144
|
-
[api, getAccessToken, fetchWallets]
|
|
2145
|
-
);
|
|
2146
|
-
const linkWallet = useCallback4(
|
|
2147
|
-
async (walletAddress, signMessage, chain = "solana") => {
|
|
2148
|
-
const token = getAccessToken();
|
|
2149
|
-
if (!token) throw new Error("Please sign in to continue.");
|
|
2150
|
-
const { challengeId, message: challengeMessage } = await api.walletChallenge(walletAddress, chain);
|
|
2151
|
-
const encoded = new TextEncoder().encode(challengeMessage);
|
|
2152
|
-
const signatureBytes = await signMessage(encoded);
|
|
2153
|
-
const signature = chain === "solana" ? uint8ArrayToBase58(signatureBytes) : Array.from(signatureBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2154
|
-
await api.linkAuthMethod(token, {
|
|
2155
|
-
provider: `${chain}_wallet`,
|
|
2156
|
-
challengeId,
|
|
2157
|
-
signature,
|
|
2158
|
-
walletAddress
|
|
2159
|
-
});
|
|
2160
|
-
await fetchWallets();
|
|
2288
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/methods/startRegistration.js
|
|
2289
|
+
async function startRegistration(options) {
|
|
2290
|
+
if (!options.optionsJSON && options.challenge) {
|
|
2291
|
+
console.warn("startRegistration() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
|
|
2292
|
+
options = { optionsJSON: options };
|
|
2293
|
+
}
|
|
2294
|
+
const { optionsJSON, useAutoRegister = false } = options;
|
|
2295
|
+
if (!browserSupportsWebAuthn()) {
|
|
2296
|
+
throw new Error("WebAuthn is not supported in this browser");
|
|
2297
|
+
}
|
|
2298
|
+
const publicKey = {
|
|
2299
|
+
...optionsJSON,
|
|
2300
|
+
challenge: base64URLStringToBuffer(optionsJSON.challenge),
|
|
2301
|
+
user: {
|
|
2302
|
+
...optionsJSON.user,
|
|
2303
|
+
id: base64URLStringToBuffer(optionsJSON.user.id)
|
|
2161
2304
|
},
|
|
2162
|
-
|
|
2163
|
-
);
|
|
2164
|
-
return {
|
|
2165
|
-
wallets,
|
|
2166
|
-
loading,
|
|
2167
|
-
fetchWallets,
|
|
2168
|
-
updateWallet,
|
|
2169
|
-
linkWallet
|
|
2305
|
+
excludeCredentials: optionsJSON.excludeCredentials?.map(toPublicKeyCredentialDescriptor)
|
|
2170
2306
|
};
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2307
|
+
const createOptions = {};
|
|
2308
|
+
if (useAutoRegister) {
|
|
2309
|
+
createOptions.mediation = "conditional";
|
|
2310
|
+
}
|
|
2311
|
+
createOptions.publicKey = publicKey;
|
|
2312
|
+
createOptions.signal = WebAuthnAbortService.createNewAbortSignal();
|
|
2313
|
+
let credential;
|
|
2314
|
+
try {
|
|
2315
|
+
credential = await navigator.credentials.create(createOptions);
|
|
2316
|
+
} catch (err) {
|
|
2317
|
+
throw identifyRegistrationError({ error: err, options: createOptions });
|
|
2318
|
+
}
|
|
2319
|
+
if (!credential) {
|
|
2320
|
+
throw new Error("Registration was not completed");
|
|
2321
|
+
}
|
|
2322
|
+
const { id, rawId, response, type } = credential;
|
|
2323
|
+
let transports = void 0;
|
|
2324
|
+
if (typeof response.getTransports === "function") {
|
|
2325
|
+
transports = response.getTransports();
|
|
2326
|
+
}
|
|
2327
|
+
let responsePublicKeyAlgorithm = void 0;
|
|
2328
|
+
if (typeof response.getPublicKeyAlgorithm === "function") {
|
|
2183
2329
|
try {
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
} finally {
|
|
2188
|
-
setLoading(false);
|
|
2330
|
+
responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm();
|
|
2331
|
+
} catch (error) {
|
|
2332
|
+
warnOnBrokenImplementation("getPublicKeyAlgorithm()", error);
|
|
2189
2333
|
}
|
|
2190
|
-
}
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
},
|
|
2198
|
-
[api, getAccessToken, fetchSessions]
|
|
2199
|
-
);
|
|
2200
|
-
return {
|
|
2201
|
-
sessions,
|
|
2202
|
-
loading,
|
|
2203
|
-
fetchSessions,
|
|
2204
|
-
revokeSession
|
|
2205
|
-
};
|
|
2206
|
-
}
|
|
2207
|
-
|
|
2208
|
-
// src/components/two-factor-modal.tsx
|
|
2209
|
-
import { useState as useState10, useEffect as useEffect6 } from "react";
|
|
2210
|
-
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2211
|
-
function TwoFactorModal({ isOpen, onClose, initialEnabled, onStatusChange }) {
|
|
2212
|
-
const { api, getAccessToken, config } = useForgeConnect();
|
|
2213
|
-
const theme = config.appearance?.theme ?? "light";
|
|
2214
|
-
const [step, setStep] = useState10(initialEnabled ? "manage" : "setup");
|
|
2215
|
-
const [setupData, setSetupData] = useState10(null);
|
|
2216
|
-
const [code, setCode] = useState10("");
|
|
2217
|
-
const [loading, setLoading] = useState10(false);
|
|
2218
|
-
const [msg, setMsg] = useState10(null);
|
|
2219
|
-
const [showSecret, setShowSecret] = useState10(false);
|
|
2220
|
-
const [recoveryCodes, setRecoveryCodes] = useState10([]);
|
|
2221
|
-
useEffect6(() => {
|
|
2222
|
-
if (isOpen) {
|
|
2223
|
-
setStep(initialEnabled ? "manage" : "setup");
|
|
2224
|
-
setSetupData(null);
|
|
2225
|
-
setCode("");
|
|
2226
|
-
setMsg(null);
|
|
2227
|
-
setLoading(false);
|
|
2228
|
-
setShowSecret(false);
|
|
2229
|
-
setRecoveryCodes([]);
|
|
2230
|
-
if (!initialEnabled) {
|
|
2231
|
-
const token = getAccessToken();
|
|
2232
|
-
if (token) {
|
|
2233
|
-
setLoading(true);
|
|
2234
|
-
api.setup2FA(token).then((data) => {
|
|
2235
|
-
setSetupData(data);
|
|
2236
|
-
setRecoveryCodes(data.recoveryCodes);
|
|
2237
|
-
}).catch((err) => {
|
|
2238
|
-
setMsg({ text: err instanceof Error ? err.message : "Could not set up 2FA.", ok: false });
|
|
2239
|
-
}).finally(() => setLoading(false));
|
|
2240
|
-
}
|
|
2334
|
+
}
|
|
2335
|
+
let responsePublicKey = void 0;
|
|
2336
|
+
if (typeof response.getPublicKey === "function") {
|
|
2337
|
+
try {
|
|
2338
|
+
const _publicKey = response.getPublicKey();
|
|
2339
|
+
if (_publicKey !== null) {
|
|
2340
|
+
responsePublicKey = bufferToBase64URLString(_publicKey);
|
|
2241
2341
|
}
|
|
2342
|
+
} catch (error) {
|
|
2343
|
+
warnOnBrokenImplementation("getPublicKey()", error);
|
|
2242
2344
|
}
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
if (!token || !code) return;
|
|
2247
|
-
setLoading(true);
|
|
2248
|
-
setMsg(null);
|
|
2345
|
+
}
|
|
2346
|
+
let responseAuthenticatorData;
|
|
2347
|
+
if (typeof response.getAuthenticatorData === "function") {
|
|
2249
2348
|
try {
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
setCode("");
|
|
2254
|
-
} catch (err) {
|
|
2255
|
-
setMsg({ text: err instanceof Error ? err.message : "Invalid code.", ok: false });
|
|
2256
|
-
} finally {
|
|
2257
|
-
setLoading(false);
|
|
2349
|
+
responseAuthenticatorData = bufferToBase64URLString(response.getAuthenticatorData());
|
|
2350
|
+
} catch (error) {
|
|
2351
|
+
warnOnBrokenImplementation("getAuthenticatorData()", error);
|
|
2258
2352
|
}
|
|
2353
|
+
}
|
|
2354
|
+
return {
|
|
2355
|
+
id,
|
|
2356
|
+
rawId: bufferToBase64URLString(rawId),
|
|
2357
|
+
response: {
|
|
2358
|
+
attestationObject: bufferToBase64URLString(response.attestationObject),
|
|
2359
|
+
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
|
|
2360
|
+
transports,
|
|
2361
|
+
publicKeyAlgorithm: responsePublicKeyAlgorithm,
|
|
2362
|
+
publicKey: responsePublicKey,
|
|
2363
|
+
authenticatorData: responseAuthenticatorData
|
|
2364
|
+
},
|
|
2365
|
+
type,
|
|
2366
|
+
clientExtensionResults: credential.getClientExtensionResults(),
|
|
2367
|
+
authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
|
|
2259
2368
|
};
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2369
|
+
}
|
|
2370
|
+
function warnOnBrokenImplementation(methodName, cause) {
|
|
2371
|
+
console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${methodName}. You should report this error to them.
|
|
2372
|
+
`, cause);
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthnAutofill.js
|
|
2376
|
+
function browserSupportsWebAuthnAutofill() {
|
|
2377
|
+
if (!browserSupportsWebAuthn()) {
|
|
2378
|
+
return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
|
|
2379
|
+
}
|
|
2380
|
+
const globalPublicKeyCredential = globalThis.PublicKeyCredential;
|
|
2381
|
+
if (globalPublicKeyCredential?.isConditionalMediationAvailable === void 0) {
|
|
2382
|
+
return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
|
|
2383
|
+
}
|
|
2384
|
+
return _browserSupportsWebAuthnAutofillInternals.stubThis(globalPublicKeyCredential.isConditionalMediationAvailable());
|
|
2385
|
+
}
|
|
2386
|
+
var _browserSupportsWebAuthnAutofillInternals = {
|
|
2387
|
+
stubThis: (value) => value
|
|
2388
|
+
};
|
|
2389
|
+
|
|
2390
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/helpers/identifyAuthenticationError.js
|
|
2391
|
+
function identifyAuthenticationError({ error, options }) {
|
|
2392
|
+
const { publicKey } = options;
|
|
2393
|
+
if (!publicKey) {
|
|
2394
|
+
throw Error("options was missing required publicKey property");
|
|
2395
|
+
}
|
|
2396
|
+
if (error.name === "AbortError") {
|
|
2397
|
+
if (options.signal instanceof AbortSignal) {
|
|
2398
|
+
return new WebAuthnError({
|
|
2399
|
+
message: "Authentication ceremony was sent an abort signal",
|
|
2400
|
+
code: "ERROR_CEREMONY_ABORTED",
|
|
2401
|
+
cause: error
|
|
2402
|
+
});
|
|
2274
2403
|
}
|
|
2275
|
-
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2404
|
+
} else if (error.name === "NotAllowedError") {
|
|
2405
|
+
return new WebAuthnError({
|
|
2406
|
+
message: error.message,
|
|
2407
|
+
code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
|
|
2408
|
+
cause: error
|
|
2409
|
+
});
|
|
2410
|
+
} else if (error.name === "SecurityError") {
|
|
2411
|
+
const effectiveDomain = globalThis.location.hostname;
|
|
2412
|
+
if (!isValidDomain(effectiveDomain)) {
|
|
2413
|
+
return new WebAuthnError({
|
|
2414
|
+
message: `${globalThis.location.hostname} is an invalid domain`,
|
|
2415
|
+
code: "ERROR_INVALID_DOMAIN",
|
|
2416
|
+
cause: error
|
|
2417
|
+
});
|
|
2418
|
+
} else if (publicKey.rpId !== effectiveDomain) {
|
|
2419
|
+
return new WebAuthnError({
|
|
2420
|
+
message: `The RP ID "${publicKey.rpId}" is invalid for this domain`,
|
|
2421
|
+
code: "ERROR_INVALID_RP_ID",
|
|
2422
|
+
cause: error
|
|
2423
|
+
});
|
|
2290
2424
|
}
|
|
2291
|
-
}
|
|
2292
|
-
|
|
2293
|
-
|
|
2425
|
+
} else if (error.name === "UnknownError") {
|
|
2426
|
+
return new WebAuthnError({
|
|
2427
|
+
message: "The authenticator was unable to process the specified options, or could not create a new assertion signature",
|
|
2428
|
+
code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
|
|
2429
|
+
cause: error
|
|
2294
2430
|
});
|
|
2431
|
+
}
|
|
2432
|
+
return error;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
// ../../node_modules/.pnpm/@simplewebauthn+browser@13.2.2/node_modules/@simplewebauthn/browser/esm/methods/startAuthentication.js
|
|
2436
|
+
async function startAuthentication(options) {
|
|
2437
|
+
if (!options.optionsJSON && options.challenge) {
|
|
2438
|
+
console.warn("startAuthentication() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
|
|
2439
|
+
options = { optionsJSON: options };
|
|
2440
|
+
}
|
|
2441
|
+
const { optionsJSON, useBrowserAutofill = false, verifyBrowserAutofillInput = true } = options;
|
|
2442
|
+
if (!browserSupportsWebAuthn()) {
|
|
2443
|
+
throw new Error("WebAuthn is not supported in this browser");
|
|
2444
|
+
}
|
|
2445
|
+
let allowCredentials;
|
|
2446
|
+
if (optionsJSON.allowCredentials?.length !== 0) {
|
|
2447
|
+
allowCredentials = optionsJSON.allowCredentials?.map(toPublicKeyCredentialDescriptor);
|
|
2448
|
+
}
|
|
2449
|
+
const publicKey = {
|
|
2450
|
+
...optionsJSON,
|
|
2451
|
+
challenge: base64URLStringToBuffer(optionsJSON.challenge),
|
|
2452
|
+
allowCredentials
|
|
2295
2453
|
};
|
|
2296
|
-
const
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
};
|
|
2301
|
-
if (!isOpen) return null;
|
|
2302
|
-
return /* @__PURE__ */ jsx10(ModalOverlay, { isOpen, onClose, children: /* @__PURE__ */ jsxs9(
|
|
2303
|
-
"div",
|
|
2304
|
-
{
|
|
2305
|
-
className: "fc-modal-content",
|
|
2306
|
-
style: { "--fc-accent": config.appearance?.accentColor ?? "#8b5cf6" },
|
|
2307
|
-
"data-theme": theme,
|
|
2308
|
-
children: [
|
|
2309
|
-
step === "setup" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2310
|
-
/* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Set up 2FA" }),
|
|
2311
|
-
msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
|
|
2312
|
-
loading && !setupData && /* @__PURE__ */ jsx10("p", { className: "fc-text", children: "Loading..." }),
|
|
2313
|
-
setupData && /* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
|
|
2314
|
-
/* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Scan this QR code with your authenticator app" }),
|
|
2315
|
-
/* @__PURE__ */ jsx10("div", { style: { display: "flex", justifyContent: "center", margin: "12px 0" }, children: /* @__PURE__ */ jsx10(
|
|
2316
|
-
"img",
|
|
2317
|
-
{
|
|
2318
|
-
src: setupData.qrCodeDataUrl,
|
|
2319
|
-
alt: "TOTP QR code",
|
|
2320
|
-
style: { width: 180, height: 180, borderRadius: 12 }
|
|
2321
|
-
}
|
|
2322
|
-
) }),
|
|
2323
|
-
/* @__PURE__ */ jsxs9("div", { style: { textAlign: "center", marginBottom: 12 }, children: [
|
|
2324
|
-
/* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: () => setShowSecret(!showSecret), children: showSecret ? "Hide code" : "Can't scan? Enter manually" }),
|
|
2325
|
-
showSecret && /* @__PURE__ */ jsx10("p", { style: { margin: "8px 0 0", fontFamily: "monospace", fontSize: 12, opacity: 0.7, wordBreak: "break-all" }, children: setupData.secret })
|
|
2326
|
-
] }),
|
|
2327
|
-
/* @__PURE__ */ jsx10(
|
|
2328
|
-
"input",
|
|
2329
|
-
{
|
|
2330
|
-
className: "fc-input",
|
|
2331
|
-
type: "text",
|
|
2332
|
-
inputMode: "numeric",
|
|
2333
|
-
placeholder: "Enter 6-digit code",
|
|
2334
|
-
maxLength: 6,
|
|
2335
|
-
value: code,
|
|
2336
|
-
onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
|
|
2337
|
-
style: { textAlign: "center", letterSpacing: "0.2em" },
|
|
2338
|
-
autoFocus: true
|
|
2339
|
-
}
|
|
2340
|
-
),
|
|
2341
|
-
/* @__PURE__ */ jsx10(
|
|
2342
|
-
"button",
|
|
2343
|
-
{
|
|
2344
|
-
type: "button",
|
|
2345
|
-
className: "fc-btn fc-btn-primary",
|
|
2346
|
-
onClick: handleEnable,
|
|
2347
|
-
disabled: loading || code.length !== 6,
|
|
2348
|
-
style: { marginTop: 10 },
|
|
2349
|
-
children: loading ? "Verifying..." : "Verify & Enable"
|
|
2350
|
-
}
|
|
2351
|
-
)
|
|
2352
|
-
] })
|
|
2353
|
-
] }),
|
|
2354
|
-
step === "manage" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2355
|
-
/* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Two-factor authentication" }),
|
|
2356
|
-
/* @__PURE__ */ jsx10("div", { style: { textAlign: "center", marginBottom: 20 }, children: /* @__PURE__ */ jsx10("span", { className: "fc-badge-verified", style: { fontSize: 12, padding: "4px 12px" }, children: "Enabled" }) }),
|
|
2357
|
-
/* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
|
|
2358
|
-
/* @__PURE__ */ jsxs9(
|
|
2359
|
-
"button",
|
|
2360
|
-
{
|
|
2361
|
-
type: "button",
|
|
2362
|
-
className: "fc-security-card",
|
|
2363
|
-
onClick: () => {
|
|
2364
|
-
setCode("");
|
|
2365
|
-
setMsg(null);
|
|
2366
|
-
setStep("confirm-regenerate");
|
|
2367
|
-
},
|
|
2368
|
-
children: [
|
|
2369
|
-
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-icon", children: /* @__PURE__ */ jsxs9("svg", { viewBox: "0 0 20 20", fill: "none", children: [
|
|
2370
|
-
/* @__PURE__ */ jsx10("path", { d: "M3 10a7 7 0 0 1 7-7m0 14a7 7 0 0 1-7-7m14 0a7 7 0 0 1-7 7m0-14a7 7 0 0 1 7 7", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
2371
|
-
/* @__PURE__ */ jsx10("path", { d: "M14 3l-1 3h3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
2372
|
-
] }) }),
|
|
2373
|
-
/* @__PURE__ */ jsxs9("span", { className: "fc-security-card-info", children: [
|
|
2374
|
-
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-label", children: "Regenerate recovery codes" }),
|
|
2375
|
-
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-detail", children: "Get new backup codes" })
|
|
2376
|
-
] }),
|
|
2377
|
-
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-chevron", children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M6 4l4 4-4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
|
|
2378
|
-
]
|
|
2379
|
-
}
|
|
2380
|
-
),
|
|
2381
|
-
/* @__PURE__ */ jsxs9(
|
|
2382
|
-
"button",
|
|
2383
|
-
{
|
|
2384
|
-
type: "button",
|
|
2385
|
-
className: "fc-security-card",
|
|
2386
|
-
onClick: () => {
|
|
2387
|
-
setCode("");
|
|
2388
|
-
setMsg(null);
|
|
2389
|
-
setStep("confirm-disable");
|
|
2390
|
-
},
|
|
2391
|
-
children: [
|
|
2392
|
-
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-icon", style: { color: "#dc2626" }, children: /* @__PURE__ */ jsxs9("svg", { viewBox: "0 0 20 20", fill: "none", children: [
|
|
2393
|
-
/* @__PURE__ */ jsx10("path", { d: "M10 2l6 3v4c0 4.42-2.56 8.22-6 9.5C6.56 17.22 4 13.42 4 9V5l6-3z", stroke: "currentColor", strokeWidth: "1.5", strokeLinejoin: "round" }),
|
|
2394
|
-
/* @__PURE__ */ jsx10("path", { d: "M7.5 7.5l5 5M12.5 7.5l-5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
2395
|
-
] }) }),
|
|
2396
|
-
/* @__PURE__ */ jsxs9("span", { className: "fc-security-card-info", children: [
|
|
2397
|
-
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-label", style: { color: "#dc2626" }, children: "Disable 2FA" }),
|
|
2398
|
-
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-detail", children: "Remove two-factor authentication" })
|
|
2399
|
-
] }),
|
|
2400
|
-
/* @__PURE__ */ jsx10("span", { className: "fc-security-card-chevron", children: /* @__PURE__ */ jsx10("svg", { viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M6 4l4 4-4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
|
|
2401
|
-
]
|
|
2402
|
-
}
|
|
2403
|
-
)
|
|
2404
|
-
] })
|
|
2405
|
-
] }),
|
|
2406
|
-
step === "confirm-regenerate" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2407
|
-
/* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Regenerate recovery codes" }),
|
|
2408
|
-
/* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Enter your 2FA code to generate new recovery codes. Your old codes will stop working." }),
|
|
2409
|
-
msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
|
|
2410
|
-
/* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
|
|
2411
|
-
/* @__PURE__ */ jsx10(
|
|
2412
|
-
"input",
|
|
2413
|
-
{
|
|
2414
|
-
className: "fc-input",
|
|
2415
|
-
type: "text",
|
|
2416
|
-
inputMode: "numeric",
|
|
2417
|
-
placeholder: "Enter 6-digit code",
|
|
2418
|
-
maxLength: 6,
|
|
2419
|
-
value: code,
|
|
2420
|
-
onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
|
|
2421
|
-
style: { textAlign: "center", letterSpacing: "0.2em" },
|
|
2422
|
-
autoFocus: true
|
|
2423
|
-
}
|
|
2424
|
-
),
|
|
2425
|
-
/* @__PURE__ */ jsx10(
|
|
2426
|
-
"button",
|
|
2427
|
-
{
|
|
2428
|
-
type: "button",
|
|
2429
|
-
className: "fc-btn fc-btn-primary",
|
|
2430
|
-
onClick: handleRegenerate,
|
|
2431
|
-
disabled: loading || code.length !== 6,
|
|
2432
|
-
style: { marginTop: 10 },
|
|
2433
|
-
children: loading ? "Generating..." : "Regenerate codes"
|
|
2434
|
-
}
|
|
2435
|
-
),
|
|
2436
|
-
/* @__PURE__ */ jsx10("div", { className: "fc-switch", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: goBack, children: "Back" }) })
|
|
2437
|
-
] })
|
|
2438
|
-
] }),
|
|
2439
|
-
step === "confirm-disable" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2440
|
-
/* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Disable 2FA" }),
|
|
2441
|
-
/* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Enter your 2FA code to disable two-factor authentication." }),
|
|
2442
|
-
msg && /* @__PURE__ */ jsx10("p", { className: msg.ok ? "fc-text" : "fc-error", children: msg.text }),
|
|
2443
|
-
/* @__PURE__ */ jsxs9("div", { className: "fc-tab", children: [
|
|
2444
|
-
/* @__PURE__ */ jsx10(
|
|
2445
|
-
"input",
|
|
2446
|
-
{
|
|
2447
|
-
className: "fc-input",
|
|
2448
|
-
type: "text",
|
|
2449
|
-
inputMode: "numeric",
|
|
2450
|
-
placeholder: "Enter 6-digit code",
|
|
2451
|
-
maxLength: 6,
|
|
2452
|
-
value: code,
|
|
2453
|
-
onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
|
|
2454
|
-
style: { textAlign: "center", letterSpacing: "0.2em" },
|
|
2455
|
-
autoFocus: true
|
|
2456
|
-
}
|
|
2457
|
-
),
|
|
2458
|
-
/* @__PURE__ */ jsx10(
|
|
2459
|
-
"button",
|
|
2460
|
-
{
|
|
2461
|
-
type: "button",
|
|
2462
|
-
className: "fc-btn fc-btn-secondary",
|
|
2463
|
-
onClick: handleDisable,
|
|
2464
|
-
disabled: loading || code.length !== 6,
|
|
2465
|
-
style: { marginTop: 10, background: "rgba(220, 38, 38, 0.1)", color: "#dc2626", borderColor: "rgba(220, 38, 38, 0.2)" },
|
|
2466
|
-
children: loading ? "Disabling..." : "Disable 2FA"
|
|
2467
|
-
}
|
|
2468
|
-
),
|
|
2469
|
-
/* @__PURE__ */ jsx10("div", { className: "fc-switch", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: goBack, children: "Back" }) })
|
|
2470
|
-
] })
|
|
2471
|
-
] }),
|
|
2472
|
-
step === "recovery-codes" && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2473
|
-
/* @__PURE__ */ jsx10("h2", { className: "fc-modal-title", children: "Save your recovery codes" }),
|
|
2474
|
-
/* @__PURE__ */ jsx10("p", { className: "fc-account-section-desc", style: { textAlign: "center" }, children: "Store these codes somewhere safe. Each code can only be used once." }),
|
|
2475
|
-
/* @__PURE__ */ jsx10("div", { className: "fc-recovery-grid", children: recoveryCodes.map((c, i) => /* @__PURE__ */ jsx10("div", { className: "fc-recovery-code", children: c }, i)) }),
|
|
2476
|
-
/* @__PURE__ */ jsx10("div", { style: { textAlign: "center", margin: "8px 0 12px" }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "fc-link", onClick: handleCopyAll, children: "Copy all" }) }),
|
|
2477
|
-
/* @__PURE__ */ jsx10("button", { type: "button", className: "fc-btn fc-btn-primary", onClick: onClose, children: "Done" })
|
|
2478
|
-
] })
|
|
2479
|
-
]
|
|
2454
|
+
const getOptions = {};
|
|
2455
|
+
if (useBrowserAutofill) {
|
|
2456
|
+
if (!await browserSupportsWebAuthnAutofill()) {
|
|
2457
|
+
throw Error("Browser does not support WebAuthn autofill");
|
|
2480
2458
|
}
|
|
2481
|
-
|
|
2459
|
+
const eligibleInputs = document.querySelectorAll("input[autocomplete$='webauthn']");
|
|
2460
|
+
if (eligibleInputs.length < 1 && verifyBrowserAutofillInput) {
|
|
2461
|
+
throw Error('No <input> with "webauthn" as the only or last value in its `autocomplete` attribute was detected');
|
|
2462
|
+
}
|
|
2463
|
+
getOptions.mediation = "conditional";
|
|
2464
|
+
publicKey.allowCredentials = [];
|
|
2465
|
+
}
|
|
2466
|
+
getOptions.publicKey = publicKey;
|
|
2467
|
+
getOptions.signal = WebAuthnAbortService.createNewAbortSignal();
|
|
2468
|
+
let credential;
|
|
2469
|
+
try {
|
|
2470
|
+
credential = await navigator.credentials.get(getOptions);
|
|
2471
|
+
} catch (err) {
|
|
2472
|
+
throw identifyAuthenticationError({ error: err, options: getOptions });
|
|
2473
|
+
}
|
|
2474
|
+
if (!credential) {
|
|
2475
|
+
throw new Error("Authentication was not completed");
|
|
2476
|
+
}
|
|
2477
|
+
const { id, rawId, response, type } = credential;
|
|
2478
|
+
let userHandle = void 0;
|
|
2479
|
+
if (response.userHandle) {
|
|
2480
|
+
userHandle = bufferToBase64URLString(response.userHandle);
|
|
2481
|
+
}
|
|
2482
|
+
return {
|
|
2483
|
+
id,
|
|
2484
|
+
rawId: bufferToBase64URLString(rawId),
|
|
2485
|
+
response: {
|
|
2486
|
+
authenticatorData: bufferToBase64URLString(response.authenticatorData),
|
|
2487
|
+
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
|
|
2488
|
+
signature: bufferToBase64URLString(response.signature),
|
|
2489
|
+
userHandle
|
|
2490
|
+
},
|
|
2491
|
+
type,
|
|
2492
|
+
clientExtensionResults: credential.getClientExtensionResults(),
|
|
2493
|
+
authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
|
|
2494
|
+
};
|
|
2482
2495
|
}
|
|
2483
2496
|
|
|
2484
2497
|
// src/components/passkeys-modal.tsx
|
|
2485
|
-
import { useState as useState11, useEffect as useEffect7, useCallback as useCallback6 } from "react";
|
|
2486
2498
|
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2487
2499
|
function PasskeysModal({ isOpen, onClose, onCountChange }) {
|
|
2488
2500
|
const { api, getAccessToken, config } = useForgeConnect();
|
|
@@ -3264,7 +3276,7 @@ function SecurityTab() {
|
|
|
3264
3276
|
}
|
|
3265
3277
|
|
|
3266
3278
|
// src/components/link-auth-modal.tsx
|
|
3267
|
-
import { useState as useState15, useEffect as useEffect11, useMemo as useMemo2 } from "react";
|
|
3279
|
+
import { useState as useState15, useEffect as useEffect11, useMemo as useMemo2, useRef as useRef8, useCallback as useCallback8 } from "react";
|
|
3268
3280
|
import { Fragment as Fragment6, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3269
3281
|
function LinkAuthModal() {
|
|
3270
3282
|
const { linkModal, closeLinkModal, config } = useForgeConnect();
|
|
@@ -3548,10 +3560,24 @@ function WalletLinkStep({ onBack, onSuccess, onFatalError }) {
|
|
|
3548
3560
|
const [loading, setLoading] = useState15(false);
|
|
3549
3561
|
const [error, setError] = useState15("");
|
|
3550
3562
|
const [showOther, setShowOther] = useState15(false);
|
|
3563
|
+
const [coldWallet, setColdWallet] = useState15(false);
|
|
3551
3564
|
const mobile = useMemo2(() => isMobile(), []);
|
|
3552
3565
|
const walletConfig = config.walletConfig;
|
|
3553
3566
|
const preferred = walletConfig?.preferredWallets ?? [];
|
|
3554
3567
|
const onlyPreferred = walletConfig?.onlyPreferred ?? false;
|
|
3568
|
+
const coldWalletRef = useRef8(coldWallet);
|
|
3569
|
+
coldWalletRef.current = coldWallet;
|
|
3570
|
+
const buildSignTxFnForAdapter = useCallback8((adapter) => {
|
|
3571
|
+
if (!adapter.signTransaction) return void 0;
|
|
3572
|
+
const TxClass = walletConfig?.Transaction;
|
|
3573
|
+
if (!TxClass) return void 0;
|
|
3574
|
+
return async (txBase64) => {
|
|
3575
|
+
const bytes = Uint8Array.from(atob(txBase64), (c) => c.charCodeAt(0));
|
|
3576
|
+
const tx = TxClass.from(bytes);
|
|
3577
|
+
const signedTx = await adapter.signTransaction(tx);
|
|
3578
|
+
return btoa(String.fromCharCode(...new Uint8Array(signedTx.serialize())));
|
|
3579
|
+
};
|
|
3580
|
+
}, [walletConfig?.Transaction]);
|
|
3555
3581
|
const handleConnect = async (w) => {
|
|
3556
3582
|
if (w.readyState !== "Installed") {
|
|
3557
3583
|
if (mobile) {
|
|
@@ -3571,9 +3597,14 @@ function WalletLinkStep({ onBack, onSuccess, onFatalError }) {
|
|
|
3571
3597
|
if (!w.adapter.connected) await w.adapter.connect();
|
|
3572
3598
|
const pk = w.adapter.publicKey;
|
|
3573
3599
|
if (!pk) throw new Error("Wallet did not provide a public key.");
|
|
3600
|
+
const useCold = coldWalletRef.current;
|
|
3574
3601
|
const adapterSignMessage = w.adapter.signMessage ? (msg) => w.adapter.signMessage(msg) : void 0;
|
|
3575
|
-
|
|
3576
|
-
|
|
3602
|
+
await linkWallet(
|
|
3603
|
+
pk.toBase58(),
|
|
3604
|
+
useCold ? void 0 : adapterSignMessage,
|
|
3605
|
+
"solana",
|
|
3606
|
+
useCold ? buildSignTxFnForAdapter(w.adapter) : void 0
|
|
3607
|
+
);
|
|
3577
3608
|
onSuccess();
|
|
3578
3609
|
} catch (err) {
|
|
3579
3610
|
onFatalError(err instanceof Error ? err.message : "Could not link this wallet. Please try again.");
|
|
@@ -3630,7 +3661,7 @@ function WalletLinkStep({ onBack, onSuccess, onFatalError }) {
|
|
|
3630
3661
|
return /* @__PURE__ */ jsxs15("div", { className: "fc-tab", children: [
|
|
3631
3662
|
loading ? /* @__PURE__ */ jsxs15("div", { style: { textAlign: "center", padding: "24px 0" }, children: [
|
|
3632
3663
|
/* @__PURE__ */ jsx16("p", { className: "fc-tab-title", children: "Connecting..." }),
|
|
3633
|
-
/* @__PURE__ */ jsx16("p", { className: "fc-text", children: "Approve the connection, then sign the verification request in your wallet" }),
|
|
3664
|
+
/* @__PURE__ */ jsx16("p", { className: "fc-text", children: coldWallet ? "Confirm the transaction on your device" : "Approve the connection, then sign the verification request in your wallet" }),
|
|
3634
3665
|
error && /* @__PURE__ */ jsxs15(Fragment6, { children: [
|
|
3635
3666
|
/* @__PURE__ */ jsx16("p", { className: "fc-error", children: error }),
|
|
3636
3667
|
/* @__PURE__ */ jsx16(
|
|
@@ -3688,6 +3719,21 @@ function WalletLinkStep({ onBack, onSuccess, onFatalError }) {
|
|
|
3688
3719
|
] }, w.adapter.name))
|
|
3689
3720
|
] }),
|
|
3690
3721
|
preferredWallets.length === 0 && otherWallets.length === 0 && mobileExtraWallets.length === 0 && /* @__PURE__ */ jsx16("p", { className: "fc-text", children: "No wallet found. Please install a Solana wallet (like Phantom) to continue." }),
|
|
3722
|
+
walletConfig?.Transaction && /* @__PURE__ */ jsxs15("label", { className: "fc-cold-wallet-toggle", children: [
|
|
3723
|
+
/* @__PURE__ */ jsx16(
|
|
3724
|
+
"input",
|
|
3725
|
+
{
|
|
3726
|
+
type: "checkbox",
|
|
3727
|
+
checked: coldWallet,
|
|
3728
|
+
onChange: (e) => setColdWallet(e.target.checked)
|
|
3729
|
+
}
|
|
3730
|
+
),
|
|
3731
|
+
/* @__PURE__ */ jsx16("span", { className: "fc-toggle-track" }),
|
|
3732
|
+
/* @__PURE__ */ jsxs15("span", { className: "fc-cold-wallet-label", children: [
|
|
3733
|
+
/* @__PURE__ */ jsx16("span", { children: "Hardware wallet" }),
|
|
3734
|
+
/* @__PURE__ */ jsx16("span", { children: "Ledger, Trezor, Keystone..." })
|
|
3735
|
+
] })
|
|
3736
|
+
] }),
|
|
3691
3737
|
error && /* @__PURE__ */ jsx16("p", { className: "fc-error", children: error })
|
|
3692
3738
|
] }),
|
|
3693
3739
|
!loading && onBack && /* @__PURE__ */ jsx16("div", { className: "fc-switch", children: /* @__PURE__ */ jsx16("button", { type: "button", className: "fc-link", onClick: onBack, children: "Back" }) })
|
|
@@ -3718,10 +3764,10 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3718
3764
|
const [accountModal, setAccountModal] = useState16({ isOpen: false });
|
|
3719
3765
|
const [linkModal, setLinkModal] = useState16({ isOpen: false });
|
|
3720
3766
|
const [challengeToken, setChallengeToken] = useState16(null);
|
|
3721
|
-
const apiRef =
|
|
3722
|
-
const refreshTimerRef =
|
|
3767
|
+
const apiRef = useRef9(createApiClient(config.apiUrl));
|
|
3768
|
+
const refreshTimerRef = useRef9(null);
|
|
3723
3769
|
const api = apiRef.current;
|
|
3724
|
-
const scheduleRefresh =
|
|
3770
|
+
const scheduleRefresh = useCallback9((token) => {
|
|
3725
3771
|
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
3726
3772
|
const delay = getRefreshDelay(token);
|
|
3727
3773
|
if (delay === null) return;
|
|
@@ -3810,7 +3856,7 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3810
3856
|
}
|
|
3811
3857
|
}
|
|
3812
3858
|
}, []);
|
|
3813
|
-
const handleAuthSuccess =
|
|
3859
|
+
const handleAuthSuccess = useCallback9(
|
|
3814
3860
|
async (token) => {
|
|
3815
3861
|
const user = await api.getMe(token);
|
|
3816
3862
|
setAuth({ status: "authenticated", user, accessToken: token });
|
|
@@ -3823,7 +3869,7 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3823
3869
|
},
|
|
3824
3870
|
[api, scheduleRefresh, onLogin]
|
|
3825
3871
|
);
|
|
3826
|
-
const loginWithEmail =
|
|
3872
|
+
const loginWithEmail = useCallback9(
|
|
3827
3873
|
async (email, password) => {
|
|
3828
3874
|
const res = await api.login(email, password);
|
|
3829
3875
|
if (res.requires2FA) {
|
|
@@ -3835,19 +3881,19 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3835
3881
|
},
|
|
3836
3882
|
[api, handleAuthSuccess]
|
|
3837
3883
|
);
|
|
3838
|
-
const register =
|
|
3884
|
+
const register = useCallback9(
|
|
3839
3885
|
async (email, password, displayName) => {
|
|
3840
3886
|
await api.register(email, password, displayName);
|
|
3841
3887
|
},
|
|
3842
3888
|
[api]
|
|
3843
3889
|
);
|
|
3844
|
-
const sendOtp =
|
|
3890
|
+
const sendOtp = useCallback9(
|
|
3845
3891
|
async (email) => {
|
|
3846
3892
|
await api.sendOtp(email);
|
|
3847
3893
|
},
|
|
3848
3894
|
[api]
|
|
3849
3895
|
);
|
|
3850
|
-
const verifyOtp =
|
|
3896
|
+
const verifyOtp = useCallback9(
|
|
3851
3897
|
async (email, code) => {
|
|
3852
3898
|
const res = await api.verifyOtp(email, code);
|
|
3853
3899
|
if (res.requires2FA) {
|
|
@@ -3859,7 +3905,7 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3859
3905
|
},
|
|
3860
3906
|
[api, handleAuthSuccess]
|
|
3861
3907
|
);
|
|
3862
|
-
const loginWithWallet =
|
|
3908
|
+
const loginWithWallet = useCallback9(
|
|
3863
3909
|
async (walletAddress, signMessage, chain = "solana", signTransaction) => {
|
|
3864
3910
|
if (signMessage) {
|
|
3865
3911
|
const { challengeId: challengeId2, message: challengeMessage } = await api.walletChallenge(walletAddress, chain);
|
|
@@ -3890,7 +3936,7 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3890
3936
|
},
|
|
3891
3937
|
[api, handleAuthSuccess]
|
|
3892
3938
|
);
|
|
3893
|
-
const loginWithOAuth =
|
|
3939
|
+
const loginWithOAuth = useCallback9(
|
|
3894
3940
|
(provider) => {
|
|
3895
3941
|
const callbackUrl = `${config.apiUrl}/auth/oauth/${provider}`;
|
|
3896
3942
|
const redirectUri = encodeURIComponent(window.location.origin + "/fc-oauth-callback");
|
|
@@ -3912,7 +3958,7 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3912
3958
|
},
|
|
3913
3959
|
[config.apiUrl]
|
|
3914
3960
|
);
|
|
3915
|
-
const logout =
|
|
3961
|
+
const logout = useCallback9(async () => {
|
|
3916
3962
|
const token = auth.accessToken;
|
|
3917
3963
|
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
3918
3964
|
setAuth({ status: "unauthenticated", user: null, accessToken: null });
|
|
@@ -3924,19 +3970,19 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3924
3970
|
}
|
|
3925
3971
|
}
|
|
3926
3972
|
}, [auth.accessToken, api, onLogout]);
|
|
3927
|
-
const forgotPassword =
|
|
3973
|
+
const forgotPassword = useCallback9(
|
|
3928
3974
|
async (email) => {
|
|
3929
3975
|
await api.forgotPassword(email);
|
|
3930
3976
|
},
|
|
3931
3977
|
[api]
|
|
3932
3978
|
);
|
|
3933
|
-
const resetPassword =
|
|
3979
|
+
const resetPassword = useCallback9(
|
|
3934
3980
|
async (token, password) => {
|
|
3935
3981
|
await api.resetPassword(token, password);
|
|
3936
3982
|
},
|
|
3937
3983
|
[api]
|
|
3938
3984
|
);
|
|
3939
|
-
const verifyEmailToken =
|
|
3985
|
+
const verifyEmailToken = useCallback9(
|
|
3940
3986
|
async (token) => {
|
|
3941
3987
|
const res = await api.verifyEmailToken(token);
|
|
3942
3988
|
if (res.requires2FA) {
|
|
@@ -3948,25 +3994,25 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3948
3994
|
},
|
|
3949
3995
|
[api, handleAuthSuccess]
|
|
3950
3996
|
);
|
|
3951
|
-
const loginWithPasskey =
|
|
3997
|
+
const loginWithPasskey = useCallback9(async () => {
|
|
3952
3998
|
const { options, challengeKey } = await api.getPasskeyLoginOptions(config.webauthnRpId);
|
|
3953
3999
|
const authResponse = await startAuthentication({ optionsJSON: options });
|
|
3954
4000
|
const { accessToken } = await api.verifyPasskeyLogin(challengeKey, authResponse, config.webauthnRpId, config.webauthnOrigin);
|
|
3955
4001
|
await handleAuthSuccess(accessToken);
|
|
3956
4002
|
}, [api, handleAuthSuccess, config.webauthnRpId, config.webauthnOrigin]);
|
|
3957
|
-
const verify2FA =
|
|
4003
|
+
const verify2FA = useCallback9(async (code) => {
|
|
3958
4004
|
if (!challengeToken) throw new Error("No 2FA challenge active");
|
|
3959
4005
|
const { accessToken } = await api.verify2FA(challengeToken, code);
|
|
3960
4006
|
setChallengeToken(null);
|
|
3961
4007
|
await handleAuthSuccess(accessToken);
|
|
3962
4008
|
}, [api, challengeToken, handleAuthSuccess]);
|
|
3963
|
-
const verifyRecoveryCode =
|
|
4009
|
+
const verifyRecoveryCode = useCallback9(async (code) => {
|
|
3964
4010
|
if (!challengeToken) throw new Error("No 2FA challenge active");
|
|
3965
4011
|
const { accessToken } = await api.verifyRecoveryCode(challengeToken, code);
|
|
3966
4012
|
setChallengeToken(null);
|
|
3967
4013
|
await handleAuthSuccess(accessToken);
|
|
3968
4014
|
}, [api, challengeToken, handleAuthSuccess]);
|
|
3969
|
-
const logoutAll =
|
|
4015
|
+
const logoutAll = useCallback9(async () => {
|
|
3970
4016
|
const token = auth.accessToken;
|
|
3971
4017
|
if (refreshTimerRef.current) clearTimeout(refreshTimerRef.current);
|
|
3972
4018
|
setAuth({ status: "unauthenticated", user: null, accessToken: null });
|
|
@@ -3978,7 +4024,7 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3978
4024
|
}
|
|
3979
4025
|
}
|
|
3980
4026
|
}, [auth.accessToken, api, onLogout]);
|
|
3981
|
-
const openModal =
|
|
4027
|
+
const openModal = useCallback9(() => {
|
|
3982
4028
|
const methods = resolveLoginMethods(config);
|
|
3983
4029
|
if (methods.length === 1 && isOAuthMethod(methods[0])) {
|
|
3984
4030
|
loginWithOAuth(methods[0]);
|
|
@@ -3991,25 +4037,25 @@ function ForgeConnectProvider({ config, children, onLogin, onLogout, walletAdapt
|
|
|
3991
4037
|
const step = resolveInitialStep(config, methods);
|
|
3992
4038
|
setModal({ isOpen: true, step });
|
|
3993
4039
|
}, [config, loginWithOAuth]);
|
|
3994
|
-
const closeModal =
|
|
4040
|
+
const closeModal = useCallback9(() => {
|
|
3995
4041
|
setModal({ isOpen: false, step: "method-select" });
|
|
3996
4042
|
}, []);
|
|
3997
|
-
const setModalStep =
|
|
4043
|
+
const setModalStep = useCallback9((step) => {
|
|
3998
4044
|
setModal((prev) => ({ ...prev, step }));
|
|
3999
4045
|
}, []);
|
|
4000
|
-
const openAccountModal =
|
|
4046
|
+
const openAccountModal = useCallback9(() => {
|
|
4001
4047
|
setAccountModal({ isOpen: true });
|
|
4002
4048
|
}, []);
|
|
4003
|
-
const closeAccountModal =
|
|
4049
|
+
const closeAccountModal = useCallback9(() => {
|
|
4004
4050
|
setAccountModal({ isOpen: false });
|
|
4005
4051
|
}, []);
|
|
4006
|
-
const openLinkModal =
|
|
4052
|
+
const openLinkModal = useCallback9((mode) => {
|
|
4007
4053
|
setLinkModal({ isOpen: true, mode: mode ?? "auth" });
|
|
4008
4054
|
}, []);
|
|
4009
|
-
const closeLinkModal =
|
|
4055
|
+
const closeLinkModal = useCallback9(() => {
|
|
4010
4056
|
setLinkModal({ isOpen: false });
|
|
4011
4057
|
}, []);
|
|
4012
|
-
const getAccessToken =
|
|
4058
|
+
const getAccessToken = useCallback9(() => auth.accessToken, [auth.accessToken]);
|
|
4013
4059
|
const value = useMemo3(() => ({
|
|
4014
4060
|
auth,
|
|
4015
4061
|
modal,
|
|
@@ -4113,14 +4159,14 @@ function AccountButton({ className, loginLabel, compact }) {
|
|
|
4113
4159
|
}
|
|
4114
4160
|
|
|
4115
4161
|
// src/hooks/use-admin.ts
|
|
4116
|
-
import { useState as useState17, useCallback as
|
|
4162
|
+
import { useState as useState17, useCallback as useCallback10 } from "react";
|
|
4117
4163
|
function useAdmin() {
|
|
4118
4164
|
const { api, getAccessToken } = useForgeConnect();
|
|
4119
4165
|
const [users, setUsers] = useState17(null);
|
|
4120
4166
|
const [selectedUser, setSelectedUser] = useState17(null);
|
|
4121
4167
|
const [userSessions, setUserSessions] = useState17(null);
|
|
4122
4168
|
const [loading, setLoading] = useState17(false);
|
|
4123
|
-
const listUsers =
|
|
4169
|
+
const listUsers = useCallback10(
|
|
4124
4170
|
async (params) => {
|
|
4125
4171
|
const token = getAccessToken();
|
|
4126
4172
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4135,7 +4181,7 @@ function useAdmin() {
|
|
|
4135
4181
|
},
|
|
4136
4182
|
[api, getAccessToken]
|
|
4137
4183
|
);
|
|
4138
|
-
const getUser =
|
|
4184
|
+
const getUser = useCallback10(
|
|
4139
4185
|
async (id) => {
|
|
4140
4186
|
const token = getAccessToken();
|
|
4141
4187
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4150,7 +4196,7 @@ function useAdmin() {
|
|
|
4150
4196
|
},
|
|
4151
4197
|
[api, getAccessToken]
|
|
4152
4198
|
);
|
|
4153
|
-
const updateUserStatus =
|
|
4199
|
+
const updateUserStatus = useCallback10(
|
|
4154
4200
|
async (id, status) => {
|
|
4155
4201
|
const token = getAccessToken();
|
|
4156
4202
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4161,7 +4207,7 @@ function useAdmin() {
|
|
|
4161
4207
|
},
|
|
4162
4208
|
[api, getAccessToken, selectedUser?.id, getUser]
|
|
4163
4209
|
);
|
|
4164
|
-
const getUserSessions =
|
|
4210
|
+
const getUserSessions = useCallback10(
|
|
4165
4211
|
async (id) => {
|
|
4166
4212
|
const token = getAccessToken();
|
|
4167
4213
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4176,7 +4222,7 @@ function useAdmin() {
|
|
|
4176
4222
|
},
|
|
4177
4223
|
[api, getAccessToken]
|
|
4178
4224
|
);
|
|
4179
|
-
const revokeUserSessions =
|
|
4225
|
+
const revokeUserSessions = useCallback10(
|
|
4180
4226
|
async (id) => {
|
|
4181
4227
|
const token = getAccessToken();
|
|
4182
4228
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4199,7 +4245,7 @@ function useAdmin() {
|
|
|
4199
4245
|
}
|
|
4200
4246
|
|
|
4201
4247
|
// src/hooks/use-roles.ts
|
|
4202
|
-
import { useState as useState18, useCallback as
|
|
4248
|
+
import { useState as useState18, useCallback as useCallback11 } from "react";
|
|
4203
4249
|
function useRoles() {
|
|
4204
4250
|
const { api, getAccessToken } = useForgeConnect();
|
|
4205
4251
|
const [roles, setRoles] = useState18(null);
|
|
@@ -4208,7 +4254,7 @@ function useRoles() {
|
|
|
4208
4254
|
const [roleUsers, setRoleUsers] = useState18(null);
|
|
4209
4255
|
const [permissionDomains, setPermissionDomains] = useState18(null);
|
|
4210
4256
|
const [loading, setLoading] = useState18(false);
|
|
4211
|
-
const listRoles =
|
|
4257
|
+
const listRoles = useCallback11(
|
|
4212
4258
|
async (tenantId) => {
|
|
4213
4259
|
const token = getAccessToken();
|
|
4214
4260
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4223,7 +4269,7 @@ function useRoles() {
|
|
|
4223
4269
|
},
|
|
4224
4270
|
[api, getAccessToken]
|
|
4225
4271
|
);
|
|
4226
|
-
const getRoleUsers =
|
|
4272
|
+
const getRoleUsers = useCallback11(
|
|
4227
4273
|
async (id) => {
|
|
4228
4274
|
const token = getAccessToken();
|
|
4229
4275
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4233,7 +4279,7 @@ function useRoles() {
|
|
|
4233
4279
|
},
|
|
4234
4280
|
[api, getAccessToken]
|
|
4235
4281
|
);
|
|
4236
|
-
const getRole =
|
|
4282
|
+
const getRole = useCallback11(
|
|
4237
4283
|
async (id) => {
|
|
4238
4284
|
const token = getAccessToken();
|
|
4239
4285
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4250,7 +4296,7 @@ function useRoles() {
|
|
|
4250
4296
|
},
|
|
4251
4297
|
[api, getAccessToken, getRoleUsers]
|
|
4252
4298
|
);
|
|
4253
|
-
const createRole =
|
|
4299
|
+
const createRole = useCallback11(
|
|
4254
4300
|
async (data) => {
|
|
4255
4301
|
const token = getAccessToken();
|
|
4256
4302
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4260,7 +4306,7 @@ function useRoles() {
|
|
|
4260
4306
|
},
|
|
4261
4307
|
[api, getAccessToken, listRoles]
|
|
4262
4308
|
);
|
|
4263
|
-
const updateRole =
|
|
4309
|
+
const updateRole = useCallback11(
|
|
4264
4310
|
async (id, data) => {
|
|
4265
4311
|
const token = getAccessToken();
|
|
4266
4312
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4270,7 +4316,7 @@ function useRoles() {
|
|
|
4270
4316
|
},
|
|
4271
4317
|
[api, getAccessToken]
|
|
4272
4318
|
);
|
|
4273
|
-
const deleteRole =
|
|
4319
|
+
const deleteRole = useCallback11(
|
|
4274
4320
|
async (id) => {
|
|
4275
4321
|
const token = getAccessToken();
|
|
4276
4322
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4279,7 +4325,7 @@ function useRoles() {
|
|
|
4279
4325
|
},
|
|
4280
4326
|
[api, getAccessToken, selectedRole?.id]
|
|
4281
4327
|
);
|
|
4282
|
-
const getPermissions =
|
|
4328
|
+
const getPermissions = useCallback11(
|
|
4283
4329
|
async () => {
|
|
4284
4330
|
const token = getAccessToken();
|
|
4285
4331
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4289,7 +4335,7 @@ function useRoles() {
|
|
|
4289
4335
|
},
|
|
4290
4336
|
[api, getAccessToken]
|
|
4291
4337
|
);
|
|
4292
|
-
const getUserRoles =
|
|
4338
|
+
const getUserRoles = useCallback11(
|
|
4293
4339
|
async (userId, tenantId) => {
|
|
4294
4340
|
const token = getAccessToken();
|
|
4295
4341
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4304,7 +4350,7 @@ function useRoles() {
|
|
|
4304
4350
|
},
|
|
4305
4351
|
[api, getAccessToken]
|
|
4306
4352
|
);
|
|
4307
|
-
const assignRole =
|
|
4353
|
+
const assignRole = useCallback11(
|
|
4308
4354
|
async (userId, roleId, tenantId) => {
|
|
4309
4355
|
const token = getAccessToken();
|
|
4310
4356
|
if (!token) throw new Error("Please sign in to continue.");
|
|
@@ -4312,7 +4358,7 @@ function useRoles() {
|
|
|
4312
4358
|
},
|
|
4313
4359
|
[api, getAccessToken]
|
|
4314
4360
|
);
|
|
4315
|
-
const revokeRole =
|
|
4361
|
+
const revokeRole = useCallback11(
|
|
4316
4362
|
async (userId, roleId, tenantId) => {
|
|
4317
4363
|
const token = getAccessToken();
|
|
4318
4364
|
if (!token) throw new Error("Please sign in to continue.");
|