diff --git a/CHANGELOG.md b/CHANGELOG.md index 811b4d3d6..872d9af09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Introducing MFA -With this release, we are introducing MultiFactorAuthentication and TOTP, this will let you: +With this release, we are introducing MultiFactorAuth and TOTP, this will let you: - require (2FA or MFA) during sign in - make use of our TOTP diff --git a/lib/build/components/assets/otpIcon.d.ts b/lib/build/components/assets/otpIcon.d.ts deleted file mode 100644 index bcb7b9c12..000000000 --- a/lib/build/components/assets/otpIcon.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -export declare const OTPIcon: () => JSX.Element; diff --git a/lib/build/multifactorauth-shared.js b/lib/build/multifactorauth-shared.js index a0a27c28d..b81459a84 100644 --- a/lib/build/multifactorauth-shared.js +++ b/lib/build/multifactorauth-shared.js @@ -219,7 +219,7 @@ var MultiFactorAuth = /** @class */ (function (_super) { return [ 2 /*return*/, this.config.appInfo.websiteBasePath - .appendPath(redirectInfo.path) + .appendPath(new NormalisedURLPath__default.default(redirectInfo.path)) .getAsStringDangerous(), ]; } diff --git a/lib/build/passwordless-shared2.js b/lib/build/passwordless-shared2.js index bd9595f66..90bcc72d8 100644 --- a/lib/build/passwordless-shared2.js +++ b/lib/build/passwordless-shared2.js @@ -2,11 +2,7 @@ var genericComponentOverrideContext = require("./genericComponentOverrideContext.js"); var PasswordlessWebJS = require("supertokens-web-js/recipe/passwordless"); -var NormalisedURLPath = require("supertokens-web-js/utils/normalisedURLPath"); -var postSuperTokensInitCallbacks = require("supertokens-web-js/utils/postSuperTokensInitCallbacks"); -var jsxRuntime = require("react/jsx-runtime"); var utils = require("./authRecipe-shared.js"); -var recipe = require("./multifactorauth-shared.js"); var windowHandler = require("supertokens-web-js/utils/windowHandler"); function _interopDefault(e) { @@ -14,57 +10,6 @@ function _interopDefault(e) { } var PasswordlessWebJS__default = /*#__PURE__*/ _interopDefault(PasswordlessWebJS); -var NormalisedURLPath__default = /*#__PURE__*/ _interopDefault(NormalisedURLPath); - -var OTPIcon = function () { - return jsxRuntime.jsxs( - "svg", - genericComponentOverrideContext.__assign( - { xmlns: "http://www.w3.org/2000/svg", width: "22", height: "23", viewBox: "0 4 22 23", fill: "none" }, - { - children: [ - jsxRuntime.jsx("path", { - d: "M7.56428 15.8664L5.26639 18.1643C5.11403 18.3167 4.90738 18.4023 4.69192 18.4023C4.47645 18.4023 4.2698 18.3167 4.11744 18.1643C3.96508 18.012 3.87949 17.8053 3.87949 17.5898C3.87949 17.3744 3.96508 17.1677 4.11744 17.0154L6.41533 14.7175C6.56769 14.5651 6.77434 14.4795 6.98981 14.4795C7.20528 14.4795 7.41192 14.5651 7.56428 14.7175C7.71664 14.8698 7.80224 15.0765 7.80224 15.292C7.80224 15.5074 7.71664 15.7141 7.56428 15.8664Z", - fill: "#73B6FF", - }), - jsxRuntime.jsx("path", { - d: "M5.17026 13.4675L1.72342 16.9143C1.57106 17.0667 1.36442 17.1523 1.14895 17.1523C0.933477 17.1523 0.726833 17.0667 0.574473 16.9143C0.422113 16.762 0.336519 16.5553 0.336519 16.3398C0.336519 16.1244 0.422113 15.9177 0.574473 15.7654L4.02131 12.3185C4.17367 12.1662 4.38032 12.0806 4.59579 12.0806C4.81125 12.0806 5.0179 12.1662 5.17026 12.3185C5.32262 12.4709 5.40821 12.6775 5.40821 12.893C5.40821 13.1085 5.32262 13.3151 5.17026 13.4675Z", - fill: "#73B6FF", - }), - jsxRuntime.jsx("path", { - d: "M10.0492 18.1315L6.60233 21.5784C6.44997 21.7307 6.24332 21.8163 6.02785 21.8163C5.81238 21.8163 5.60574 21.7307 5.45338 21.5784C5.30102 21.426 5.21542 21.2194 5.21542 21.0039C5.21542 20.7884 5.30102 20.5818 5.45338 20.4294L8.90022 16.9826C9.05258 16.8302 9.25922 16.7446 9.47469 16.7446C9.69016 16.7446 9.89681 16.8302 10.0492 16.9826C10.2015 17.135 10.2871 17.3416 10.2871 17.5571C10.2871 17.7725 10.2015 17.9792 10.0492 18.1315Z", - fill: "#73B6FF", - }), - jsxRuntime.jsx("path", { - d: "M16.935 4.28797L4.96211 8.24925C4.38349 8.47179 4.29448 9.27295 4.87309 9.49549L8.96789 11.3649L14.8876 7.13653L10.6592 13.0562L12.5286 17.151C12.7956 17.6851 13.5523 17.6406 13.7303 17.0175L17.6916 5.04462C17.8696 4.59953 17.4246 4.15444 16.935 4.28797Z", - fill: "url(#paint0_linear_3828_8733)", - }), - jsxRuntime.jsx("defs", { - children: jsxRuntime.jsxs( - "linearGradient", - genericComponentOverrideContext.__assign( - { - id: "paint0_linear_3828_8733", - x1: "16.9576", - y1: "4.56946", - x2: "9.50502", - y2: "12.0221", - gradientUnits: "userSpaceOnUse", - }, - { - children: [ - jsxRuntime.jsx("stop", { stopColor: "#5FABFF" }), - jsxRuntime.jsx("stop", { offset: "1", stopColor: "#1485FF" }), - ], - } - ) - ), - }), - ], - } - ) - ); -}; var getFunctionOverrides = function (onHandleEvent) { return function (originalImp) { @@ -549,27 +494,6 @@ var Passwordless = /** @class */ (function (_super) { } Passwordless.init = function (config) { var normalisedConfig = normalisePasswordlessConfig(config); - postSuperTokensInitCallbacks.PostSuperTokensInitCallbacks.addPostInitCallback(function () { - var mfa = recipe.MultiFactorAuth.getInstance(); - if (mfa !== undefined) { - mfa.addMFAFactors([ - { - id: "otp-phone", - name: "SMS based OTP", - description: "Get an OTP code on your phone to complete the authentication request", - path: new NormalisedURLPath__default.default("/check-auth/otp-phone"), - logo: OTPIcon, - }, - { - id: "otp-email", - name: "SMS based OTP", - description: "Get an OTP code on your email address to complete the authentication request", - path: new NormalisedURLPath__default.default("/check-auth/otp-email"), - logo: OTPIcon, - }, - ]); - } - }); return { recipeID: Passwordless.RECIPE_ID, authReact: function (appInfo) { diff --git a/lib/build/passwordless.js b/lib/build/passwordless.js index ce950e342..f8cba3862 100644 --- a/lib/build/passwordless.js +++ b/lib/build/passwordless.js @@ -20,9 +20,6 @@ require("./authRecipe-shared.js"); require("./recipeModule-shared.js"); require("./session-shared2.js"); require("supertokens-web-js/recipe/session"); -require("./multifactorauth-shared.js"); -require("supertokens-web-js/recipe/multifactorauth"); -require("supertokens-web-js/utils/sessionClaimValidatorStore"); /* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. * 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/multifactorauth/types.d.ts b/lib/build/recipe/multifactorauth/types.d.ts index 93d7086d8..e861b76c0 100644 --- a/lib/build/recipe/multifactorauth/types.d.ts +++ b/lib/build/recipe/multifactorauth/types.d.ts @@ -7,7 +7,6 @@ import type { } from "../recipeModule/types"; import type { FC } from "react"; import type { OverrideableBuilder } from "supertokens-js-override"; -import type NormalisedURLPath from "supertokens-web-js/lib/build/normalisedURLPath"; import type { RecipeInterface } from "supertokens-web-js/recipe/multifactorauth"; import type { MFAFactorInfo } from "supertokens-web-js/recipe/multifactorauth/types"; export declare type ComponentOverrideMap = { @@ -54,10 +53,7 @@ export declare type PreAPIHookContext = { url: string; userContext: any; }; -export declare type OnHandleEventContext = { - action: "FACTOR_CHOSEN"; - userContext: any; -}; +export declare type OnHandleEventContext = never; export declare type FactorChooserThemeProps = { mfaInfo: MFAFactorInfo; availableFactors: SecondaryFactorRedirectionInfo[]; @@ -73,5 +69,5 @@ export declare type SecondaryFactorRedirectionInfo = { name: string; description: string; logo: FC; - path: NormalisedURLPath; + path: string; }; diff --git a/lib/build/recipe/passwordless/components/features/mfa/index.d.ts b/lib/build/recipe/passwordless/components/features/mfa/index.d.ts deleted file mode 100644 index 02b7a8daa..000000000 --- a/lib/build/recipe/passwordless/components/features/mfa/index.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as React from "react"; -import type { FeatureBaseProps } from "../../../../../types"; -import type Recipe from "../../../recipe"; -import type { ComponentOverrideMap } from "../../../types"; -import type { PasswordlessSignInUpAction, SignInUpState, SignInUpChildProps } from "../../../types"; -import type { RecipeInterface } from "supertokens-web-js/recipe/passwordless"; -export declare const useSuccessInAnotherTabChecker: ( - state: SignInUpState, - dispatch: React.Dispatch, - userContext: any -) => React.MutableRefObject; -export declare const useFeatureReducer: ( - recipeImpl: RecipeInterface | undefined, - userContext: any -) => [SignInUpState, React.Dispatch]; -export declare function useChildProps( - recipe: Recipe, - dispatch: React.Dispatch, - state: SignInUpState, - callingConsumeCodeRef: React.MutableRefObject, - userContext: any, - history: any -): SignInUpChildProps; -export declare function useChildProps( - recipe: Recipe | undefined, - dispatch: React.Dispatch, - state: SignInUpState, - callingConsumeCodeRef: React.MutableRefObject, - userContext: any, - history: any -): SignInUpChildProps | undefined; -export declare const SignInUpFeature: React.FC< - FeatureBaseProps & { - recipe: Recipe; - useComponentOverrides: () => ComponentOverrideMap; - } ->; -export default SignInUpFeature; diff --git a/lib/build/thirdpartypasswordless.js b/lib/build/thirdpartypasswordless.js index 796cbe40c..7de9631c9 100644 --- a/lib/build/thirdpartypasswordless.js +++ b/lib/build/thirdpartypasswordless.js @@ -26,9 +26,6 @@ require("supertokens-web-js/lib/build/normalisedURLPath"); require("supertokens-web-js/recipe/thirdpartypasswordless"); require("./passwordless-shared2.js"); require("supertokens-web-js/recipe/passwordless"); -require("./multifactorauth-shared.js"); -require("supertokens-web-js/recipe/multifactorauth"); -require("supertokens-web-js/utils/sessionClaimValidatorStore"); var Wrapper = /** @class */ (function () { function Wrapper() {} diff --git a/lib/ts/components/assets/otpIcon.tsx b/lib/ts/components/assets/otpIcon.tsx deleted file mode 100644 index 5c0e1c977..000000000 --- a/lib/ts/components/assets/otpIcon.tsx +++ /dev/null @@ -1,32 +0,0 @@ -export const OTPIcon = () => ( - - - - - - - - - - - - -); diff --git a/lib/ts/recipe/multifactorauth/recipe.tsx b/lib/ts/recipe/multifactorauth/recipe.tsx index ba7e88b10..5caab85c9 100644 --- a/lib/ts/recipe/multifactorauth/recipe.tsx +++ b/lib/ts/recipe/multifactorauth/recipe.tsx @@ -135,7 +135,9 @@ export default class MultiFactorAuth extends RecipeModule< } else if (context.action === "GO_TO_FACTOR") { const redirectInfo = this.getSecondaryFactors().find((f) => f.id === context.factorId); if (redirectInfo !== undefined) { - return this.config.appInfo.websiteBasePath.appendPath(redirectInfo.path).getAsStringDangerous(); + return this.config.appInfo.websiteBasePath + .appendPath(new NormalisedURLPath(redirectInfo.path)) + .getAsStringDangerous(); } throw new Error("Requested redirect to unknown factor id"); } else { diff --git a/lib/ts/recipe/multifactorauth/types.ts b/lib/ts/recipe/multifactorauth/types.ts index 08b22e940..a47873af0 100644 --- a/lib/ts/recipe/multifactorauth/types.ts +++ b/lib/ts/recipe/multifactorauth/types.ts @@ -22,7 +22,6 @@ import type { } from "../recipeModule/types"; import type { FC } from "react"; import type { OverrideableBuilder } from "supertokens-js-override"; -import type NormalisedURLPath from "supertokens-web-js/lib/build/normalisedURLPath"; import type { RecipeInterface } from "supertokens-web-js/recipe/multifactorauth"; import type { MFAFactorInfo } from "supertokens-web-js/recipe/multifactorauth/types"; @@ -81,10 +80,7 @@ export type PreAPIHookContext = { userContext: any; }; -export type OnHandleEventContext = { - action: "FACTOR_CHOSEN"; - userContext: any; -}; +export type OnHandleEventContext = never; export type FactorChooserThemeProps = { mfaInfo: MFAFactorInfo; @@ -102,5 +98,5 @@ export type SecondaryFactorRedirectionInfo = { name: string; description: string; logo: FC; - path: NormalisedURLPath; + path: string; }; diff --git a/lib/ts/recipe/passwordless/components/features/mfa/index.tsx b/lib/ts/recipe/passwordless/components/features/mfa/index.tsx deleted file mode 100644 index 49eb6f92f..000000000 --- a/lib/ts/recipe/passwordless/components/features/mfa/index.tsx +++ /dev/null @@ -1,397 +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 * as React from "react"; -import { Fragment } from "react"; -import { useMemo } from "react"; -import { useRef } from "react"; -import { useEffect } from "react"; - -import { ComponentOverrideContext } from "../../../../../components/componentOverride/componentOverrideContext"; -import FeatureWrapper from "../../../../../components/featureWrapper"; -import { useUserContext } from "../../../../../usercontext"; -import { clearErrorQueryParam, getQueryParams, getRedirectToPathFromURL } from "../../../../../utils"; -import Session from "../../../../session"; -import SessionRecipe from "../../../../session/recipe"; -import { getPhoneNumberUtils } from "../../../phoneNumberUtils"; -import SignInUpThemeWrapper from "../../themes/signInUp"; -import { defaultTranslationsPasswordless } from "../../themes/translations"; - -import type { FeatureBaseProps } from "../../../../../types"; -import type Recipe from "../../../recipe"; -import type { AdditionalLoginAttemptInfoProperties, ComponentOverrideMap } from "../../../types"; -import type { PasswordlessSignInUpAction, SignInUpState, SignInUpChildProps, NormalisedConfig } from "../../../types"; -import type { RecipeInterface } from "supertokens-web-js/recipe/passwordless"; -import type { User } from "supertokens-web-js/types"; - -export const useSuccessInAnotherTabChecker = ( - state: SignInUpState, - dispatch: React.Dispatch, - userContext: any -) => { - const callingConsumeCodeRef = useRef(false); - - useEffect(() => { - // We only need to start checking this if we have an active login attempt - if (state.loginAttemptInfo && !state.successInAnotherTab) { - const checkSessionIntervalHandle = setInterval(async () => { - if (callingConsumeCodeRef.current === false) { - const hasSession = await Session.doesSessionExist({ - userContext, - }); - if (hasSession) { - dispatch({ type: "successInAnotherTab" }); - } - } - }, 2000); - - return () => { - clearInterval(checkSessionIntervalHandle); - }; - } - // Nothing to clean up - return; - }, [state.loginAttemptInfo, state.successInAnotherTab]); - - return callingConsumeCodeRef; -}; - -export const useFeatureReducer = ( - recipeImpl: RecipeInterface | undefined, - userContext: any -): [SignInUpState, React.Dispatch] => { - const [state, dispatch] = React.useReducer( - (oldState: SignInUpState, action: PasswordlessSignInUpAction) => { - switch (action.type) { - case "load": - return { - loaded: true, - error: action.error, - loginAttemptInfo: action.loginAttemptInfo, - successInAnotherTab: false, - }; - case "resendCode": - if (!oldState.loginAttemptInfo) { - return oldState; - } - return { - ...oldState, - error: undefined, - loginAttemptInfo: { - ...oldState.loginAttemptInfo, - lastResend: action.timestamp, - }, - }; - case "restartFlow": - return { - ...oldState, - error: action.error, - loginAttemptInfo: undefined, - }; - case "setError": - return { - ...oldState, - error: action.error, - }; - case "startLogin": - return { - ...oldState, - loginAttemptInfo: action.loginAttemptInfo, - error: undefined, - }; - case "successInAnotherTab": - return { - ...oldState, - successInAnotherTab: true, - }; - default: - return oldState; - } - }, - { - error: undefined, - loaded: false, - loginAttemptInfo: undefined, - successInAnotherTab: false, - }, - (initArg) => { - let error: string | undefined = undefined; - const errorQueryParam = getQueryParams("error"); - const messageQueryParam = getQueryParams("message"); - if (errorQueryParam !== null) { - if (errorQueryParam === "signin") { - error = "SOMETHING_WENT_WRONG_ERROR"; - } else if (errorQueryParam === "restart_link") { - error = "ERROR_SIGN_IN_UP_LINK"; - } else if (errorQueryParam === "custom" && messageQueryParam !== null) { - error = messageQueryParam; - } - } - return { - ...initArg, - error, - }; - } - ); - useEffect(() => { - if (recipeImpl === undefined) { - return; - } - async function load() { - let error: string | undefined = undefined; - const errorQueryParam = getQueryParams("error"); - const messageQueryParam = getQueryParams("message"); - if (errorQueryParam !== null) { - if (errorQueryParam === "signin") { - error = "SOMETHING_WENT_WRONG_ERROR"; - } else if (errorQueryParam === "restart_link") { - error = "ERROR_SIGN_IN_UP_LINK"; - } else if (errorQueryParam === "custom" && messageQueryParam !== null) { - error = messageQueryParam; - } - } - const loginAttemptInfo = await recipeImpl?.getLoginAttemptInfo({ - userContext, - }); - // No need to check if the component is unmounting, since this has no effect then. - dispatch({ type: "load", loginAttemptInfo, error }); - } - if (state.loaded === false) { - void load(); - } - }, [state.loaded, recipeImpl, userContext]); - return [state, dispatch]; -}; - -// We are overloading to explicitly state that if recipe is defined then the return value is defined as well. -export function useChildProps( - recipe: Recipe, - dispatch: React.Dispatch, - state: SignInUpState, - callingConsumeCodeRef: React.MutableRefObject, - userContext: any, - history: any -): SignInUpChildProps; -export function useChildProps( - recipe: Recipe | undefined, - dispatch: React.Dispatch, - state: SignInUpState, - callingConsumeCodeRef: React.MutableRefObject, - userContext: any, - history: any -): SignInUpChildProps | undefined; - -export function useChildProps( - recipe: Recipe | undefined, - dispatch: React.Dispatch, - state: SignInUpState, - callingConsumeCodeRef: React.MutableRefObject, - userContext: any, - history: any -): SignInUpChildProps | undefined { - const recipeImplementation = React.useMemo( - () => - recipe && - getModifiedRecipeImplementation(recipe.webJSRecipe, recipe.config, dispatch, callingConsumeCodeRef), - [recipe] - ); - - return useMemo(() => { - if (!recipe || !recipeImplementation) { - return undefined; - } - return { - onSuccess: (result: { createdNewRecipeUser: boolean; user: User }) => { - return SessionRecipe.getInstanceOrThrow().validateGlobalClaimsAndHandleSuccessRedirection( - { - rid: recipe.config.recipeId, - successRedirectContext: { - action: "SUCCESS", - isNewRecipeUser: result.createdNewRecipeUser, - user: result.user, - redirectToPath: getRedirectToPathFromURL(), - }, - }, - userContext, - history - ); - }, - recipeImplementation: recipeImplementation, - config: recipe.config, - }; - }, [state, recipeImplementation]); -} - -export const SignInUpFeature: React.FC< - FeatureBaseProps & { - recipe: Recipe; - useComponentOverrides: () => ComponentOverrideMap; - } -> = (props) => { - const recipeComponentOverrides = props.useComponentOverrides(); - const userContext = useUserContext(); - const [state, dispatch] = useFeatureReducer(props.recipe.webJSRecipe, userContext); - const callingConsumeCodeRef = useSuccessInAnotherTabChecker(state, dispatch, userContext); - const childProps = useChildProps(props.recipe, dispatch, state, callingConsumeCodeRef, userContext, props.history)!; - - return ( - - - - {/* No custom theme, use default. */} - {props.children === undefined && ( - - )} - - {/* Otherwise, custom theme is provided, propagate props. */} - {props.children && - React.Children.map(props.children, (child) => { - if (React.isValidElement(child)) { - return React.cloneElement(child, { - ...childProps, - featureState: state, - dispatch: dispatch, - }); - } - return child; - })} - - - - ); -}; - -export default SignInUpFeature; - -function getModifiedRecipeImplementation( - originalImpl: RecipeInterface, - config: NormalisedConfig, - dispatch: React.Dispatch, - callingConsumeCodeRef: React.MutableRefObject -): RecipeInterface { - return { - ...originalImpl, - createCode: async (input) => { - let contactInfo; - const phoneNumberUtils = await getPhoneNumberUtils(); - if ("email" in input) { - contactInfo = input.email; - } else { - contactInfo = phoneNumberUtils.formatNumber( - input.phoneNumber, - config.signInUpFeature.defaultCountry || "", - phoneNumberUtils.numberFormat.E164 - ); - } - - // This contactMethod refers to the one that was used to deliver the login info - // This can be an important distinction in case both email and phone are allowed - const contactMethod: "EMAIL" | "PHONE" = "email" in input ? "EMAIL" : "PHONE"; - const additionalAttemptInfo = { - lastResend: Date.now(), - contactMethod, - contactInfo, - redirectToPath: getRedirectToPathFromURL(), - }; - - const res = await originalImpl.createCode({ - ...input, - userContext: { ...input.userContext, additionalAttemptInfo }, - }); - if (res.status === "OK") { - const loginAttemptInfo = (await originalImpl.getLoginAttemptInfo({ - userContext: input.userContext, - }))!; - dispatch({ type: "startLogin", loginAttemptInfo }); - } - return res; - }, - resendCode: async (input) => { - /** - * In this case we want the code that is calling resendCode in the - * UI to handle STGeneralError so we let this throw - */ - const res = await originalImpl.resendCode(input); - - if (res.status === "OK") { - const loginAttemptInfo = await originalImpl.getLoginAttemptInfo({ - userContext: input.userContext, - }); - - if (loginAttemptInfo !== undefined) { - const timestamp = Date.now(); - - await originalImpl.setLoginAttemptInfo({ - userContext: input.userContext, - attemptInfo: { - ...loginAttemptInfo, - lastResend: timestamp, - }, - }); - dispatch({ type: "resendCode", timestamp }); - } - } else if (res.status === "RESTART_FLOW_ERROR") { - await originalImpl.clearLoginAttemptInfo({ - userContext: input.userContext, - }); - - dispatch({ type: "restartFlow", error: "ERROR_SIGN_IN_UP_RESEND_RESTART_FLOW" }); - } - return res; - }, - - consumeCode: async (input) => { - // We need to call consume code while callingConsume, so we don't detect - // the session creation too early and go to successInAnotherTab too early - callingConsumeCodeRef.current = true; - - const res = await originalImpl.consumeCode(input); - - if (res.status === "RESTART_FLOW_ERROR") { - await originalImpl.clearLoginAttemptInfo({ - userContext: input.userContext, - }); - - dispatch({ type: "restartFlow", error: "ERROR_SIGN_IN_UP_CODE_CONSUME_RESTART_FLOW" }); - } else if (res.status === "SIGN_IN_UP_NOT_ALLOWED") { - await originalImpl.clearLoginAttemptInfo({ - userContext: input.userContext, - }); - - dispatch({ type: "restartFlow", error: res.reason }); - } else if (res.status === "OK") { - await originalImpl.clearLoginAttemptInfo({ - userContext: input.userContext, - }); - } - - callingConsumeCodeRef.current = false; - - return res; - }, - - clearLoginAttemptInfo: async (input) => { - await originalImpl.clearLoginAttemptInfo({ - userContext: input.userContext, - }); - clearErrorQueryParam(); - dispatch({ type: "restartFlow", error: undefined }); - }, - }; -} diff --git a/lib/ts/recipe/passwordless/recipe.tsx b/lib/ts/recipe/passwordless/recipe.tsx index 0f33eb395..7974b587d 100644 --- a/lib/ts/recipe/passwordless/recipe.tsx +++ b/lib/ts/recipe/passwordless/recipe.tsx @@ -18,14 +18,10 @@ */ import PasswordlessWebJS from "supertokens-web-js/recipe/passwordless"; -import NormalisedURLPath from "supertokens-web-js/utils/normalisedURLPath"; -import { PostSuperTokensInitCallbacks } from "supertokens-web-js/utils/postSuperTokensInitCallbacks"; -import { OTPIcon } from "../../components/assets/otpIcon"; import { SSR_ERROR } from "../../constants"; import { isTest } from "../../utils"; import AuthRecipe from "../authRecipe"; -import MultiFactorAuth from "../multifactorauth/recipe"; import { getFunctionOverrides } from "./functionOverrides"; import { normalisePasswordlessConfig } from "./utils"; @@ -73,28 +69,6 @@ export default class Passwordless extends AuthRecipe< ): RecipeInitResult { const normalisedConfig = normalisePasswordlessConfig(config); - PostSuperTokensInitCallbacks.addPostInitCallback(() => { - const mfa = MultiFactorAuth.getInstance(); - if (mfa !== undefined) { - mfa.addMFAFactors([ - { - id: "otp-phone", - name: "SMS based OTP", - description: "Get an OTP code on your phone to complete the authentication request", - path: new NormalisedURLPath("/check-auth/otp-phone"), - logo: OTPIcon, - }, - { - id: "otp-email", - name: "SMS based OTP", - description: "Get an OTP code on your email address to complete the authentication request", - path: new NormalisedURLPath("/check-auth/otp-email"), - logo: OTPIcon, - }, - ]); - } - }); - return { recipeID: Passwordless.RECIPE_ID, authReact: ( diff --git a/test/with-typescript/src/App.tsx b/test/with-typescript/src/App.tsx index 65b0a3a5d..5ee482b75 100644 --- a/test/with-typescript/src/App.tsx +++ b/test/with-typescript/src/App.tsx @@ -37,6 +37,7 @@ import { ThirdPartyEmailPasswordPreBuiltUI } from "../../../recipe/thirdpartyema import { EmailPasswordPreBuiltUI } from "../../../recipe/emailpassword/prebuiltui"; import { AccessDeniedScreen } from "../../../recipe/session/prebuiltui"; import EmailVerification from "../../../recipe/emailverification"; +import MultiFactorAuth from "../../../recipe/multifactorauth"; /* * This application is used with the purpose of illustrating Supertokens with typescript. @@ -1280,3 +1281,15 @@ Multitenancy.init({ }), }, }); + +MultiFactorAuth.init(); +MultiFactorAuth.init({ + firstFactors: ["emailpassword", "unknown"], + factorChooserScreen: { + style: "[data-supertokens~=container] { background-color: red; }", + }, + getFactorInfo: (factors) => [ + ...factors, + { id: "asfd", logo: () =>
A
, description: "test", name: "asdf", path: "/mfa/asdf" }, + ], +});