diff --git a/lib/build/components/errorBoundary.d.ts b/lib/build/components/errorBoundary.d.ts deleted file mode 100644 index 2b672d624..000000000 --- a/lib/build/components/errorBoundary.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; -import type { ErrorInfo, ReactNode } from "react"; -declare type ErrorBoundaryState = { - hasError: boolean; -}; -export default class ErrorBoundary extends React.Component { - constructor(props: { hasError: boolean }); - static getDerivedStateFromError(): ErrorBoundaryState; - componentDidCatch(error: Error, errorInfo: ErrorInfo): void; - render(): JSX.Element | ReactNode | undefined; -} -export {}; diff --git a/lib/build/index2.js b/lib/build/index2.js index 00ec7a4a1..189632c39 100644 --- a/lib/build/index2.js +++ b/lib/build/index2.js @@ -19,31 +19,6 @@ var NormalisedURLPath__default = /*#__PURE__*/ _interopDefault(NormalisedURLPath var ComponentOverrideContext = React__default.default.createContext("IS_DEFAULT"); -/* - * Component. - */ -var ErrorBoundary = /** @class */ (function (_super) { - genericComponentOverrideContext.__extends(ErrorBoundary, _super); - function ErrorBoundary(props) { - var _this = _super.call(this, props) || this; - _this.state = { hasError: false }; - return _this; - } - ErrorBoundary.getDerivedStateFromError = function () { - return { hasError: true }; - }; - ErrorBoundary.prototype.componentDidCatch = function (error, errorInfo) { - console.info(error, errorInfo); - }; - ErrorBoundary.prototype.render = function () { - if (this.state.hasError) { - return jsxRuntime.jsx(React.Fragment, {}); - } - return this.props.children; - }; - return ErrorBoundary; -})(React__default.default.Component); - var dynamicLoginMethodsContext = React__default.default.createContext(undefined); var useDynamicLoginMethods = function () { var value = React__default.default.useContext(dynamicLoginMethodsContext); @@ -117,31 +92,29 @@ function FeatureWrapper(_a) { genericComponentOverrideContext.__assign( { value: loadedDynamicLoginMethods }, { - children: jsxRuntime.jsx(ErrorBoundary, { - children: jsxRuntime.jsx( - translationContext.TranslationContextProvider, - genericComponentOverrideContext.__assign( - { - defaultLanguage: st.languageTranslations.defaultLanguage, - defaultStore: genericComponentOverrideContext.mergeObjects( - defaultStore, - st.languageTranslations.userTranslationStore - ), - translationControlEventSource: st.languageTranslations.translationEventSource, - userTranslationFunc: st.languageTranslations.userTranslationFunc, - }, - { - children: jsxRuntime.jsx( - WithOrWithoutShadowDom, - genericComponentOverrideContext.__assign( - { useShadowDom: useShadowDom }, - { children: children } - ) - ), - } - ) - ), - }), + children: jsxRuntime.jsx( + translationContext.TranslationContextProvider, + genericComponentOverrideContext.__assign( + { + defaultLanguage: st.languageTranslations.defaultLanguage, + defaultStore: genericComponentOverrideContext.mergeObjects( + defaultStore, + st.languageTranslations.userTranslationStore + ), + translationControlEventSource: st.languageTranslations.translationEventSource, + userTranslationFunc: st.languageTranslations.userTranslationFunc, + }, + { + children: jsxRuntime.jsx( + WithOrWithoutShadowDom, + genericComponentOverrideContext.__assign( + { useShadowDom: useShadowDom }, + { children: children } + ) + ), + } + ) + ), } ) ); @@ -373,15 +346,13 @@ var DynamicLoginMethodsSpinner = function () { genericComponentOverrideContext.__assign( { value: recipeComponentOverrides }, { - children: jsxRuntime.jsx(ErrorBoundary, { - children: jsxRuntime.jsx( - WithOrWithoutShadowDom, - genericComponentOverrideContext.__assign( - { useShadowDom: recipe.config.useShadowDom }, - { children: jsxRuntime.jsx(DynamicLoginMethodsSpinnerTheme, { config: recipe.config }) } - ) - ), - }), + children: jsxRuntime.jsx( + WithOrWithoutShadowDom, + genericComponentOverrideContext.__assign( + { useShadowDom: recipe.config.useShadowDom }, + { children: jsxRuntime.jsx(DynamicLoginMethodsSpinnerTheme, { config: recipe.config }) } + ) + ), } ) ); diff --git a/lib/build/passwordless-shared2.js b/lib/build/passwordless-shared2.js index 47cdc0509..5b42a4724 100644 --- a/lib/build/passwordless-shared2.js +++ b/lib/build/passwordless-shared2.js @@ -231,6 +231,46 @@ function normalisePasswordlessBaseConfig(config) { style: style, }); } +function getEnabledContactMethods(contactMethod, currentDynamicLoginMethods) { + var _a; + var enabledContactMethods = contactMethod === "EMAIL_OR_PHONE" ? ["EMAIL", "PHONE"] : [contactMethod]; + var firstFactors; + if (currentDynamicLoginMethods.loaded && currentDynamicLoginMethods.loginMethods.firstFactors) { + firstFactors = currentDynamicLoginMethods.loginMethods.firstFactors; + } else { + firstFactors = + (_a = recipe.MultiFactorAuth.getInstance()) === null || _a === void 0 ? void 0 : _a.config.firstFactors; + } + if (firstFactors !== undefined) { + if (enabledContactMethods.includes("PHONE")) { + if (!firstFactors.includes("otp-phone") && !firstFactors.includes("link-phone")) { + enabledContactMethods = enabledContactMethods.filter(function (c) { + return c !== "PHONE"; + }); + } + } else { + if (firstFactors.includes("otp-phone") || firstFactors.includes("link-phone")) { + throw new Error("The enabled contact method is not a superset of the requested first factors"); + } + } + if (enabledContactMethods.includes("EMAIL")) { + if (!firstFactors.includes("otp-email") && !firstFactors.includes("link-email")) { + enabledContactMethods = enabledContactMethods.filter(function (c) { + return c !== "EMAIL"; + }); + } + } else { + if (firstFactors.includes("otp-email") || firstFactors.includes("link-email")) { + throw new Error("The enabled contact method is not a superset of the requested first factors"); + } + } + } + if (enabledContactMethods.length === 0) { + // It should never get here, but as a sanity check this is fine + throw new Error("The enabled contact method is not a superset of the requested first factors"); + } + return enabledContactMethods; +} /* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. * @@ -343,5 +383,6 @@ var Passwordless = /** @class */ (function (_super) { })(utils.AuthRecipe); exports.Passwordless = Passwordless; +exports.getEnabledContactMethods = getEnabledContactMethods; exports.normalisePasswordlessConfig = normalisePasswordlessConfig; exports.passwordlessFactors = passwordlessFactors; diff --git a/lib/build/passwordless-shared4.js b/lib/build/passwordless-shared4.js index 44da91257..8916ded9f 100644 --- a/lib/build/passwordless-shared4.js +++ b/lib/build/passwordless-shared4.js @@ -4947,35 +4947,10 @@ function SignInUpThemeWrapper(props) { ); } function getActiveScreen(props, currentDynamicLoginMethods) { - var _a; - var contactMethod = props.config.contactMethod; - var enabledContactMethods = contactMethod === "EMAIL_OR_PHONE" ? ["EMAIL", "PHONE"] : [contactMethod]; - var firstFactors; - if (currentDynamicLoginMethods.loaded && currentDynamicLoginMethods.loginMethods.firstFactors) { - firstFactors = currentDynamicLoginMethods.loginMethods.firstFactors; - } else { - firstFactors = - (_a = recipe$1.MultiFactorAuth.getInstance()) === null || _a === void 0 ? void 0 : _a.config.firstFactors; - } - if (firstFactors !== undefined) { - if (enabledContactMethods.includes("PHONE")) { - if (!firstFactors.includes("otp-phone") && !firstFactors.includes("link-phone")) { - enabledContactMethods = enabledContactMethods.filter(function (c) { - return c !== "PHONE"; - }); - } - } - if (enabledContactMethods.includes("EMAIL")) { - if (!firstFactors.includes("otp-email") && !firstFactors.includes("link-email")) { - enabledContactMethods = enabledContactMethods.filter(function (c) { - return c !== "EMAIL"; - }); - } - } - } - if (enabledContactMethods.length === 0) { - throw new Error("No overlap between first factors and enabled contact methods"); // redirect to access denied? - } + var enabledContactMethods = recipe$2.getEnabledContactMethods( + props.config.contactMethod, + currentDynamicLoginMethods + ); if (props.featureState.successInAnotherTab) { return exports.SignInUpScreens.CloseTab; } else if (props.featureState.loginAttemptInfo && props.featureState.loginAttemptInfo.flowType === "MAGIC_LINK") { diff --git a/lib/build/recipe/multifactorauth/index.d.ts b/lib/build/recipe/multifactorauth/index.d.ts index 02c7496be..c04d19aaa 100644 --- a/lib/build/recipe/multifactorauth/index.d.ts +++ b/lib/build/recipe/multifactorauth/index.d.ts @@ -11,7 +11,7 @@ export default class Wrapper { ): import("../../types").RecipeInitResult< GetRedirectionURLContext, "GET_MFA_INFO", - OnHandleEventContext, + never, import("./types").NormalisedConfig >; static getMFAInfo(input?: { userContext?: any; options?: RecipeFunctionOptions }): Promise<{ diff --git a/lib/build/recipe/passwordless/utils.d.ts b/lib/build/recipe/passwordless/utils.d.ts index a5b28e889..c6762d875 100644 --- a/lib/build/recipe/passwordless/utils.d.ts +++ b/lib/build/recipe/passwordless/utils.d.ts @@ -1,2 +1,7 @@ import type { Config, NormalisedConfig } from "./types"; +import type { DynamicLoginMethodsContextValue } from "../multitenancy/dynamicLoginMethodsContext"; export declare function normalisePasswordlessConfig(config: Config): NormalisedConfig; +export declare function getEnabledContactMethods( + contactMethod: "PHONE" | "EMAIL" | "EMAIL_OR_PHONE", + currentDynamicLoginMethods: DynamicLoginMethodsContextValue +): string[]; diff --git a/lib/build/recipe/totp/index.d.ts b/lib/build/recipe/totp/index.d.ts index ce8248b52..d20d92047 100644 --- a/lib/build/recipe/totp/index.d.ts +++ b/lib/build/recipe/totp/index.d.ts @@ -29,7 +29,13 @@ export default class Wrapper { >; static verifyCode(input: { totp: string; options?: RecipeFunctionOptions; userContext?: any }): Promise< | { - status: "OK" | "INVALID_TOTP_ERROR"; + status: "OK"; + fetchResponse: Response; + } + | { + status: "INVALID_TOTP_ERROR"; + failedTOTPAttemptCount: number; + maximumTOTPAttemptCount: number; fetchResponse: Response; } | { @@ -50,7 +56,13 @@ export default class Wrapper { fetchResponse: Response; } | { - status: "INVALID_TOTP_ERROR" | "UNKNOWN_DEVICE_ERROR"; + status: "INVALID_TOTP_ERROR"; + failedTOTPAttemptCount: number; + maximumTOTPAttemptCount: number; + fetchResponse: Response; + } + | { + status: "UNKNOWN_DEVICE_ERROR"; fetchResponse: Response; } | { diff --git a/lib/ts/components/errorBoundary.tsx b/lib/ts/components/errorBoundary.tsx deleted file mode 100644 index 0cab7dbbf..000000000 --- a/lib/ts/components/errorBoundary.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/* - * Imports. - */ -import React from "react"; -import { Fragment } from "react"; - -import type { ErrorInfo, ReactNode } from "react"; - -type ErrorBoundaryState = { hasError: boolean }; -/* - * Component. - */ -export default class ErrorBoundary extends React.Component { - constructor(props: { hasError: boolean }) { - super(props); - this.state = { hasError: false }; - } - - static getDerivedStateFromError(): ErrorBoundaryState { - return { hasError: true }; - } - - componentDidCatch(error: Error, errorInfo: ErrorInfo): void { - console.info(error, errorInfo); - } - - render(): JSX.Element | ReactNode | undefined { - if (this.state.hasError) { - return ; - } - - return this.props.children; - } -} diff --git a/lib/ts/components/featureWrapper.tsx b/lib/ts/components/featureWrapper.tsx index 6a4366767..dea33ce57 100644 --- a/lib/ts/components/featureWrapper.tsx +++ b/lib/ts/components/featureWrapper.tsx @@ -29,8 +29,6 @@ import { TranslationContextProvider } from "../translation/translationContext"; import { useUserContext } from "../usercontext"; import { mergeObjects } from "../utils"; -import ErrorBoundary from "./errorBoundary"; - import type { GetLoginMethodsResponseNormalized } from "../recipe/multitenancy/types"; import type { TranslationStore } from "../translation/translationHelpers"; import type { PropsWithChildren } from "react"; @@ -74,15 +72,13 @@ export default function FeatureWrapper({ return ( - - - {children} - - + + {children} + ); } diff --git a/lib/ts/recipe/multitenancy/components/features/dynamicLoginMethodsSpinner/index.tsx b/lib/ts/recipe/multitenancy/components/features/dynamicLoginMethodsSpinner/index.tsx index 2680f8c70..b801a5328 100644 --- a/lib/ts/recipe/multitenancy/components/features/dynamicLoginMethodsSpinner/index.tsx +++ b/lib/ts/recipe/multitenancy/components/features/dynamicLoginMethodsSpinner/index.tsx @@ -13,7 +13,6 @@ * under the License. */ import { ComponentOverrideContext } from "../../../../../components/componentOverride/componentOverrideContext"; -import ErrorBoundary from "../../../../../components/errorBoundary"; import { WithOrWithoutShadowDom } from "../../../../../components/featureWrapper"; import { useRecipeComponentOverrideContext } from "../../../componentOverrideContext"; import Multitenancy from "../../../recipe"; @@ -28,11 +27,9 @@ const DynamicLoginMethodsSpinner: React.FC = () => { return ( - - - - - + + + ); }; diff --git a/lib/ts/recipe/passwordless/components/themes/signInUp/index.tsx b/lib/ts/recipe/passwordless/components/themes/signInUp/index.tsx index eeb94226c..b2ddd22c8 100644 --- a/lib/ts/recipe/passwordless/components/themes/signInUp/index.tsx +++ b/lib/ts/recipe/passwordless/components/themes/signInUp/index.tsx @@ -21,8 +21,8 @@ import { SuperTokensBranding } from "../../../../../components/SuperTokensBrandi import { hasFontDefined } from "../../../../../styles/styles"; import UserContextWrapper from "../../../../../usercontext/userContextWrapper"; import GeneralError from "../../../../emailpassword/components/library/generalError"; -import MultiFactorAuth from "../../../../multifactorauth/recipe"; import { useDynamicLoginMethods } from "../../../../multitenancy/dynamicLoginMethodsContext"; +import { getEnabledContactMethods } from "../../../utils"; import { ThemeBase } from "../themeBase"; import { CloseTabScreen } from "./closeTabScreen"; @@ -137,32 +137,7 @@ export function getActiveScreen( props: Pick, currentDynamicLoginMethods: DynamicLoginMethodsContextValue ) { - const contactMethod = props.config.contactMethod; - let enabledContactMethods = contactMethod === "EMAIL_OR_PHONE" ? ["EMAIL", "PHONE"] : [contactMethod]; - - let firstFactors; - if (currentDynamicLoginMethods.loaded && currentDynamicLoginMethods.loginMethods.firstFactors) { - firstFactors = currentDynamicLoginMethods.loginMethods.firstFactors; - } else { - firstFactors = MultiFactorAuth.getInstance()?.config.firstFactors; - } - - if (firstFactors !== undefined) { - if (enabledContactMethods.includes("PHONE")) { - if (!firstFactors.includes("otp-phone") && !firstFactors.includes("link-phone")) { - enabledContactMethods = enabledContactMethods.filter((c) => c !== "PHONE"); - } - } - if (enabledContactMethods.includes("EMAIL")) { - if (!firstFactors.includes("otp-email") && !firstFactors.includes("link-email")) { - enabledContactMethods = enabledContactMethods.filter((c) => c !== "EMAIL"); - } - } - } - - if (enabledContactMethods.length === 0) { - throw new Error("No overlap between first factors and enabled contact methods"); // redirect to access denied? - } + const enabledContactMethods = getEnabledContactMethods(props.config.contactMethod, currentDynamicLoginMethods); if (props.featureState.successInAnotherTab) { return SignInUpScreens.CloseTab; @@ -177,5 +152,6 @@ export function getActiveScreen( } else if (enabledContactMethods[0] === "PHONE") { return SignInUpScreens.PhoneForm; } + throw new Error("Couldn't choose active screen; Should never happen"); } diff --git a/lib/ts/recipe/passwordless/utils.ts b/lib/ts/recipe/passwordless/utils.ts index cc5d40eeb..8906e7af6 100644 --- a/lib/ts/recipe/passwordless/utils.ts +++ b/lib/ts/recipe/passwordless/utils.ts @@ -14,6 +14,7 @@ */ import { normaliseAuthRecipe } from "../authRecipe/utils"; +import MultiFactorAuth from "../multifactorauth/recipe"; import { defaultPhoneNumberValidator, @@ -25,6 +26,7 @@ import { import type { Config, NormalisedConfig, SignInUpFeatureConfigInput } from "./types"; import type { FeatureBaseConfig, NormalisedBaseConfig } from "../../types"; +import type { DynamicLoginMethodsContextValue } from "../multitenancy/dynamicLoginMethodsContext"; import type { RecipeInterface } from "supertokens-web-js/recipe/passwordless"; export function normalisePasswordlessConfig(config: Config): NormalisedConfig { @@ -137,3 +139,45 @@ function normalisePasswordlessBaseConfig(config?: T & FeatureBaseConfig): T & style, }; } + +export function getEnabledContactMethods( + contactMethod: "PHONE" | "EMAIL" | "EMAIL_OR_PHONE", + currentDynamicLoginMethods: DynamicLoginMethodsContextValue +) { + let enabledContactMethods = contactMethod === "EMAIL_OR_PHONE" ? ["EMAIL", "PHONE"] : [contactMethod]; + + let firstFactors; + if (currentDynamicLoginMethods.loaded && currentDynamicLoginMethods.loginMethods.firstFactors) { + firstFactors = currentDynamicLoginMethods.loginMethods.firstFactors; + } else { + firstFactors = MultiFactorAuth.getInstance()?.config.firstFactors; + } + + if (firstFactors !== undefined) { + if (enabledContactMethods.includes("PHONE")) { + if (!firstFactors.includes("otp-phone") && !firstFactors.includes("link-phone")) { + enabledContactMethods = enabledContactMethods.filter((c) => c !== "PHONE"); + } + } else { + if (firstFactors.includes("otp-phone") || firstFactors.includes("link-phone")) { + throw new Error("The enabled contact method is not a superset of the requested first factors"); + } + } + + if (enabledContactMethods.includes("EMAIL")) { + if (!firstFactors.includes("otp-email") && !firstFactors.includes("link-email")) { + enabledContactMethods = enabledContactMethods.filter((c) => c !== "EMAIL"); + } + } else { + if (firstFactors.includes("otp-email") || firstFactors.includes("link-email")) { + throw new Error("The enabled contact method is not a superset of the requested first factors"); + } + } + } + + if (enabledContactMethods.length === 0) { + // It should never get here, but as a sanity check this is fine + throw new Error("The enabled contact method is not a superset of the requested first factors"); + } + return enabledContactMethods; +}