@passflow/react 0.2.0 → 0.2.10
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.js +4 -4
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +467 -40
- package/dist/index.es.js.map +1 -1
- package/dist/src/components/flow/two-factor-verify/index.d.ts +1 -0
- package/dist/src/components/flow/two-factor-verify/index.d.ts.map +1 -1
- package/dist/src/components/form/forgot-password/forgot-password.d.ts.map +1 -1
- package/dist/src/components/form/index.d.ts +1 -0
- package/dist/src/components/form/index.d.ts.map +1 -1
- package/dist/src/components/form/invitation-join/index.d.ts.map +1 -1
- package/dist/src/components/form/reset-password/index.d.ts.map +1 -1
- package/dist/src/components/form/signin/index.d.ts.map +1 -1
- package/dist/src/components/form/signup/index.d.ts.map +1 -1
- package/dist/src/components/form/two-factor-challenge/index.d.ts +10 -0
- package/dist/src/components/form/two-factor-challenge/index.d.ts.map +1 -0
- package/dist/src/components/form/two-factor-challenge/method-selector.d.ts +12 -0
- package/dist/src/components/form/two-factor-challenge/method-selector.d.ts.map +1 -0
- package/dist/src/components/form/two-factor-challenge/otp-input.d.ts +12 -0
- package/dist/src/components/form/two-factor-challenge/otp-input.d.ts.map +1 -0
- package/dist/src/components/form/two-factor-verify/two-factor-recovery-form.d.ts.map +1 -1
- package/dist/src/components/form/verify-challenge/verify-challenge-otp-manual.d.ts.map +1 -1
- package/dist/src/components/provider/passflow-provider.d.ts.map +1 -1
- package/dist/src/context/auth-context.d.ts +10 -1
- package/dist/src/context/auth-context.d.ts.map +1 -1
- package/dist/src/hooks/index.d.ts +3 -0
- package/dist/src/hooks/index.d.ts.map +1 -1
- package/dist/src/hooks/use-app-settings.d.ts.map +1 -1
- package/dist/src/hooks/use-session-expired.d.ts +49 -0
- package/dist/src/hooks/use-session-expired.d.ts.map +1 -0
- package/dist/src/hooks/use-two-factor-challenge.d.ts +16 -0
- package/dist/src/hooks/use-two-factor-challenge.d.ts.map +1 -0
- package/dist/src/hooks/use-two-factor-methods.d.ts +14 -0
- package/dist/src/hooks/use-two-factor-methods.d.ts.map +1 -0
- package/dist/src/hooks/use-two-factor-setup-magic-link.d.ts.map +1 -1
- package/dist/src/types/index.d.ts +18 -0
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/utils/classify-two-factor-error.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +2 -2
package/dist/index.es.js
CHANGED
|
@@ -6,6 +6,8 @@ import clsx from 'clsx';
|
|
|
6
6
|
import { twMerge } from 'tailwind-merge';
|
|
7
7
|
import * as React from 'react';
|
|
8
8
|
import { useState, useEffect, forwardRef, createContext, useCallback, useContext, useRef, useLayoutEffect, useMemo, useReducer } from 'react';
|
|
9
|
+
import { PassflowEvent, parseToken, Passflow } from '@passflow/core';
|
|
10
|
+
export * from '@passflow/core';
|
|
9
11
|
import queryString from 'query-string';
|
|
10
12
|
import { getCountryForTimezone } from 'countries-and-timezones';
|
|
11
13
|
import { usePhoneInput, defaultCountries, parseCountry, FlagImage } from 'react-international-phone';
|
|
@@ -16,11 +18,9 @@ import { HelmetProvider, Helmet } from 'react-helmet-async';
|
|
|
16
18
|
import { ErrorBoundary } from 'react-error-boundary';
|
|
17
19
|
import { phone as phone$1 } from 'phone';
|
|
18
20
|
import { useForm, Controller } from 'react-hook-form';
|
|
19
|
-
import { parseToken, Passflow } from '@passflow/core';
|
|
20
|
-
export * from '@passflow/core';
|
|
21
21
|
import 'react-dom';
|
|
22
22
|
|
|
23
|
-
const version = "0.
|
|
23
|
+
const version = "0.2.10";
|
|
24
24
|
|
|
25
25
|
window.passflowReactAppVersion = () => {
|
|
26
26
|
console.log(`App Version: ${version}`);
|
|
@@ -292,7 +292,6 @@ function getUserFriendlyErrorMessage(error) {
|
|
|
292
292
|
return "Two-factor authentication is not enabled for your account. Please contact your administrator to enable 2FA.";
|
|
293
293
|
case "invalid_code":
|
|
294
294
|
return "Invalid code. Please check your authenticator app and try again.";
|
|
295
|
-
case "generic":
|
|
296
295
|
default:
|
|
297
296
|
return error.message;
|
|
298
297
|
}
|
|
@@ -788,9 +787,27 @@ const NavigationContext$1 = createContext({
|
|
|
788
787
|
});
|
|
789
788
|
|
|
790
789
|
const AuthContext = createContext(void 0);
|
|
791
|
-
const AuthProvider = ({ children }) => {
|
|
790
|
+
const AuthProvider = ({ children, onSessionExpired }) => {
|
|
792
791
|
const passflow = usePassflow();
|
|
793
792
|
const [isLoading, setIsLoading] = useState(false);
|
|
793
|
+
const [isSessionExpired, setIsSessionExpired] = useState(false);
|
|
794
|
+
useEffect(() => {
|
|
795
|
+
const subscriber = {
|
|
796
|
+
onAuthChange: (eventType, payload) => {
|
|
797
|
+
if (eventType === PassflowEvent.SessionExpired) {
|
|
798
|
+
const reason = payload?.reason ?? "refresh_failed";
|
|
799
|
+
setIsSessionExpired(true);
|
|
800
|
+
onSessionExpired?.(reason);
|
|
801
|
+
} else if (eventType === PassflowEvent.SignIn || eventType === PassflowEvent.Register) {
|
|
802
|
+
setIsSessionExpired(false);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
passflow.subscribe(subscriber, [PassflowEvent.SessionExpired, PassflowEvent.SignIn, PassflowEvent.Register]);
|
|
807
|
+
return () => {
|
|
808
|
+
passflow.unsubscribe(subscriber);
|
|
809
|
+
};
|
|
810
|
+
}, [passflow, onSessionExpired]);
|
|
794
811
|
const isAuthenticated = useCallback(() => passflow.isAuthenticated(), [passflow]);
|
|
795
812
|
const getTokens = useCallback(
|
|
796
813
|
async (doRefresh) => {
|
|
@@ -820,6 +837,7 @@ const AuthProvider = ({ children }) => {
|
|
|
820
837
|
isAuthenticated,
|
|
821
838
|
logout,
|
|
822
839
|
isLoading,
|
|
840
|
+
isSessionExpired,
|
|
823
841
|
getTokens
|
|
824
842
|
};
|
|
825
843
|
return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children });
|
|
@@ -1032,10 +1050,9 @@ const useAppSettings = () => {
|
|
|
1032
1050
|
break;
|
|
1033
1051
|
}
|
|
1034
1052
|
} catch {
|
|
1035
|
-
continue;
|
|
1036
1053
|
}
|
|
1037
1054
|
}
|
|
1038
|
-
if (response
|
|
1055
|
+
if (response?.ok) {
|
|
1039
1056
|
const settings = await response.json();
|
|
1040
1057
|
const discoveredAppId = settings.login_app?.app_id || settings.appId;
|
|
1041
1058
|
if (discoveredAppId) {
|
|
@@ -1713,7 +1730,8 @@ const useTwoFactorSetupMagicLink = (token) => {
|
|
|
1713
1730
|
}
|
|
1714
1731
|
setError(null);
|
|
1715
1732
|
try {
|
|
1716
|
-
const
|
|
1733
|
+
const passflowWithMagicLink = passflow;
|
|
1734
|
+
const response = await passflowWithMagicLink.validateTwoFactorSetupMagicLink(token);
|
|
1717
1735
|
if (response.success && response.sessionToken && response.userId) {
|
|
1718
1736
|
setSessionToken(response.sessionToken);
|
|
1719
1737
|
setUserId(response.userId);
|
|
@@ -1771,7 +1789,8 @@ const useTwoFactorSetupMagicLink = (token) => {
|
|
|
1771
1789
|
clearInterval(countdownTimerRef.current);
|
|
1772
1790
|
}
|
|
1773
1791
|
if (shouldClearOnUnmount.current) {
|
|
1774
|
-
passflow
|
|
1792
|
+
const passflowWithSession = passflow;
|
|
1793
|
+
passflowWithSession.clearMagicLinkSession?.();
|
|
1775
1794
|
}
|
|
1776
1795
|
};
|
|
1777
1796
|
}, [passflow]);
|
|
@@ -1789,6 +1808,178 @@ const useTwoFactorSetupMagicLink = (token) => {
|
|
|
1789
1808
|
};
|
|
1790
1809
|
};
|
|
1791
1810
|
|
|
1811
|
+
const useTwoFactorChallenge = () => {
|
|
1812
|
+
const passflow = usePassflow();
|
|
1813
|
+
const [challenge, setChallenge] = useState(null);
|
|
1814
|
+
const [selectedMethod, setSelectedMethod] = useState(null);
|
|
1815
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
1816
|
+
const [error, setError] = useState(null);
|
|
1817
|
+
const requestChallenge = useCallback(
|
|
1818
|
+
async (firstFactorMethod) => {
|
|
1819
|
+
setIsLoading(true);
|
|
1820
|
+
setError(null);
|
|
1821
|
+
try {
|
|
1822
|
+
const response = await passflow.twoFactor.requestChallenge({
|
|
1823
|
+
first_factor_method: firstFactorMethod
|
|
1824
|
+
});
|
|
1825
|
+
setChallenge(response);
|
|
1826
|
+
setSelectedMethod(response.method);
|
|
1827
|
+
} catch (e) {
|
|
1828
|
+
setError(e);
|
|
1829
|
+
setChallenge(null);
|
|
1830
|
+
} finally {
|
|
1831
|
+
setIsLoading(false);
|
|
1832
|
+
}
|
|
1833
|
+
},
|
|
1834
|
+
[passflow]
|
|
1835
|
+
);
|
|
1836
|
+
const verify = useCallback(
|
|
1837
|
+
async (response, trustDevice = false) => {
|
|
1838
|
+
if (!challenge?.challenge_id) {
|
|
1839
|
+
setError(new Error("No active challenge session"));
|
|
1840
|
+
return null;
|
|
1841
|
+
}
|
|
1842
|
+
setIsLoading(true);
|
|
1843
|
+
setError(null);
|
|
1844
|
+
try {
|
|
1845
|
+
const result = await passflow.twoFactor.verifyV2({
|
|
1846
|
+
challenge_id: challenge.challenge_id,
|
|
1847
|
+
method: selectedMethod || challenge.method,
|
|
1848
|
+
response,
|
|
1849
|
+
trust_device: trustDevice
|
|
1850
|
+
});
|
|
1851
|
+
return result;
|
|
1852
|
+
} catch (e) {
|
|
1853
|
+
setError(e);
|
|
1854
|
+
return null;
|
|
1855
|
+
} finally {
|
|
1856
|
+
setIsLoading(false);
|
|
1857
|
+
}
|
|
1858
|
+
},
|
|
1859
|
+
[passflow, challenge, selectedMethod]
|
|
1860
|
+
);
|
|
1861
|
+
const switchMethod = useCallback(
|
|
1862
|
+
async (method) => {
|
|
1863
|
+
if (!challenge?.challenge_id) {
|
|
1864
|
+
setError(new Error("No active challenge session"));
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
setIsLoading(true);
|
|
1868
|
+
setError(null);
|
|
1869
|
+
try {
|
|
1870
|
+
const response = await passflow.twoFactor.switchToAlternative({
|
|
1871
|
+
challenge_id: challenge.challenge_id,
|
|
1872
|
+
method
|
|
1873
|
+
});
|
|
1874
|
+
setChallenge(response);
|
|
1875
|
+
setSelectedMethod(method);
|
|
1876
|
+
} catch (e) {
|
|
1877
|
+
setError(e);
|
|
1878
|
+
} finally {
|
|
1879
|
+
setIsLoading(false);
|
|
1880
|
+
}
|
|
1881
|
+
},
|
|
1882
|
+
[passflow, challenge]
|
|
1883
|
+
);
|
|
1884
|
+
const reset = useCallback(() => {
|
|
1885
|
+
setChallenge(null);
|
|
1886
|
+
setSelectedMethod(null);
|
|
1887
|
+
setError(null);
|
|
1888
|
+
setIsLoading(false);
|
|
1889
|
+
}, []);
|
|
1890
|
+
return {
|
|
1891
|
+
challenge,
|
|
1892
|
+
isLoading,
|
|
1893
|
+
error,
|
|
1894
|
+
requestChallenge,
|
|
1895
|
+
verify,
|
|
1896
|
+
switchMethod,
|
|
1897
|
+
selectedMethod,
|
|
1898
|
+
reset
|
|
1899
|
+
};
|
|
1900
|
+
};
|
|
1901
|
+
|
|
1902
|
+
const useTwoFactorMethods = () => {
|
|
1903
|
+
const passflow = usePassflow();
|
|
1904
|
+
const [availableMethods, setAvailableMethods] = useState([]);
|
|
1905
|
+
const [registeredMethods, setRegisteredMethods] = useState([]);
|
|
1906
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
1907
|
+
const [error, setError] = useState(null);
|
|
1908
|
+
const refresh = useCallback(async () => {
|
|
1909
|
+
setIsLoading(true);
|
|
1910
|
+
setError(null);
|
|
1911
|
+
try {
|
|
1912
|
+
const methods = await passflow.twoFactor.getRegisteredMethods();
|
|
1913
|
+
setRegisteredMethods(methods);
|
|
1914
|
+
const available = methods.map((m) => m.method);
|
|
1915
|
+
setAvailableMethods(available);
|
|
1916
|
+
} catch (e) {
|
|
1917
|
+
setError(e);
|
|
1918
|
+
setRegisteredMethods([]);
|
|
1919
|
+
setAvailableMethods([]);
|
|
1920
|
+
} finally {
|
|
1921
|
+
setIsLoading(false);
|
|
1922
|
+
}
|
|
1923
|
+
}, [passflow]);
|
|
1924
|
+
const removeMethod = useCallback(
|
|
1925
|
+
async (methodId) => {
|
|
1926
|
+
setIsLoading(true);
|
|
1927
|
+
setError(null);
|
|
1928
|
+
try {
|
|
1929
|
+
await passflow.twoFactor.removeMethod?.(methodId);
|
|
1930
|
+
await refresh();
|
|
1931
|
+
} catch (e) {
|
|
1932
|
+
setError(e);
|
|
1933
|
+
} finally {
|
|
1934
|
+
setIsLoading(false);
|
|
1935
|
+
}
|
|
1936
|
+
},
|
|
1937
|
+
[passflow, refresh]
|
|
1938
|
+
);
|
|
1939
|
+
return {
|
|
1940
|
+
availableMethods,
|
|
1941
|
+
registeredMethods,
|
|
1942
|
+
isLoading,
|
|
1943
|
+
error,
|
|
1944
|
+
refresh,
|
|
1945
|
+
removeMethod
|
|
1946
|
+
};
|
|
1947
|
+
};
|
|
1948
|
+
|
|
1949
|
+
function useSessionExpired(options) {
|
|
1950
|
+
const passflow = usePassflow();
|
|
1951
|
+
const [isSessionExpired, setIsSessionExpired] = useState(false);
|
|
1952
|
+
const [expiredReason, setExpiredReason] = useState(null);
|
|
1953
|
+
useEffect(() => {
|
|
1954
|
+
const subscriber = {
|
|
1955
|
+
onAuthChange: (eventType, payload) => {
|
|
1956
|
+
if (eventType === PassflowEvent.SessionExpired) {
|
|
1957
|
+
const reason = payload?.reason ?? "refresh_failed";
|
|
1958
|
+
setIsSessionExpired(true);
|
|
1959
|
+
setExpiredReason(reason);
|
|
1960
|
+
options?.onSessionExpired?.(reason);
|
|
1961
|
+
} else if (eventType === PassflowEvent.SignIn || eventType === PassflowEvent.Register) {
|
|
1962
|
+
setIsSessionExpired(false);
|
|
1963
|
+
setExpiredReason(null);
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1967
|
+
passflow.subscribe(subscriber, [PassflowEvent.SessionExpired, PassflowEvent.SignIn, PassflowEvent.Register]);
|
|
1968
|
+
return () => {
|
|
1969
|
+
passflow.unsubscribe(subscriber);
|
|
1970
|
+
};
|
|
1971
|
+
}, [passflow, options?.onSessionExpired]);
|
|
1972
|
+
const resetSessionExpired = () => {
|
|
1973
|
+
setIsSessionExpired(false);
|
|
1974
|
+
setExpiredReason(null);
|
|
1975
|
+
};
|
|
1976
|
+
return {
|
|
1977
|
+
isSessionExpired,
|
|
1978
|
+
expiredReason,
|
|
1979
|
+
resetSessionExpired
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1792
1983
|
const FieldPhone = ({ id, onChange, isError = false, className = "" }) => {
|
|
1793
1984
|
const [show, setShow] = useState(false);
|
|
1794
1985
|
const [filterValue, setFilterValue] = useState("");
|
|
@@ -2146,7 +2337,7 @@ const TwoFactorSetupForm = ({ onComplete, onCancel, numInputs }) => {
|
|
|
2146
2337
|
/* @__PURE__ */ jsx(Icon, { size: "small", id: "warning", type: "general", className: "icon-warning" }),
|
|
2147
2338
|
/* @__PURE__ */ jsx("span", { children: "These codes will only be shown once. Make sure to save them!" })
|
|
2148
2339
|
] }),
|
|
2149
|
-
/* @__PURE__ */ jsx("div", { className: "passflow-2fa-recovery-codes", children: recoveryCodes.map((recoveryCode
|
|
2340
|
+
/* @__PURE__ */ jsx("div", { className: "passflow-2fa-recovery-codes", children: recoveryCodes.map((recoveryCode) => /* @__PURE__ */ jsx("code", { className: "passflow-2fa-recovery-code", children: recoveryCode }, recoveryCode)) }),
|
|
2150
2341
|
/* @__PURE__ */ jsx(Button, { size: "big", type: "button", variant: "secondary", onClick: handleCopyRecoveryCodes, children: "Copy to Clipboard" }),
|
|
2151
2342
|
/* @__PURE__ */ jsx(Button, { size: "big", type: "button", variant: "primary", onClick: handleComplete, children: "I've Saved These Codes" })
|
|
2152
2343
|
] })
|
|
@@ -2190,6 +2381,245 @@ const TwoFactorSetupFlow = ({
|
|
|
2190
2381
|
return /* @__PURE__ */ jsx(TwoFactorSetupForm, { onComplete: handleSetupComplete, onCancel: handleCancel });
|
|
2191
2382
|
};
|
|
2192
2383
|
|
|
2384
|
+
const methodLabels = {
|
|
2385
|
+
totp: "Authenticator App",
|
|
2386
|
+
email_otp: "Email Code",
|
|
2387
|
+
sms_otp: "SMS Code",
|
|
2388
|
+
passkey: "Passkey",
|
|
2389
|
+
recovery_codes: "Recovery Code",
|
|
2390
|
+
push_fcm: "Push Notification (FCM)",
|
|
2391
|
+
push_webpush: "Push Notification (Web)"
|
|
2392
|
+
};
|
|
2393
|
+
const methodIcons = {
|
|
2394
|
+
totp: "📱",
|
|
2395
|
+
email_otp: "✉️",
|
|
2396
|
+
sms_otp: "💬",
|
|
2397
|
+
passkey: "🔑",
|
|
2398
|
+
recovery_codes: "🔒",
|
|
2399
|
+
push_fcm: "🔔",
|
|
2400
|
+
push_webpush: "🔔"
|
|
2401
|
+
};
|
|
2402
|
+
const MethodSelector = ({
|
|
2403
|
+
availableMethods,
|
|
2404
|
+
currentMethod,
|
|
2405
|
+
onSelectMethod,
|
|
2406
|
+
isOpen,
|
|
2407
|
+
onClose
|
|
2408
|
+
}) => {
|
|
2409
|
+
const handleSelect = (method) => {
|
|
2410
|
+
onSelectMethod(method);
|
|
2411
|
+
onClose();
|
|
2412
|
+
};
|
|
2413
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Root, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { children: [
|
|
2414
|
+
/* @__PURE__ */ jsx(DialogPrimitive.Overlay, { className: "pf-dialog-overlay" }),
|
|
2415
|
+
/* @__PURE__ */ jsxs(DialogPrimitive.Content, { className: "pf-dialog-content pf-method-selector-dialog", children: [
|
|
2416
|
+
/* @__PURE__ */ jsx(DialogPrimitive.Title, { className: "pf-dialog-title", children: "Choose Verification Method" }),
|
|
2417
|
+
/* @__PURE__ */ jsx(DialogPrimitive.Description, { className: "pf-dialog-description", children: "Select an alternative method to verify your identity" }),
|
|
2418
|
+
/* @__PURE__ */ jsx("div", { className: "pf-method-selector-list", children: availableMethods.map((method) => /* @__PURE__ */ jsxs(
|
|
2419
|
+
"button",
|
|
2420
|
+
{
|
|
2421
|
+
type: "button",
|
|
2422
|
+
className: `pf-method-selector-item ${method === currentMethod ? "pf-method-selector-item-active" : ""}`,
|
|
2423
|
+
onClick: () => handleSelect(method),
|
|
2424
|
+
disabled: method === currentMethod,
|
|
2425
|
+
children: [
|
|
2426
|
+
/* @__PURE__ */ jsx("span", { className: "pf-method-icon", children: methodIcons[method] || "🔐" }),
|
|
2427
|
+
/* @__PURE__ */ jsx("span", { className: "pf-method-label", children: methodLabels[method] || method }),
|
|
2428
|
+
method === currentMethod && /* @__PURE__ */ jsx("span", { className: "pf-method-current-badge", children: "Current" })
|
|
2429
|
+
]
|
|
2430
|
+
},
|
|
2431
|
+
method
|
|
2432
|
+
)) }),
|
|
2433
|
+
/* @__PURE__ */ jsx(DialogPrimitive.Close, { asChild: true, children: /* @__PURE__ */ jsx("button", { type: "button", className: "pf-dialog-close", "aria-label": "Close", children: "×" }) })
|
|
2434
|
+
] })
|
|
2435
|
+
] }) });
|
|
2436
|
+
};
|
|
2437
|
+
|
|
2438
|
+
const OtpInputComponent = ({
|
|
2439
|
+
value,
|
|
2440
|
+
onChange,
|
|
2441
|
+
numInputs = 6,
|
|
2442
|
+
error,
|
|
2443
|
+
disabled = false,
|
|
2444
|
+
autoFocus = true
|
|
2445
|
+
}) => {
|
|
2446
|
+
const containerRef = useRef(null);
|
|
2447
|
+
useEffect(() => {
|
|
2448
|
+
if (autoFocus && containerRef.current) {
|
|
2449
|
+
const firstInput = containerRef.current.querySelector("input");
|
|
2450
|
+
if (firstInput) {
|
|
2451
|
+
firstInput.focus();
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
}, [autoFocus]);
|
|
2455
|
+
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "pf-otp-input-container", children: [
|
|
2456
|
+
/* @__PURE__ */ jsx(
|
|
2457
|
+
OtpInput,
|
|
2458
|
+
{
|
|
2459
|
+
value,
|
|
2460
|
+
onChange,
|
|
2461
|
+
numInputs,
|
|
2462
|
+
renderSeparator: /* @__PURE__ */ jsx("span", { className: "pf-otp-separator" }),
|
|
2463
|
+
renderInput: (props) => /* @__PURE__ */ jsx(
|
|
2464
|
+
"input",
|
|
2465
|
+
{
|
|
2466
|
+
...props,
|
|
2467
|
+
className: `pf-otp-input ${error ? "pf-otp-input-error" : ""} ${disabled ? "pf-otp-input-disabled" : ""}`,
|
|
2468
|
+
disabled
|
|
2469
|
+
}
|
|
2470
|
+
),
|
|
2471
|
+
inputType: "tel",
|
|
2472
|
+
shouldAutoFocus: autoFocus
|
|
2473
|
+
}
|
|
2474
|
+
),
|
|
2475
|
+
error && /* @__PURE__ */ jsx("div", { className: "pf-otp-error", children: error })
|
|
2476
|
+
] });
|
|
2477
|
+
};
|
|
2478
|
+
|
|
2479
|
+
const TwoFactorChallenge = ({
|
|
2480
|
+
firstFactorMethod,
|
|
2481
|
+
onSuccess,
|
|
2482
|
+
onError,
|
|
2483
|
+
trustDevice = false
|
|
2484
|
+
}) => {
|
|
2485
|
+
const { challenge, isLoading, error, requestChallenge, verify, switchMethod, selectedMethod } = useTwoFactorChallenge();
|
|
2486
|
+
const [otpValue, setOtpValue] = useState("");
|
|
2487
|
+
const [isMethodSelectorOpen, setIsMethodSelectorOpen] = useState(false);
|
|
2488
|
+
useEffect(() => {
|
|
2489
|
+
requestChallenge(firstFactorMethod);
|
|
2490
|
+
}, [requestChallenge, firstFactorMethod]);
|
|
2491
|
+
useEffect(() => {
|
|
2492
|
+
if (error && onError) {
|
|
2493
|
+
onError(error);
|
|
2494
|
+
}
|
|
2495
|
+
}, [error, onError]);
|
|
2496
|
+
const handleVerify = async () => {
|
|
2497
|
+
if (!otpValue) {
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2500
|
+
const result = await verify(otpValue, trustDevice);
|
|
2501
|
+
if (result) {
|
|
2502
|
+
onSuccess?.();
|
|
2503
|
+
}
|
|
2504
|
+
};
|
|
2505
|
+
const handleSwitchMethod = async (method) => {
|
|
2506
|
+
setOtpValue("");
|
|
2507
|
+
await switchMethod(method);
|
|
2508
|
+
};
|
|
2509
|
+
const handleOtpChange = (value) => {
|
|
2510
|
+
setOtpValue(value);
|
|
2511
|
+
if (value.length === 6) {
|
|
2512
|
+
setTimeout(() => {
|
|
2513
|
+
verify(value, trustDevice).then((result) => {
|
|
2514
|
+
if (result) {
|
|
2515
|
+
onSuccess?.();
|
|
2516
|
+
}
|
|
2517
|
+
});
|
|
2518
|
+
}, 100);
|
|
2519
|
+
}
|
|
2520
|
+
};
|
|
2521
|
+
if (!challenge) {
|
|
2522
|
+
return /* @__PURE__ */ jsx("div", { className: "pf-two-factor-challenge pf-loading", children: /* @__PURE__ */ jsx("p", { children: "Loading challenge..." }) });
|
|
2523
|
+
}
|
|
2524
|
+
const currentMethod = selectedMethod || challenge.method;
|
|
2525
|
+
const availableMethods = [challenge.method, ...challenge.alternative_methods || []];
|
|
2526
|
+
const renderChallengeInput = () => {
|
|
2527
|
+
switch (currentMethod) {
|
|
2528
|
+
case "totp":
|
|
2529
|
+
case "email_otp":
|
|
2530
|
+
case "sms_otp":
|
|
2531
|
+
return /* @__PURE__ */ jsxs("div", { className: "pf-challenge-input-wrapper", children: [
|
|
2532
|
+
/* @__PURE__ */ jsx(
|
|
2533
|
+
OtpInputComponent,
|
|
2534
|
+
{
|
|
2535
|
+
value: otpValue,
|
|
2536
|
+
onChange: handleOtpChange,
|
|
2537
|
+
numInputs: 6,
|
|
2538
|
+
error: error?.message,
|
|
2539
|
+
disabled: isLoading,
|
|
2540
|
+
autoFocus: true
|
|
2541
|
+
}
|
|
2542
|
+
),
|
|
2543
|
+
currentMethod === "email_otp" && challenge.code_sent_to && /* @__PURE__ */ jsxs("p", { className: "pf-challenge-hint", children: [
|
|
2544
|
+
"Code sent to ",
|
|
2545
|
+
challenge.code_sent_to
|
|
2546
|
+
] }),
|
|
2547
|
+
currentMethod === "sms_otp" && challenge.code_sent_to && /* @__PURE__ */ jsxs("p", { className: "pf-challenge-hint", children: [
|
|
2548
|
+
"Code sent to ",
|
|
2549
|
+
challenge.code_sent_to
|
|
2550
|
+
] }),
|
|
2551
|
+
/* @__PURE__ */ jsx(
|
|
2552
|
+
"button",
|
|
2553
|
+
{
|
|
2554
|
+
type: "button",
|
|
2555
|
+
onClick: handleVerify,
|
|
2556
|
+
disabled: isLoading || otpValue.length !== 6,
|
|
2557
|
+
className: "pf-button pf-button-primary",
|
|
2558
|
+
children: isLoading ? "Verifying..." : "Verify"
|
|
2559
|
+
}
|
|
2560
|
+
)
|
|
2561
|
+
] });
|
|
2562
|
+
case "passkey":
|
|
2563
|
+
return /* @__PURE__ */ jsx("div", { className: "pf-challenge-input-wrapper", children: /* @__PURE__ */ jsx("button", { type: "button", onClick: handleVerify, disabled: isLoading, className: "pf-button pf-button-primary", children: isLoading ? "Verifying..." : "Use Passkey" }) });
|
|
2564
|
+
case "recovery_codes":
|
|
2565
|
+
return /* @__PURE__ */ jsxs("div", { className: "pf-challenge-input-wrapper", children: [
|
|
2566
|
+
/* @__PURE__ */ jsx(
|
|
2567
|
+
"input",
|
|
2568
|
+
{
|
|
2569
|
+
type: "text",
|
|
2570
|
+
value: otpValue,
|
|
2571
|
+
onChange: (e) => setOtpValue(e.target.value),
|
|
2572
|
+
placeholder: "Enter recovery code",
|
|
2573
|
+
disabled: isLoading,
|
|
2574
|
+
className: "pf-input"
|
|
2575
|
+
}
|
|
2576
|
+
),
|
|
2577
|
+
/* @__PURE__ */ jsx(
|
|
2578
|
+
"button",
|
|
2579
|
+
{
|
|
2580
|
+
type: "button",
|
|
2581
|
+
onClick: handleVerify,
|
|
2582
|
+
disabled: isLoading || !otpValue,
|
|
2583
|
+
className: "pf-button pf-button-primary",
|
|
2584
|
+
children: isLoading ? "Verifying..." : "Verify"
|
|
2585
|
+
}
|
|
2586
|
+
)
|
|
2587
|
+
] });
|
|
2588
|
+
default:
|
|
2589
|
+
return /* @__PURE__ */ jsxs("p", { children: [
|
|
2590
|
+
"Unsupported method: ",
|
|
2591
|
+
currentMethod
|
|
2592
|
+
] });
|
|
2593
|
+
}
|
|
2594
|
+
};
|
|
2595
|
+
return /* @__PURE__ */ jsxs("div", { className: "pf-two-factor-challenge", children: [
|
|
2596
|
+
/* @__PURE__ */ jsxs("div", { className: "pf-challenge-method-info", children: [
|
|
2597
|
+
/* @__PURE__ */ jsx("h3", { children: "Two-Factor Authentication" }),
|
|
2598
|
+
/* @__PURE__ */ jsx("p", { children: "Enter the verification code" })
|
|
2599
|
+
] }),
|
|
2600
|
+
renderChallengeInput(),
|
|
2601
|
+
availableMethods.length > 1 && /* @__PURE__ */ jsx(
|
|
2602
|
+
"button",
|
|
2603
|
+
{
|
|
2604
|
+
type: "button",
|
|
2605
|
+
onClick: () => setIsMethodSelectorOpen(true),
|
|
2606
|
+
className: "pf-button pf-button-secondary pf-button-switch-method",
|
|
2607
|
+
children: "Use different method"
|
|
2608
|
+
}
|
|
2609
|
+
),
|
|
2610
|
+
/* @__PURE__ */ jsx(
|
|
2611
|
+
MethodSelector,
|
|
2612
|
+
{
|
|
2613
|
+
availableMethods,
|
|
2614
|
+
currentMethod,
|
|
2615
|
+
onSelectMethod: handleSwitchMethod,
|
|
2616
|
+
isOpen: isMethodSelectorOpen,
|
|
2617
|
+
onClose: () => setIsMethodSelectorOpen(false)
|
|
2618
|
+
}
|
|
2619
|
+
)
|
|
2620
|
+
] });
|
|
2621
|
+
};
|
|
2622
|
+
|
|
2193
2623
|
const TwoFactorVerifyForm = ({
|
|
2194
2624
|
onSuccess,
|
|
2195
2625
|
onUseRecovery,
|
|
@@ -2440,7 +2870,6 @@ const TwoFactorRecoveryForm = ({
|
|
|
2440
2870
|
isError && "passflow-field--error",
|
|
2441
2871
|
isInputDisabled && "passflow-field--disabled"
|
|
2442
2872
|
),
|
|
2443
|
-
autoFocus: !isInputDisabled,
|
|
2444
2873
|
autoComplete: "off",
|
|
2445
2874
|
disabled: isInputDisabled
|
|
2446
2875
|
}
|
|
@@ -2478,7 +2907,8 @@ const TwoFactorRecoveryForm = ({
|
|
|
2478
2907
|
const TwoFactorVerifyFlow = ({
|
|
2479
2908
|
successAuthRedirect,
|
|
2480
2909
|
signInPath = routes.signin.path,
|
|
2481
|
-
twoFactorSetupPath = routes.two_factor_setup?.path
|
|
2910
|
+
twoFactorSetupPath = routes.two_factor_setup?.path,
|
|
2911
|
+
useV2Flow = false
|
|
2482
2912
|
}) => {
|
|
2483
2913
|
const [mode, setMode] = useState("verify");
|
|
2484
2914
|
const [shouldBlockRender, setShouldBlockRender] = useState(false);
|
|
@@ -2531,6 +2961,9 @@ const TwoFactorVerifyFlow = ({
|
|
|
2531
2961
|
if (!passflow.isTwoFactorVerificationRequired() && TwoFactorLoopPrevention.canRedirect()) {
|
|
2532
2962
|
return null;
|
|
2533
2963
|
}
|
|
2964
|
+
if (useV2Flow) {
|
|
2965
|
+
return /* @__PURE__ */ jsx(TwoFactorChallenge, { onSuccess: handleSuccess });
|
|
2966
|
+
}
|
|
2534
2967
|
return /* @__PURE__ */ jsx(Fragment, { children: mode === "verify" ? /* @__PURE__ */ jsx(
|
|
2535
2968
|
TwoFactorVerifyForm,
|
|
2536
2969
|
{
|
|
@@ -2692,9 +3125,9 @@ const SignInForm = ({
|
|
|
2692
3125
|
navigate({ to: twoFactorVerifyPath ?? routes.two_factor_verify.path });
|
|
2693
3126
|
return;
|
|
2694
3127
|
}
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
else window.location.href = await getUrlWithTokens(passflow,
|
|
3128
|
+
const redirectUrl = successAuthRedirect ?? appSettings?.defaults.redirect ?? "";
|
|
3129
|
+
if (!isValidUrl(redirectUrl)) navigate({ to: redirectUrl });
|
|
3130
|
+
else window.location.href = await getUrlWithTokens(passflow, redirectUrl);
|
|
2698
3131
|
}
|
|
2699
3132
|
};
|
|
2700
3133
|
const onSubmitPasskeyHandler = async (passkeyPayload) => {
|
|
@@ -2708,9 +3141,9 @@ const SignInForm = ({
|
|
|
2708
3141
|
navigate({ to: twoFactorVerifyPath ?? routes.two_factor_verify.path });
|
|
2709
3142
|
return;
|
|
2710
3143
|
}
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
else window.location.href = await getUrlWithTokens(passflow,
|
|
3144
|
+
const redirectUrl = successAuthRedirect ?? appSettings?.defaults.redirect ?? "";
|
|
3145
|
+
if (!isValidUrl(redirectUrl)) navigate({ to: redirectUrl });
|
|
3146
|
+
else window.location.href = await getUrlWithTokens(passflow, redirectUrl);
|
|
2714
3147
|
}
|
|
2715
3148
|
};
|
|
2716
3149
|
const onSubmitPasswordlessHandler = async (userPayload) => {
|
|
@@ -2719,8 +3152,7 @@ const SignInForm = ({
|
|
|
2719
3152
|
...userPayload,
|
|
2720
3153
|
challenge_type: getPasswordlessData(authMethods, defaultMethod)?.challengeType,
|
|
2721
3154
|
create_tenant: createTenantForNewUser,
|
|
2722
|
-
|
|
2723
|
-
redirect_url: successAuthRedirect ?? appSettings.defaults.redirect,
|
|
3155
|
+
redirect_url: successAuthRedirect ?? appSettings?.defaults.redirect,
|
|
2724
3156
|
...!isEmpty(inviteToken) && { invite_token: inviteToken }
|
|
2725
3157
|
};
|
|
2726
3158
|
const response = await fetch(payload, "passwordless");
|
|
@@ -3151,25 +3583,24 @@ const SignUpForm = ({
|
|
|
3151
3583
|
};
|
|
3152
3584
|
const status = await fetch(payload, "password");
|
|
3153
3585
|
if (status) {
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
else window.location.href = await getUrlWithTokens(passflow,
|
|
3586
|
+
const redirectUrl = successAuthRedirect ?? appSettings?.defaults.redirect ?? "";
|
|
3587
|
+
if (!isValidUrl(redirectUrl)) navigate({ to: redirectUrl });
|
|
3588
|
+
else window.location.href = await getUrlWithTokens(passflow, redirectUrl);
|
|
3157
3589
|
}
|
|
3158
3590
|
};
|
|
3159
3591
|
const onSubmitPasskeyHandler = async () => {
|
|
3592
|
+
const redirectUrl = successAuthRedirect ?? appSettings?.defaults.redirect ?? "";
|
|
3160
3593
|
const payload = {
|
|
3161
3594
|
relying_party_id: relyingPartyId,
|
|
3162
3595
|
create_tenant: createTenantForNewUser,
|
|
3163
|
-
|
|
3164
|
-
redirect_url: successAuthRedirect ?? appSettings.defaults.redirect,
|
|
3596
|
+
redirect_url: redirectUrl,
|
|
3165
3597
|
...!isEmpty(inviteToken) && { invite_token: inviteToken },
|
|
3166
3598
|
scopes
|
|
3167
3599
|
};
|
|
3168
3600
|
const response = await fetch(payload, "passkey");
|
|
3169
3601
|
if (response) {
|
|
3170
|
-
if (!isValidUrl(
|
|
3171
|
-
|
|
3172
|
-
else window.location.href = await getUrlWithTokens(passflow, successAuthRedirect ?? appSettings.defaults.redirect);
|
|
3602
|
+
if (!isValidUrl(redirectUrl)) navigate({ to: redirectUrl });
|
|
3603
|
+
else window.location.href = await getUrlWithTokens(passflow, redirectUrl);
|
|
3173
3604
|
}
|
|
3174
3605
|
};
|
|
3175
3606
|
const onSubmitPasswordlessHandler = async (userPayload) => {
|
|
@@ -3178,8 +3609,7 @@ const SignUpForm = ({
|
|
|
3178
3609
|
...userPayload,
|
|
3179
3610
|
challenge_type: currentChallegeType,
|
|
3180
3611
|
create_tenant: createTenantForNewUser,
|
|
3181
|
-
|
|
3182
|
-
redirect_url: successAuthRedirect ?? appSettings.defaults.redirect,
|
|
3612
|
+
redirect_url: successAuthRedirect ?? appSettings?.defaults.redirect,
|
|
3183
3613
|
...!isEmpty(inviteToken) && { invite_token: inviteToken }
|
|
3184
3614
|
};
|
|
3185
3615
|
const response = await fetch(payload, "passwordless");
|
|
@@ -3665,8 +4095,7 @@ const VerifyChallengeOTPManual = ({
|
|
|
3665
4095
|
const payload = {
|
|
3666
4096
|
create_tenant: createTenantForNewUser,
|
|
3667
4097
|
challenge_type: challengeType,
|
|
3668
|
-
|
|
3669
|
-
redirect_url: successAuthRedirect ?? appSettings.defaults.redirect,
|
|
4098
|
+
redirect_url: successAuthRedirect ?? appSettings?.defaults.redirect ?? "",
|
|
3670
4099
|
...identity === "email" ? { email: identityValue } : { phone: identityValue }
|
|
3671
4100
|
};
|
|
3672
4101
|
const refetchResponse = await refetch(payload, type);
|
|
@@ -3919,8 +4348,7 @@ const ForgotPassword = ({
|
|
|
3919
4348
|
...isEmail && { email: values.email_or_username },
|
|
3920
4349
|
...isUsername && { username: values.email_or_username },
|
|
3921
4350
|
...isPhone && { phone: validatedPhone.phoneNumber },
|
|
3922
|
-
|
|
3923
|
-
redirect_url: successResetRedirect ?? appSettings.defaults.redirect
|
|
4351
|
+
redirect_url: successResetRedirect ?? appSettings?.defaults.redirect
|
|
3924
4352
|
};
|
|
3925
4353
|
const status = await fetch(payload);
|
|
3926
4354
|
if (status) {
|
|
@@ -4138,12 +4566,12 @@ const ResetPassword = ({ successAuthRedirect }) => {
|
|
|
4138
4566
|
const resetTokenType = resetTokenData;
|
|
4139
4567
|
const status = await fetch(values.password);
|
|
4140
4568
|
if (status) {
|
|
4141
|
-
if (!isValidUrl(resetTokenType?.redirect_url ?? successAuthRedirect ?? appSettings
|
|
4142
|
-
navigate({ to: resetTokenType?.redirect_url ?? successAuthRedirect ?? appSettings
|
|
4569
|
+
if (!isValidUrl(resetTokenType?.redirect_url ?? successAuthRedirect ?? appSettings?.defaults.redirect))
|
|
4570
|
+
navigate({ to: resetTokenType?.redirect_url ?? successAuthRedirect ?? appSettings?.defaults.redirect });
|
|
4143
4571
|
else
|
|
4144
4572
|
window.location.href = await getUrlWithTokens(
|
|
4145
4573
|
passflow,
|
|
4146
|
-
resetTokenType?.redirect_url ?? successAuthRedirect ?? appSettings
|
|
4574
|
+
resetTokenType?.redirect_url ?? successAuthRedirect ?? appSettings?.defaults.redirect
|
|
4147
4575
|
);
|
|
4148
4576
|
}
|
|
4149
4577
|
};
|
|
@@ -4306,7 +4734,7 @@ const InvitationJoinFlow = ({
|
|
|
4306
4734
|
type: "button",
|
|
4307
4735
|
variant: "primary",
|
|
4308
4736
|
className: "passflow-button-invitation-join",
|
|
4309
|
-
onClick: () => void onClickAcceptInvitationHandler(redirectUrl ?? successAuthRedirect ?? appSettings
|
|
4737
|
+
onClick: () => void onClickAcceptInvitationHandler(redirectUrl ?? successAuthRedirect ?? appSettings?.defaults.redirect),
|
|
4310
4738
|
disabled: isInvitationJoinLoading,
|
|
4311
4739
|
children: "Accept invitation"
|
|
4312
4740
|
}
|
|
@@ -6385,7 +6813,6 @@ const PassflowProvider = ({
|
|
|
6385
6813
|
}
|
|
6386
6814
|
}
|
|
6387
6815
|
} catch (error) {
|
|
6388
|
-
continue;
|
|
6389
6816
|
}
|
|
6390
6817
|
}
|
|
6391
6818
|
console.warn("Failed to discover appId from /settings");
|
|
@@ -6415,5 +6842,5 @@ const PassflowProvider = ({
|
|
|
6415
6842
|
return /* @__PURE__ */ jsx(PassflowContext.Provider, { value: passflowValue, children: /* @__PURE__ */ jsx(NavigationContext$1.Provider, { value: navigationValue, children: /* @__PURE__ */ jsx(AuthProvider, { children }) }) });
|
|
6416
6843
|
};
|
|
6417
6844
|
|
|
6418
|
-
export { Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, FieldPassword, FieldPhone, FieldText, ForgotPassword, ForgotPasswordSuccess, Icon, InvitationJoin, Link, PassflowFlow, PassflowProvider, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, ProvidersBox, ResetPassword, SignIn, SignInForm, SignUp, SignUpForm, Switch, TwoFactorRecoveryForm, TwoFactorSetupForm, TwoFactorSetupMagicLinkFlow, TwoFactorVerifyForm, VerifyChallengeMagicLink, VerifyChallengeOTP, Wrapper, useAppSettings, useAuth, useAuthCloudRedirect, useForgotPassword, useJoinInvite, useLogout, useNavigation, useOutsideClick, usePassflow, usePasswordlessComplete, useProvider, useResetPassword, useSignIn, useSignUp, useTwoFactorManage, useTwoFactorSetup, useTwoFactorSetupMagicLink, useTwoFactorStatus, useTwoFactorVerify, useUserPasskeys };
|
|
6845
|
+
export { Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, FieldPassword, FieldPhone, FieldText, ForgotPassword, ForgotPasswordSuccess, Icon, InvitationJoin, Link, PassflowFlow, PassflowProvider, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, ProvidersBox, ResetPassword, SignIn, SignInForm, SignUp, SignUpForm, Switch, TwoFactorChallenge, TwoFactorRecoveryForm, TwoFactorSetupForm, TwoFactorSetupMagicLinkFlow, TwoFactorVerifyForm, VerifyChallengeMagicLink, VerifyChallengeOTP, Wrapper, useAppSettings, useAuth, useAuthCloudRedirect, useForgotPassword, useJoinInvite, useLogout, useNavigation, useOutsideClick, usePassflow, usePasswordlessComplete, useProvider, useResetPassword, useSessionExpired, useSignIn, useSignUp, useTwoFactorChallenge, useTwoFactorManage, useTwoFactorMethods, useTwoFactorSetup, useTwoFactorSetupMagicLink, useTwoFactorStatus, useTwoFactorVerify, useUserPasskeys };
|
|
6419
6846
|
//# sourceMappingURL=index.es.js.map
|