diff --git a/lib/build/checkedRoundIcon.js b/lib/build/checkedRoundIcon.js index e50893379..56e1ae3c4 100644 --- a/lib/build/checkedRoundIcon.js +++ b/lib/build/checkedRoundIcon.js @@ -3,6 +3,97 @@ var genericComponentOverrideContext = require("./genericComponentOverrideContext.js"); var jsxRuntime = require("react/jsx-runtime"); +/* 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. + */ +/* + * Component. + */ +function EmailLargeIcon() { + return jsxRuntime.jsx( + "svg", + genericComponentOverrideContext.__assign( + { xmlns: "http://www.w3.org/2000/svg", width: "59.867", height: "40.34", viewBox: "0 0 59.867 40.34" }, + { + children: jsxRuntime.jsxs( + "g", + genericComponentOverrideContext.__assign( + { id: "email", transform: "translate(0 -83.5)" }, + { + children: [ + jsxRuntime.jsx("path", { + id: "Path_91396", + d: "M470.393 98.615h-3.508v36.805h3.508a3.031 3.031 0 0 0 .89-2.15v-32.505a3.031 3.031 0 0 0-.89-2.15z", + fill: "#8ae7ff", + transform: "translate(-412.293 -13.348)", + }), + jsxRuntime.jsx("path", { + id: "Path_91397", + d: "M115.09 100.765a3.031 3.031 0 0 0-.89-2.15H68.39a3.031 3.031 0 0 0-.89 2.15v32.506a3.031 3.031 0 0 0 .89 2.15h45.81a3.031 3.031 0 0 0 .89-2.15z", + fill: "#c4f3ff", + transform: "translate(-59.607 -13.348)", + }), + jsxRuntime.jsx("path", { + id: "Path_91398", + fill: "#4fdbff", + d: "M451.54 391l-3.04 3.508h3.508a3.031 3.031 0 0 0 2.15-.89z", + transform: "translate(-396.058 -271.545)", + }), + jsxRuntime.jsx("path", { + id: "Path_91399", + d: "M121.814 225.009v-.468L99.773 202.5l-24.658 24.658a3.031 3.031 0 0 0 2.15.89h41.509a3.04 3.04 0 0 0 3.04-3.039z", + fill: "#8ae7ff", + transform: "translate(-66.332 -105.086)", + }), + jsxRuntime.jsx("path", { + id: "Path_91400", + d: "M452.008 91H448.5l3.04 3.508 2.617-2.617a3.031 3.031 0 0 0-2.149-.891z", + fill: "#c4f3ff", + transform: "translate(-396.058 -6.623)", + }), + jsxRuntime.jsx("path", { + id: "Path_91401", + fill: "#fff", + d: "M118.774 91H77.265a3.031 3.031 0 0 0-2.15.89l20.318 20.318a6.139 6.139 0 0 0 8.681 0l17.7-17.7v-.468a3.04 3.04 0 0 0-3.04-3.04z", + transform: "translate(-66.332 -6.623)", + }), + jsxRuntime.jsx("path", { + id: "Path_91402", + d: "M55.95 83.5H10.933a3.922 3.922 0 0 0-3.917 3.917v8.36H.877a.877.877 0 1 0 0 1.754H11.4a.877.877 0 1 0 0-1.754H8.77v-8.36a2.147 2.147 0 0 1 .147-.776l17.029 17.029-17.03 17.03a2.147 2.147 0 0 1-.147-.776v-5.729a.877.877 0 1 0-1.754 0v5.729a3.922 3.922 0 0 0 3.917 3.917H55.95a3.922 3.922 0 0 0 3.917-3.917V87.417A3.922 3.922 0 0 0 55.95 83.5zm-15.013 20.17l17.03-17.029a2.147 2.147 0 0 1 .147.776v32.506a2.147 2.147 0 0 1-.147.776zM55.95 85.254a2.147 2.147 0 0 1 .776.147l-19.564 19.564a5.267 5.267 0 0 1-7.441 0L10.156 85.4a2.147 2.147 0 0 1 .776-.147zm-45.017 36.832a2.147 2.147 0 0 1-.776-.146l17.029-17.03 1.295 1.295a7.024 7.024 0 0 0 9.922 0l1.297-1.295 17.027 17.03a2.147 2.147 0 0 1-.776.146z", + }), + jsxRuntime.jsx("path", { + id: "Path_91403", + d: "M7.893 218.5a.877.877 0 0 0-.877.877v2.631H.877a.877.877 0 0 0 0 1.754h14.031a.877.877 0 0 0 0-1.754H8.77v-2.631a.877.877 0 0 0-.877-.877z", + transform: "translate(0 -119.215)", + }), + jsxRuntime.jsx("path", { + id: "Path_91404", + d: "M11.4 283.762a.877.877 0 0 0 0-1.754H8.77v-2.631a.877.877 0 1 0-1.754 0v2.631H.877a.877.877 0 0 0 0 1.754z", + transform: "translate(0 -172.199)", + }), + ], + } + ) + ), + } + ) + ); +} + /* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. * * This software is licensed under the Apache License, Version 2.0 (the @@ -60,3 +151,4 @@ function CheckedRoundIcon() { } exports.CheckedRoundIcon = CheckedRoundIcon; +exports.EmailLargeIcon = EmailLargeIcon; diff --git a/lib/build/components/assets/blockedIcon.d.ts b/lib/build/components/assets/blockedIcon.d.ts new file mode 100644 index 000000000..1832c8417 --- /dev/null +++ b/lib/build/components/assets/blockedIcon.d.ts @@ -0,0 +1,2 @@ +/// +export declare const BlockedIcon: () => JSX.Element; diff --git a/lib/build/emailLargeIcon.js b/lib/build/emailLargeIcon.js deleted file mode 100644 index 3985ac948..000000000 --- a/lib/build/emailLargeIcon.js +++ /dev/null @@ -1,97 +0,0 @@ -"use strict"; - -var genericComponentOverrideContext = require("./genericComponentOverrideContext.js"); -var jsxRuntime = require("react/jsx-runtime"); - -/* 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. - */ -/* - * Component. - */ -function EmailLargeIcon() { - return jsxRuntime.jsx( - "svg", - genericComponentOverrideContext.__assign( - { xmlns: "http://www.w3.org/2000/svg", width: "59.867", height: "40.34", viewBox: "0 0 59.867 40.34" }, - { - children: jsxRuntime.jsxs( - "g", - genericComponentOverrideContext.__assign( - { id: "email", transform: "translate(0 -83.5)" }, - { - children: [ - jsxRuntime.jsx("path", { - id: "Path_91396", - d: "M470.393 98.615h-3.508v36.805h3.508a3.031 3.031 0 0 0 .89-2.15v-32.505a3.031 3.031 0 0 0-.89-2.15z", - fill: "#8ae7ff", - transform: "translate(-412.293 -13.348)", - }), - jsxRuntime.jsx("path", { - id: "Path_91397", - d: "M115.09 100.765a3.031 3.031 0 0 0-.89-2.15H68.39a3.031 3.031 0 0 0-.89 2.15v32.506a3.031 3.031 0 0 0 .89 2.15h45.81a3.031 3.031 0 0 0 .89-2.15z", - fill: "#c4f3ff", - transform: "translate(-59.607 -13.348)", - }), - jsxRuntime.jsx("path", { - id: "Path_91398", - fill: "#4fdbff", - d: "M451.54 391l-3.04 3.508h3.508a3.031 3.031 0 0 0 2.15-.89z", - transform: "translate(-396.058 -271.545)", - }), - jsxRuntime.jsx("path", { - id: "Path_91399", - d: "M121.814 225.009v-.468L99.773 202.5l-24.658 24.658a3.031 3.031 0 0 0 2.15.89h41.509a3.04 3.04 0 0 0 3.04-3.039z", - fill: "#8ae7ff", - transform: "translate(-66.332 -105.086)", - }), - jsxRuntime.jsx("path", { - id: "Path_91400", - d: "M452.008 91H448.5l3.04 3.508 2.617-2.617a3.031 3.031 0 0 0-2.149-.891z", - fill: "#c4f3ff", - transform: "translate(-396.058 -6.623)", - }), - jsxRuntime.jsx("path", { - id: "Path_91401", - fill: "#fff", - d: "M118.774 91H77.265a3.031 3.031 0 0 0-2.15.89l20.318 20.318a6.139 6.139 0 0 0 8.681 0l17.7-17.7v-.468a3.04 3.04 0 0 0-3.04-3.04z", - transform: "translate(-66.332 -6.623)", - }), - jsxRuntime.jsx("path", { - id: "Path_91402", - d: "M55.95 83.5H10.933a3.922 3.922 0 0 0-3.917 3.917v8.36H.877a.877.877 0 1 0 0 1.754H11.4a.877.877 0 1 0 0-1.754H8.77v-8.36a2.147 2.147 0 0 1 .147-.776l17.029 17.029-17.03 17.03a2.147 2.147 0 0 1-.147-.776v-5.729a.877.877 0 1 0-1.754 0v5.729a3.922 3.922 0 0 0 3.917 3.917H55.95a3.922 3.922 0 0 0 3.917-3.917V87.417A3.922 3.922 0 0 0 55.95 83.5zm-15.013 20.17l17.03-17.029a2.147 2.147 0 0 1 .147.776v32.506a2.147 2.147 0 0 1-.147.776zM55.95 85.254a2.147 2.147 0 0 1 .776.147l-19.564 19.564a5.267 5.267 0 0 1-7.441 0L10.156 85.4a2.147 2.147 0 0 1 .776-.147zm-45.017 36.832a2.147 2.147 0 0 1-.776-.146l17.029-17.03 1.295 1.295a7.024 7.024 0 0 0 9.922 0l1.297-1.295 17.027 17.03a2.147 2.147 0 0 1-.776.146z", - }), - jsxRuntime.jsx("path", { - id: "Path_91403", - d: "M7.893 218.5a.877.877 0 0 0-.877.877v2.631H.877a.877.877 0 0 0 0 1.754h14.031a.877.877 0 0 0 0-1.754H8.77v-2.631a.877.877 0 0 0-.877-.877z", - transform: "translate(0 -119.215)", - }), - jsxRuntime.jsx("path", { - id: "Path_91404", - d: "M11.4 283.762a.877.877 0 0 0 0-1.754H8.77v-2.631a.877.877 0 1 0-1.754 0v2.631H.877a.877.877 0 0 0 0 1.754z", - transform: "translate(0 -172.199)", - }), - ], - } - ) - ), - } - ) - ); -} - -exports.EmailLargeIcon = EmailLargeIcon; diff --git a/lib/build/emailverificationprebuiltui.js b/lib/build/emailverificationprebuiltui.js index 19256f686..c35b9f47a 100644 --- a/lib/build/emailverificationprebuiltui.js +++ b/lib/build/emailverificationprebuiltui.js @@ -11,10 +11,9 @@ var recipe = require("./session-shared2.js"); var translations = require("./translations.js"); var translations$1 = require("./emailverification-shared2.js"); var STGeneralError = require("supertokens-web-js/utils/error"); -var emailLargeIcon = require("./emailLargeIcon.js"); +var checkedRoundIcon = require("./checkedRoundIcon.js"); var translationContext = require("./translationContext.js"); var generalError = require("./emailpassword-shared.js"); -var checkedRoundIcon = require("./checkedRoundIcon.js"); var button = require("./emailpassword-shared2.js"); require("supertokens-web-js"); require("supertokens-web-js/utils/cookieHandler"); @@ -261,7 +260,7 @@ var EmailVerificationSendVerifyEmail = function (props) { "div", genericComponentOverrideContext.__assign( { "data-supertokens": "sendVerifyEmailIcon" }, - { children: jsxRuntime.jsx(emailLargeIcon.EmailLargeIcon, {}) } + { children: jsxRuntime.jsx(checkedRoundIcon.EmailLargeIcon, {}) } ) ), jsxRuntime.jsx( diff --git a/lib/build/passwordless-shared4.js b/lib/build/passwordless-shared4.js index a2d8d0665..dd15c6da7 100644 --- a/lib/build/passwordless-shared4.js +++ b/lib/build/passwordless-shared4.js @@ -24,7 +24,6 @@ var validators = require("./emailpassword-shared6.js"); var arrowLeftIcon = require("./arrowLeftIcon.js"); var multifactorauth = require("./multifactorauth.js"); var backButton = require("./emailpassword-shared8.js"); -var emailLargeIcon = require("./emailLargeIcon.js"); var recipe$2 = require("./passwordless-shared2.js"); function _interopDefault(e) { @@ -4631,7 +4630,7 @@ var PasswordlessLinkSent = function (props) { { children: props.loginAttemptInfo.contactMethod === "EMAIL" - ? jsxRuntime.jsx(emailLargeIcon.EmailLargeIcon, {}) + ? jsxRuntime.jsx(checkedRoundIcon.EmailLargeIcon, {}) : jsxRuntime.jsx(SMSLargeIcon, {}), } ) diff --git a/lib/build/passwordlessprebuiltui.js b/lib/build/passwordlessprebuiltui.js index cc648d49f..44ad0b01e 100644 --- a/lib/build/passwordlessprebuiltui.js +++ b/lib/build/passwordlessprebuiltui.js @@ -41,7 +41,6 @@ require("./arrowLeftIcon.js"); require("./multifactorauth.js"); require("./multifactorauth-shared2.js"); require("./emailpassword-shared8.js"); -require("./emailLargeIcon.js"); require("supertokens-web-js/recipe/passwordless"); require("./otpIcon.js"); require("./authRecipe-shared.js"); diff --git a/lib/build/recipe/totp/components/themes/translations.d.ts b/lib/build/recipe/totp/components/themes/translations.d.ts index 4b560da2a..cb35b598d 100644 --- a/lib/build/recipe/totp/components/themes/translations.d.ts +++ b/lib/build/recipe/totp/components/themes/translations.d.ts @@ -11,6 +11,8 @@ export declare const defaultTranslationsTOTP: { TOTP_CODE_INPUT_LABEL: string; TOTP_CODE_CONTINUE_BUTTON: string; TOTP_REMOVE_DEVICE_LINK: string; + TOTP_BLOCKED_TITLE: string; + TOTP_BLOCKED_SUBTITLE: string; BRANDING_POWERED_BY_START: string; BRANDING_POWERED_BY_END: string; SOMETHING_WENT_WRONG_ERROR: string; diff --git a/lib/build/recipe/totp/types.d.ts b/lib/build/recipe/totp/types.d.ts index 2c8786159..8f7c2f471 100644 --- a/lib/build/recipe/totp/types.d.ts +++ b/lib/build/recipe/totp/types.d.ts @@ -30,6 +30,7 @@ export declare type TOTPMFAAction = | { type: "setBlocked"; error: string | undefined; + nextRetryAt: number; } | { type: "setError"; @@ -48,6 +49,7 @@ export declare type TOTPMFAAction = export declare type TOTPMFAState = { deviceInfo?: TOTPDeviceInfo; showSecret: boolean; + nextRetryAt?: number; isBlocked: boolean; loaded: boolean; error: string | undefined; @@ -63,6 +65,8 @@ export declare type TOTPMFAProps = { config: NormalisedConfig; onSuccess: () => void; onShowSecretClick: () => void; + onBackButtonClicked: () => void; + onRetryClicked: () => void; dispatch: Dispatch; featureState: TOTPMFAState; userContext?: any; @@ -75,16 +79,6 @@ export declare type TOTPMFAScreenConfig = { blockedScreenStyle: string; loadingScreenStyle: string; }; -/** - * When calling getLoginAttemptInfo/setLoginAttemptInfo from web-js we use generics to get - * access to properties in local storage that web-js does not set by default. - * This allows us to strongly type the response while keeping it dynamic. - * - * In the context of auth-react this type indicates all the additional properties we need. - */ -export declare type AdditionalDeviceInfoProperties = { - redirectToPath?: string; -}; export declare type UserInput = { totpMFAScreen?: Partial; override?: { diff --git a/lib/build/thirdpartypasswordlessprebuiltui.js b/lib/build/thirdpartypasswordlessprebuiltui.js index fa76166c7..dbd6a32a2 100644 --- a/lib/build/thirdpartypasswordlessprebuiltui.js +++ b/lib/build/thirdpartypasswordlessprebuiltui.js @@ -42,7 +42,6 @@ require("./arrowLeftIcon.js"); require("./multifactorauth.js"); require("./multifactorauth-shared2.js"); require("./emailpassword-shared8.js"); -require("./emailLargeIcon.js"); require("./passwordless-shared2.js"); require("supertokens-web-js/recipe/passwordless"); require("./otpIcon.js"); diff --git a/lib/build/totpprebuiltui.js b/lib/build/totpprebuiltui.js index f8c247b66..3b1fc1145 100644 --- a/lib/build/totpprebuiltui.js +++ b/lib/build/totpprebuiltui.js @@ -7,21 +7,20 @@ var uiEntry = require("./index2.js"); var session = require("./session-shared3.js"); var recipe$2 = require("./totp-shared.js"); var React = require("react"); +var windowHandler = require("supertokens-web-js/utils/windowHandler"); +var recipe = require("./multifactorauth-shared.js"); var recipe$1 = require("./session-shared2.js"); var SuperTokensBranding = require("./SuperTokensBranding.js"); var translations = require("./translations.js"); var generalError = require("./emailpassword-shared.js"); -var checkedRoundIcon = require("./checkedRoundIcon.js"); var translationContext = require("./translationContext.js"); var STGeneralError = require("supertokens-web-js/utils/error"); var formBase = require("./emailpassword-shared9.js"); var validators = require("./passwordless-shared3.js"); var arrowLeftIcon = require("./arrowLeftIcon.js"); -var recipe = require("./multifactorauth-shared.js"); require("supertokens-web-js"); require("supertokens-web-js/utils/cookieHandler"); require("supertokens-web-js/utils/postSuperTokensInitCallbacks"); -require("supertokens-web-js/utils/windowHandler"); require("supertokens-web-js/recipe/multitenancy"); require("supertokens-web-js/utils"); require("supertokens-web-js/utils/normalisedURLDomain"); @@ -32,10 +31,10 @@ require("./session-shared.js"); require("supertokens-web-js/recipe/totp"); require("./otpIcon.js"); require("./recipeModule-shared.js"); -require("./emailpassword-shared5.js"); -require("./emailpassword-shared2.js"); require("supertokens-web-js/recipe/multifactorauth"); require("supertokens-web-js/utils/sessionClaimValidatorStore"); +require("./emailpassword-shared5.js"); +require("./emailpassword-shared2.js"); function _interopDefault(e) { return e && e.__esModule ? e : { default: e }; @@ -92,6 +91,28 @@ var ThemeBase = function (_a) { }); }; +var BlockedIcon = function () { + return jsxRuntime.jsxs( + "svg", + genericComponentOverrideContext.__assign( + { xmlns: "http://www.w3.org/2000/svg", width: "65", height: "65", viewBox: "0 0 65 65", fill: "none" }, + { + children: [ + jsxRuntime.jsx("circle", { cx: "32.5", cy: "32.5", r: "32.5", fill: "#FCEAEB" }), + jsxRuntime.jsx("path", { + d: "M32.8804 36.5547C32.1233 36.5547 31.5039 37.1741 31.5039 37.9312C31.5039 38.6882 32.1233 39.3076 32.8804 39.3076C33.6374 39.3076 34.2568 38.6882 34.2568 37.9312C34.2568 37.1741 33.6374 36.5547 32.8804 36.5547Z", + fill: "#CF3644", + }), + jsxRuntime.jsx("path", { + d: "M41.829 29.6724V23.4783C41.829 18.5402 37.82 14.5312 32.8819 14.5312C27.9352 14.5312 23.9348 18.5402 23.9348 23.4783V29.6724H19.1172V50.3195H46.6466V29.6724H41.829ZM33.5701 40.5982V44.1254C33.5701 44.5039 33.2604 44.8136 32.8819 44.8136C32.5034 44.8136 32.1937 44.5039 32.1937 44.1254V40.5982C31.0065 40.2885 30.129 39.2131 30.129 37.9312C30.129 36.4085 31.3592 35.1783 32.8819 35.1783C34.4046 35.1783 35.6348 36.4085 35.6348 37.9312C35.6348 39.2131 34.7573 40.2885 33.5701 40.5982ZM40.4525 29.6724H25.3113V23.4783C25.3113 19.3059 28.7095 15.9077 32.8819 15.9077C37.0543 15.9077 40.4525 19.3059 40.4525 23.4783V29.6724Z", + fill: "#CF3644", + }), + ], + } + ) + ); +}; + var TOTPBlockedScreen = function () { var t = translationContext.useTranslation(); return jsxRuntime.jsx( @@ -105,28 +126,23 @@ var TOTPBlockedScreen = function () { { "data-supertokens": "row noFormRow" }, { children: [ - jsxRuntime.jsx(checkedRoundIcon.CheckedRoundIcon, {}), + jsxRuntime.jsx(BlockedIcon, {}), jsxRuntime.jsx( "div", genericComponentOverrideContext.__assign( { "data-supertokens": "headerTitle" }, - { children: t("PWLESS_CLOSE_TAB_TITLE") } + { children: t("TOTP_BLOCKED_TITLE") } ) ), jsxRuntime.jsx("div", { "data-supertokens": "divider" }), - jsxRuntime.jsxs( + jsxRuntime.jsx( "div", genericComponentOverrideContext.__assign( { "data-supertokens": "headerSubtitle secondaryText" }, - { - children: [ - t("PWLESS_CLOSE_TAB_SUBTITLE_LINE1"), - jsxRuntime.jsx("br", {}), - t("PWLESS_CLOSE_TAB_SUBTITLE_LINE2"), - ], - } + { children: t("TOTP_BLOCKED_SUBTITLE") } ) ), + jsxRuntime.jsx("div", { "data-supertokens": "divider" }), ], } ) @@ -3492,6 +3508,8 @@ var defaultTranslationsTOTP = { TOTP_CODE_INPUT_LABEL: "Please enter TOTP from the app", TOTP_CODE_CONTINUE_BUTTON: "Continue", TOTP_REMOVE_DEVICE_LINK: "Choose another factor", + TOTP_BLOCKED_TITLE: "Account locked", + TOTP_BLOCKED_SUBTITLE: "Account locked due to multiple failed login attempts.", } ), }; @@ -3521,13 +3539,24 @@ var useFeatureReducer = function () { case "createDevice": return genericComponentOverrideContext.__assign( genericComponentOverrideContext.__assign({}, oldState), - { deviceInfo: action.deviceInfo, error: undefined } + { + deviceInfo: action.deviceInfo, + isBlocked: false, + showSecret: false, + nextRetryAt: undefined, + error: undefined, + } ); case "showSecret": return genericComponentOverrideContext.__assign( genericComponentOverrideContext.__assign({}, oldState), { showSecret: true } ); + case "restartFlow": + return genericComponentOverrideContext.__assign( + genericComponentOverrideContext.__assign({}, oldState), + { isBlocked: false, showSecret: false, nextRetryAt: undefined, error: action.error } + ); default: return oldState; } @@ -3631,10 +3660,6 @@ function useOnLoad(recipeImpl, dispatch, userContext) { delete deviceInfo.status; _a.label = 2; case 2: - // if (deviceInfo && !isAllowedToSetup) { - // await recipeImpl.removeDevice({ deviceName: deviceInfo.deviceName, userContext }); - // deviceInfo = undefined; - // } // No need to check if the component is unmounting, since this has no effect then. dispatch({ type: "load", deviceInfo: deviceInfo, error: error }); return [2 /*return*/]; @@ -3647,12 +3672,52 @@ function useOnLoad(recipeImpl, dispatch, userContext) { genericComponentOverrideContext.useOnMountAPICall(fetchMFAInfo, onLoad, handleLoadError); } function useChildProps(recipe, recipeImplementation, state, dispatch, userContext, history) { + var _this = this; return React.useMemo( function () { return { onShowSecretClick: function () { dispatch({ type: "showSecret" }); }, + onBackButtonClicked: function () { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!state.deviceInfo) return [3 /*break*/, 2]; + return [ + 4 /*yield*/, + recipeImplementation.removeDevice({ + deviceName: state.deviceInfo.deviceName, + userContext: userContext, + }), + ]; + case 1: + _a.sent(); + _a.label = 2; + case 2: + // If we don't have history available this would mean we are not using react-router-dom, so we use window's history + if (history === undefined) { + return [ + 2 /*return*/, + windowHandler.WindowHandlerReference.getReferenceOrThrow() + .windowHandler.getWindowUnsafe() + .history.back(), + ]; + } + // If we do have history and goBack function on it this means we are using react-router-dom v5 or lower + if (history.goBack !== undefined) { + return [2 /*return*/, history.goBack()]; + } + // If we reach this code this means we are using react-router-dom v6 + return [2 /*return*/, history(-1)]; + } + }); + }); + }, + onRetryClicked: function () { + dispatch({ type: "restartFlow", error: undefined }); + }, onSuccess: function () { var redirectToPath = genericComponentOverrideContext.getRedirectToPathFromURL(); var redirectInfo = @@ -3783,11 +3848,14 @@ function getModifiedRecipeImplementation(originalImpl, dispatch) { case 1: res = _a.sent(); if (res.status === "LIMIT_REACHED_ERROR") { - dispatch({ type: "setBlocked", error: "ERROR_SIGN_IN_UP_CODE_VERIFY_BLOCKED" }); + dispatch({ + type: "setBlocked", + error: "ERROR_SIGN_IN_UP_CODE_VERIFY_BLOCKED", + nextRetryAt: Date.now() + res.retryAfterMs, + }); } else if (res.status === "INVALID_TOTP_ERROR") { dispatch({ type: "setError", error: "ERROR_SIGN_IN_UP_CODE_VERIFY_INVALID_TOTP" }); } - console.log(res); return [2 /*return*/, res]; } }); @@ -3802,9 +3870,12 @@ function getModifiedRecipeImplementation(originalImpl, dispatch) { return [4 /*yield*/, originalImpl.verifyDevice(input)]; case 1: res = _a.sent(); - console.log(res); if (!(res.status === "LIMIT_REACHED_ERROR")) return [3 /*break*/, 2]; - dispatch({ type: "setBlocked", error: "ERROR_TOTP_MFA_VERIFY_DEVICE_BLOCKED" }); + dispatch({ + type: "setBlocked", + error: "ERROR_TOTP_MFA_VERIFY_DEVICE_BLOCKED", + nextRetryAt: Date.now() + res.retryAfterMs, + }); return [3 /*break*/, 5]; case 2: if (!(res.status === "INVALID_TOTP_ERROR")) return [3 /*break*/, 3]; diff --git a/lib/ts/components/assets/blockedIcon.tsx b/lib/ts/components/assets/blockedIcon.tsx index 4121ce24d..07dfc7912 100644 --- a/lib/ts/components/assets/blockedIcon.tsx +++ b/lib/ts/components/assets/blockedIcon.tsx @@ -1,7 +1,13 @@ export const BlockedIcon = () => ( - - - - + + + + ); diff --git a/lib/ts/recipe/multifactorauth/recipe.tsx b/lib/ts/recipe/multifactorauth/recipe.tsx index 9bf643c76..431819ddb 100644 --- a/lib/ts/recipe/multifactorauth/recipe.tsx +++ b/lib/ts/recipe/multifactorauth/recipe.tsx @@ -24,6 +24,7 @@ import { SessionClaimValidatorStore } from "supertokens-web-js/utils/sessionClai import { SSR_ERROR } from "../../constants"; import SuperTokens from "../../superTokens"; +import { appendQueryParamsToURL, getCurrentNormalisedUrlPath, getRedirectToPathFromURL } from "../../utils"; import RecipeModule from "../recipeModule"; import { DEFAULT_FACTOR_CHOOSER_PATH } from "./constants"; @@ -41,7 +42,6 @@ import type { } from "./types"; import type { NormalisedConfigWithAppInfoAndRecipeID, RecipeInitResult, WebJSRecipeInterface } from "../../types"; import type { NormalisedAppInfo } from "../../types"; -import { appendQueryParamsToURL, getCurrentNormalisedUrlPath, getRedirectToPathFromURL } from "../../utils"; export default class MultiFactorAuth extends RecipeModule< GetRedirectionURLContext, @@ -161,13 +161,13 @@ export default class MultiFactorAuth extends RecipeModule< return this.config.getFactorInfo(this.secondaryFactors); } - async redirectToFactor(factorId: string, redirectBack: boolean = false, history?: any) { + async redirectToFactor(factorId: string, redirectBack = false, history?: any) { let url = await this.getRedirectUrl({ action: "GO_TO_FACTOR", factorId }); if (redirectBack) { - let redirectUrl = getCurrentNormalisedUrlPath().getAsStringDangerous(); + const redirectUrl = getCurrentNormalisedUrlPath().getAsStringDangerous(); url = appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); } else { - let redirectUrl = getRedirectToPathFromURL(); + const redirectUrl = getRedirectToPathFromURL(); if (redirectUrl) { url = appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); } @@ -175,13 +175,13 @@ export default class MultiFactorAuth extends RecipeModule< return SuperTokens.getInstanceOrThrow().redirectToUrl(url, history); } - async redirectToFactorChooser(redirectBack: boolean = false, history?: any) { + async redirectToFactorChooser(redirectBack = false, history?: any) { let url = await this.getRedirectUrl({ action: "FACTOR_CHOOSER" }); if (redirectBack) { - let redirectUrl = getCurrentNormalisedUrlPath().getAsStringDangerous(); + const redirectUrl = getCurrentNormalisedUrlPath().getAsStringDangerous(); url = appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); } else { - let redirectUrl = getRedirectToPathFromURL(); + const redirectUrl = getRedirectToPathFromURL(); if (redirectUrl) { url = appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); } diff --git a/lib/ts/recipe/totp/components/features/mfa/index.tsx b/lib/ts/recipe/totp/components/features/mfa/index.tsx index b9c6a2cdb..edcb9e344 100644 --- a/lib/ts/recipe/totp/components/features/mfa/index.tsx +++ b/lib/ts/recipe/totp/components/features/mfa/index.tsx @@ -18,11 +18,13 @@ import * as React from "react"; import { Fragment } from "react"; import { useMemo } from "react"; +import { WindowHandlerReference } from "supertokens-web-js/utils/windowHandler"; import { ComponentOverrideContext } from "../../../../../components/componentOverride/componentOverrideContext"; import FeatureWrapper from "../../../../../components/featureWrapper"; import { useUserContext } from "../../../../../usercontext"; import { getQueryParams, getRedirectToPathFromURL, useOnMountAPICall } from "../../../../../utils"; +import MultiFactorAuth from "../../../../multifactorauth/recipe"; import SessionRecipe from "../../../../session/recipe"; import MFATOTPThemeWrapper from "../../themes/mfa"; import { defaultTranslationsTOTP } from "../../themes/translations"; @@ -36,9 +38,8 @@ import type { TOTPMFAChildProps, TOTPMFAState, } from "../../../types"; +import type { MFAFactorInfo } from "supertokens-web-js/recipe/multifactorauth/types"; import type { RecipeInterface } from "supertokens-web-js/recipe/totp"; -import MultiFactorAuth from "../../../../multifactorauth/recipe"; -import { MFAFactorInfo } from "supertokens-web-js/recipe/multifactorauth/types"; export const useFeatureReducer = (): [TOTPMFAState, React.Dispatch] => { return React.useReducer( @@ -67,6 +68,9 @@ export const useFeatureReducer = (): [TOTPMFAState, React.Dispatch({ - // userContext, - // }); let deviceInfo: TOTPDeviceInfo | undefined; if (isAllowedToSetup && (doSetup || !isAlreadySetup)) { - // if (!deviceInfo) { const createResp = await recipeImpl.createDevice({ userContext }); if (createResp?.status !== "OK") { throw new Error("TOTP device creation failed with duplicate name; should never happen"); @@ -157,15 +165,7 @@ function useOnLoad(recipeImpl: RecipeInterface, dispatch: React.Dispatch({ - // userContext, - // }); - // } } - // if (deviceInfo && !isAllowedToSetup) { - // await recipeImpl.removeDevice({ deviceName: deviceInfo.deviceName, userContext }); - // deviceInfo = undefined; - // } // No need to check if the component is unmounting, since this has no effect then. dispatch({ type: "load", deviceInfo, error }); @@ -189,6 +189,27 @@ export function useChildProps( onShowSecretClick: () => { dispatch({ type: "showSecret" }); }, + onBackButtonClicked: async () => { + if (state.deviceInfo) { + await recipeImplementation.removeDevice({ + deviceName: state.deviceInfo.deviceName, + userContext, + }); + } + // If we don't have history available this would mean we are not using react-router-dom, so we use window's history + if (history === undefined) { + return WindowHandlerReference.getReferenceOrThrow().windowHandler.getWindowUnsafe().history.back(); + } + // If we do have history and goBack function on it this means we are using react-router-dom v5 or lower + if (history.goBack !== undefined) { + return history.goBack(); + } + // If we reach this code this means we are using react-router-dom v6 + return history(-1); + }, + onRetryClicked() { + dispatch({ type: "restartFlow", error: undefined }); + }, onSuccess: () => { const redirectToPath = getRedirectToPathFromURL(); const redirectInfo = @@ -293,7 +314,11 @@ function getModifiedRecipeImplementation( const res = await originalImpl.verifyCode(input); if (res.status === "LIMIT_REACHED_ERROR") { - dispatch({ type: "setBlocked", error: "ERROR_SIGN_IN_UP_CODE_VERIFY_BLOCKED", nextRetryAt: Date.now() + res.retryAfterMs }); + dispatch({ + type: "setBlocked", + error: "ERROR_SIGN_IN_UP_CODE_VERIFY_BLOCKED", + nextRetryAt: Date.now() + res.retryAfterMs, + }); } else if (res.status === "INVALID_TOTP_ERROR") { dispatch({ type: "setError", error: "ERROR_SIGN_IN_UP_CODE_VERIFY_INVALID_TOTP" }); } @@ -305,7 +330,11 @@ function getModifiedRecipeImplementation( const res = await originalImpl.verifyDevice(input); if (res.status === "LIMIT_REACHED_ERROR") { - dispatch({ type: "setBlocked", error: "ERROR_TOTP_MFA_VERIFY_DEVICE_BLOCKED", nextRetryAt: Date.now() + res.retryAfterMs }); + dispatch({ + type: "setBlocked", + error: "ERROR_TOTP_MFA_VERIFY_DEVICE_BLOCKED", + nextRetryAt: Date.now() + res.retryAfterMs, + }); } else if (res.status === "INVALID_TOTP_ERROR") { dispatch({ type: "setError", error: "ERROR_TOTP_MFA_VERIFY_DEVICE_INVALID_TOTP" }); } else if (res.status === "UNKNOWN_DEVICE_ERROR") { diff --git a/lib/ts/recipe/totp/components/themes/mfa/blockedScreen.tsx b/lib/ts/recipe/totp/components/themes/mfa/blockedScreen.tsx index 4d77f9fe6..7570be1dd 100644 --- a/lib/ts/recipe/totp/components/themes/mfa/blockedScreen.tsx +++ b/lib/ts/recipe/totp/components/themes/mfa/blockedScreen.tsx @@ -16,7 +16,7 @@ import { BlockedIcon } from "../../../../../components/assets/blockedIcon"; import { withOverride } from "../../../../../components/componentOverride/withOverride"; import { useTranslation } from "../../../../../translation/translationContext"; -const TOTPBlockedScreen: React.FC = (props: { onRestartClicked: () => void }) => { +const TOTPBlockedScreen: React.FC = () => { const t = useTranslation(); return ( @@ -25,9 +25,8 @@ const TOTPBlockedScreen: React.FC = (props: { onRestartClicked: () => void }) =>
{t("TOTP_BLOCKED_TITLE")}
-
- {t("TOTP_BLOCKED_SUBTITLE")} -
+
{t("TOTP_BLOCKED_SUBTITLE")}
+
); diff --git a/lib/ts/recipe/totp/components/themes/mfa/totpDeviceInfoSection.tsx b/lib/ts/recipe/totp/components/themes/mfa/totpDeviceInfoSection.tsx index 8b9a16824..8b30bf113 100644 --- a/lib/ts/recipe/totp/components/themes/mfa/totpDeviceInfoSection.tsx +++ b/lib/ts/recipe/totp/components/themes/mfa/totpDeviceInfoSection.tsx @@ -13,12 +13,13 @@ * under the License. */ +import QRCode from "react-qr-code"; + import { withOverride } from "../../../../../components/componentOverride/withOverride"; +import { useTranslation } from "../../../../../translation/translationContext"; import type { TOTPMFACommonProps } from "../../../types"; import type { DeviceInfo } from "supertokens-web-js/recipe/totp"; -import QRCode from "react-qr-code"; -import { useTranslation } from "../../../../../translation/translationContext"; export const DeviceInfoSection = withOverride( "TOTPDeviceInfoSection", diff --git a/lib/ts/recipe/totp/components/themes/translations.ts b/lib/ts/recipe/totp/components/themes/translations.ts index faacc5a7c..8c0a3b14e 100644 --- a/lib/ts/recipe/totp/components/themes/translations.ts +++ b/lib/ts/recipe/totp/components/themes/translations.ts @@ -16,5 +16,7 @@ export const defaultTranslationsTOTP = { TOTP_CODE_INPUT_LABEL: "Please enter TOTP from the app", TOTP_CODE_CONTINUE_BUTTON: "Continue", TOTP_REMOVE_DEVICE_LINK: "Choose another factor", + TOTP_BLOCKED_TITLE: "Account locked", + TOTP_BLOCKED_SUBTITLE: "Account locked due to multiple failed login attempts.", }, }; diff --git a/lib/ts/recipe/totp/recipe.tsx b/lib/ts/recipe/totp/recipe.tsx index c49c45a5c..0c7345719 100644 --- a/lib/ts/recipe/totp/recipe.tsx +++ b/lib/ts/recipe/totp/recipe.tsx @@ -25,6 +25,7 @@ import { OTPIcon } from "../../components/assets/otpIcon"; import { SSR_ERROR } from "../../constants"; import MultiFactorAuth from "../multifactorauth/recipe"; import RecipeModule from "../recipeModule"; +import Session from "../session/recipe"; import { DEFAULT_TOTP_PATH } from "./constants"; import { getFunctionOverrides } from "./functionOverrides"; @@ -39,7 +40,6 @@ import type { } from "./types"; import type { NormalisedConfigWithAppInfoAndRecipeID, RecipeInitResult, WebJSRecipeInterface } from "../../types"; import type { NormalisedAppInfo } from "../../types"; -import Session from "../session/recipe"; export default class TOTP extends RecipeModule< GetRedirectionURLContext, diff --git a/lib/ts/recipe/totp/types.ts b/lib/ts/recipe/totp/types.ts index cf736bf85..3c65e2a78 100644 --- a/lib/ts/recipe/totp/types.ts +++ b/lib/ts/recipe/totp/types.ts @@ -102,17 +102,6 @@ export type TOTPMFAScreenConfig = { loadingScreenStyle: string; // TODO: ?? }; -/** - * When calling getLoginAttemptInfo/setLoginAttemptInfo from web-js we use generics to get - * access to properties in local storage that web-js does not set by default. - * This allows us to strongly type the response while keeping it dynamic. - * - * In the context of auth-react this type indicates all the additional properties we need. - */ -export type AdditionalDeviceInfoProperties = { - redirectToPath?: string; -}; - // Config is what does in the constructor of the recipe. export type UserInput = { totpMFAScreen?: Partial;