diff --git a/lib/build/genericComponentOverrideContext.js b/lib/build/genericComponentOverrideContext.js index 5b9a429eb..9d97a3933 100644 --- a/lib/build/genericComponentOverrideContext.js +++ b/lib/build/genericComponentOverrideContext.js @@ -227,6 +227,54 @@ typeof SuppressedError === "function" return (e.name = "SuppressedError"), (e.error = error), (e.suppressed = suppressed), e; }; +/* 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. + */ +var package_version = "0.35.7"; + +/* 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. + */ +var SUPERTOKENS_DEBUG_NAMESPACE = "com.supertokens.auth-react"; +var __debugLogsEnabled = false; +function enableLogging() { + __debugLogsEnabled = true; +} +function logDebugMessage(message) { + if (__debugLogsEnabled) { + // eslint-disable-next-line no-console + console.log( + "" + .concat(SUPERTOKENS_DEBUG_NAMESPACE, ' {t: "') + .concat(new Date().toISOString(), '", message: "') + .concat(message, '", supertokens-auth-react-ver: "') + .concat(package_version, '"}') + ); + } +} + /* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. * * This software is licensed under the Apache License, Version 2.0 (the @@ -1133,6 +1181,10 @@ var SuperTokens = /** @class */ (function () { ]; case 1: redirectUrl = _a.sent(); + if (redirectUrl === null) { + logDebugMessage("Skipping redirection because the user override returned null"); + return [2 /*return*/]; + } redirectUrl = appendQueryParamsToURL(redirectUrl, queryParams); return [2 /*return*/, this.redirectToUrl(redirectUrl, options.history)]; } @@ -1164,9 +1216,9 @@ var SuperTokens = /** @class */ (function () { translationEventSource: new TranslationController(), userTranslationFunc: translationConfig.translationFunc, }; - var enableDebugLogs = false; - if (config.enableDebugLogs !== undefined) { - enableDebugLogs = config.enableDebugLogs; + var enableDebugLogs = Boolean(config === null || config === void 0 ? void 0 : config.enableDebugLogs); + if (enableDebugLogs) { + enableLogging(); } this.userGetRedirectionURL = config.getRedirectionURL; this.recipeList = config.recipeList.map(function (_a) { @@ -1323,6 +1375,7 @@ exports.getQueryParams = getQueryParams; exports.getRedirectToPathFromURL = getRedirectToPathFromURL; exports.getURLHash = getURLHash; exports.isTest = isTest; +exports.logDebugMessage = logDebugMessage; exports.matchRecipeIdUsingQueryParams = matchRecipeIdUsingQueryParams; exports.mergeObjects = mergeObjects; exports.normaliseRecipeModuleConfig = normaliseRecipeModuleConfig; diff --git a/lib/build/index2.js b/lib/build/index2.js index 2b9fda041..d42ef5214 100644 --- a/lib/build/index2.js +++ b/lib/build/index2.js @@ -1037,6 +1037,15 @@ var SessionAuth = function (_a) { ]; case 1: failureRedirectInfo = _a.sent(); + if (failureRedirectInfo.redirectPath === null) { + setContext(toSetContext); + genericComponentOverrideContext.logDebugMessage( + "Skipping redirection because the user override returned null for validator ".concat( + JSON.stringify(failureRedirectInfo.failedClaim, null, 2) + ) + ); + return [2 /*return*/]; + } if (!(failureRedirectInfo.redirectPath !== undefined)) return [3 /*break*/, 3]; setContext(toSetContext); return [ diff --git a/lib/build/logger.d.ts b/lib/build/logger.d.ts new file mode 100644 index 000000000..a5e767ecb --- /dev/null +++ b/lib/build/logger.d.ts @@ -0,0 +1,2 @@ +export declare function enableLogging(): void; +export declare function logDebugMessage(message: string): void; diff --git a/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/resetPasswordEmail.d.ts b/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/resetPasswordEmail.d.ts index bcc6e2b05..8ff8a9b05 100644 --- a/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/resetPasswordEmail.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/resetPasswordEmail.d.ts @@ -1,7 +1,7 @@ /// export declare const ResetPasswordEmail: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: Omit[]; + formFields: import("../../../types").FormFieldThemeProps[]; error: string | undefined; } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; diff --git a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts index 46dd93ed6..2b40c960b 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts @@ -10,6 +10,6 @@ export declare const SignIn: import("react").ComponentType< config: import("../../../types").NormalisedConfig; signUpClicked?: (() => void) | undefined; forgotPasswordClick: () => void; - onSuccess: () => void; + onSuccess: (result: { user: import("supertokens-web-js/types").User }) => void; } >; diff --git a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signInForm.d.ts b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signInForm.d.ts index f57175ca5..7dbc1321c 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signInForm.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signInForm.d.ts @@ -1,7 +1,7 @@ /// export declare const SignInForm: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: Omit[]; + formFields: import("../../../types").FormFieldThemeProps[]; error: string | undefined; } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; diff --git a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts index 1a0268aa8..380a6e333 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts @@ -9,6 +9,6 @@ export declare const SignUp: import("react").ComponentType< onError: (error: string) => void; config: import("../../../types").NormalisedConfig; signInClicked?: (() => void) | undefined; - onSuccess: () => void; + onSuccess: (result: { user: import("supertokens-web-js/types").User }) => void; } >; diff --git a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUpForm.d.ts b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUpForm.d.ts index 85b7e4c8d..bbe4a2eb2 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUpForm.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUpForm.d.ts @@ -1,14 +1,15 @@ /// export declare const SignUpForm: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { + formFields: import("../../../types").FormFieldThemeProps[]; + error: string | undefined; + } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; clearError: () => void; onError: (error: string) => void; config: import("../../../types").NormalisedConfig; signInClicked?: (() => void) | undefined; onSuccess: (result: { user: import("supertokens-web-js/types").User }) => void; - formFields: import("../../../types").FormFieldThemeProps[]; - error: string | undefined; } & { header?: JSX.Element | undefined; footer?: JSX.Element | undefined; diff --git a/lib/build/recipe/recipeModule/index.d.ts b/lib/build/recipe/recipeModule/index.d.ts index 83002fb43..8f1ce6015 100644 --- a/lib/build/recipe/recipeModule/index.d.ts +++ b/lib/build/recipe/recipeModule/index.d.ts @@ -11,6 +11,6 @@ export default abstract class RecipeModule< history?: any, queryParams?: Record ) => Promise; - getRedirectUrl: (context: GetRedirectionURLContextType) => Promise; + getRedirectUrl: (context: GetRedirectionURLContextType) => Promise; getDefaultRedirectionURL(_: GetRedirectionURLContextType): Promise; } diff --git a/lib/build/recipe/recipeModule/types.d.ts b/lib/build/recipe/recipeModule/types.d.ts index d39ad4720..2aa0c2761 100644 --- a/lib/build/recipe/recipeModule/types.d.ts +++ b/lib/build/recipe/recipeModule/types.d.ts @@ -18,7 +18,7 @@ export declare type RecipePreAPIHookFunction = (context: RecipePreAPIHoo export declare type RecipePostAPIHookFunction = (context: RecipePostAPIHookContext) => Promise; export declare type RecipeOnHandleEventFunction = (context: EventType) => void; export declare type UserInput = { - getRedirectionURL?: (context: GetRedirectionURLContextType) => Promise; + getRedirectionURL?: (context: GetRedirectionURLContextType) => Promise; preAPIHook?: RecipePreAPIHookFunction; postAPIHook?: RecipePostAPIHookFunction; onHandleEvent?: RecipeOnHandleEventFunction; @@ -31,7 +31,7 @@ export declare type Config; export declare type NormalisedConfig = { - getRedirectionURL: (context: GetRedirectionURLContextType) => Promise; + getRedirectionURL: (context: GetRedirectionURLContextType) => Promise; onHandleEvent: RecipeOnHandleEventFunction; useShadowDom: boolean; rootStyle: string; diff --git a/lib/build/recipe/session/utils.d.ts b/lib/build/recipe/session/utils.d.ts index 828d0b578..8b71365ae 100644 --- a/lib/build/recipe/session/utils.d.ts +++ b/lib/build/recipe/session/utils.d.ts @@ -13,6 +13,6 @@ export declare const getFailureRedirectionInfo: ({ | undefined; userContext: any; }) => Promise<{ - redirectPath?: string | undefined; + redirectPath?: string | null | undefined; failedClaim?: ClaimValidationError | undefined; }>; diff --git a/lib/build/recipe/thirdpartyemailpassword/components/themes/translations.d.ts b/lib/build/recipe/thirdpartyemailpassword/components/themes/translations.d.ts index bb256d116..454ff978e 100644 --- a/lib/build/recipe/thirdpartyemailpassword/components/themes/translations.d.ts +++ b/lib/build/recipe/thirdpartyemailpassword/components/themes/translations.d.ts @@ -58,6 +58,9 @@ export declare const defaultTranslationsThirdPartyEmailPassword: { "Password must contain at least one alphabet": undefined; "Password must contain at least one number": undefined; "Email is invalid": undefined; + "Reset password link was not created because of account take over risk. Please contact support. (ERR_CODE_001)": undefined; + "Cannot sign up due to security reasons. Please try logging in, use a different login method or contact support. (ERR_CODE_007)": undefined; + "Cannot sign in due to security reasons. Please try resetting your password, use a different login method or contact support. (ERR_CODE_008)": undefined; EMAIL_VERIFICATION_RESEND_SUCCESS: string; EMAIL_VERIFICATION_SEND_TITLE: string; EMAIL_VERIFICATION_SEND_DESC_START: string; @@ -86,5 +89,8 @@ export declare const defaultTranslationsThirdPartyEmailPassword: { THIRD_PARTY_PROVIDER_DEFAULT_BTN_START: string; THIRD_PARTY_PROVIDER_DEFAULT_BTN_END: string; THIRD_PARTY_ERROR_NO_EMAIL: string; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_004)": undefined; + "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_005)": undefined; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_006)": undefined; }; }; diff --git a/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts b/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts index dbe4e941e..b24b59e60 100644 --- a/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts +++ b/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts @@ -57,6 +57,8 @@ export declare const defaultTranslationsThirdPartyPasswordless: { "Failed to generate a one time code. Please try again": undefined; "Phone number is invalid": undefined; "Email is invalid": undefined; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_002)": undefined; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_003)": undefined; BRANDING_POWERED_BY_START: string; BRANDING_POWERED_BY_END: string; SOMETHING_WENT_WRONG_ERROR: string; @@ -69,5 +71,8 @@ export declare const defaultTranslationsThirdPartyPasswordless: { THIRD_PARTY_PROVIDER_DEFAULT_BTN_START: string; THIRD_PARTY_PROVIDER_DEFAULT_BTN_END: string; THIRD_PARTY_ERROR_NO_EMAIL: string; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_004)": undefined; + "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_005)": undefined; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_006)": undefined; }; }; diff --git a/lib/build/recipeModule-shared.js b/lib/build/recipeModule-shared.js index 5074ca865..24110a4e2 100644 --- a/lib/build/recipeModule-shared.js +++ b/lib/build/recipeModule-shared.js @@ -29,6 +29,14 @@ var RecipeModule = /** @class */ (function (_super) { return [4 /*yield*/, this.getRedirectUrl(context)]; case 1: redirectUrl = _a.sent(); + if (redirectUrl === null) { + genericComponentOverrideContext.logDebugMessage( + "Skipping redirection because the user override returned null for context ".concat( + JSON.stringify(context, null, 2) + ) + ); + return [2 /*return*/]; + } redirectUrl = genericComponentOverrideContext.appendQueryParamsToURL( redirectUrl, queryParams diff --git a/lib/build/session-shared2.js b/lib/build/session-shared2.js index 7d78ccc90..5d424b8a7 100644 --- a/lib/build/session-shared2.js +++ b/lib/build/session-shared2.js @@ -248,6 +248,14 @@ var Session = /** @class */ (function (_super) { ]; case 5: failureRedirectInfo = _a.sent(); + if (failureRedirectInfo.redirectPath === null) { + genericComponentOverrideContext.logDebugMessage( + "Skipping redirection because the user override returned null for validator ".concat( + JSON.stringify(failureRedirectInfo.failedClaim, null, 2) + ) + ); + return [2 /*return*/]; + } // if redirectPath is string that means failed claim had callback that returns path, we redirect there otherwise continue if (failureRedirectInfo.redirectPath !== undefined) { return [ diff --git a/lib/build/superTokens.d.ts b/lib/build/superTokens.d.ts index b823c6099..154622b99 100644 --- a/lib/build/superTokens.d.ts +++ b/lib/build/superTokens.d.ts @@ -25,7 +25,7 @@ export default class SuperTokens { ): RecipeModule; changeLanguage: (lang: string) => Promise; loadTranslation(store: TranslationStore): void; - getRedirectUrl(context: GetRedirectionURLContext): Promise; + getRedirectUrl(context: GetRedirectionURLContext): Promise; redirectToAuth: (options: { show?: "signin" | "signup" | undefined; history?: any; diff --git a/lib/build/types.d.ts b/lib/build/types.d.ts index ad8f5fb88..220004efb 100644 --- a/lib/build/types.d.ts +++ b/lib/build/types.d.ts @@ -13,7 +13,13 @@ export declare type GetRedirectionURLContext = { showSignIn?: boolean; }; export declare type ValidationFailureCallback = - | (({ userContext, reason }: { userContext: any; reason: any }) => Promise | string | undefined) + | (({ + userContext, + reason, + }: { + userContext: any; + reason: any; + }) => Promise | string | undefined | null) | undefined; export declare type SessionClaimValidator = SessionClaimValidatorWebJS & { showAccessDeniedOnFailure?: boolean; @@ -40,7 +46,7 @@ export declare type SuperTokensConfig = { translationFunc?: TranslationFunc; }; enableDebugLogs?: boolean; - getRedirectionURL?: (context: GetRedirectionURLContext) => Promise; + getRedirectionURL?: (context: GetRedirectionURLContext) => Promise; }; export declare type WebJSRecipeInterface = Omit; export declare type CreateRecipeFunction> = ( diff --git a/lib/ts/logger.ts b/lib/ts/logger.ts new file mode 100644 index 000000000..e8d77c5b8 --- /dev/null +++ b/lib/ts/logger.ts @@ -0,0 +1,33 @@ +/* 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. + */ + +import { package_version as version } from "./version"; + +const SUPERTOKENS_DEBUG_NAMESPACE = "com.supertokens.auth-react"; + +let __debugLogsEnabled = false; + +export function enableLogging(): void { + __debugLogsEnabled = true; +} + +export function logDebugMessage(message: string): void { + if (__debugLogsEnabled) { + // eslint-disable-next-line no-console + console.log( + `${SUPERTOKENS_DEBUG_NAMESPACE} {t: "${new Date().toISOString()}", message: "${message}", supertokens-auth-react-ver: "${version}"}` + ); + } +} diff --git a/lib/ts/recipe/recipeModule/index.ts b/lib/ts/recipe/recipeModule/index.ts index 28eb247db..554fa395b 100644 --- a/lib/ts/recipe/recipeModule/index.ts +++ b/lib/ts/recipe/recipeModule/index.ts @@ -13,6 +13,7 @@ * under the License. */ +import { logDebugMessage } from "../../logger"; import SuperTokens from "../../superTokens"; import { appendQueryParamsToURL } from "../../utils"; @@ -32,12 +33,24 @@ export default abstract class RecipeModule< queryParams?: Record ): Promise => { let redirectUrl = await this.getRedirectUrl(context); + + if (redirectUrl === null) { + logDebugMessage( + `Skipping redirection because the user override returned null for context ${JSON.stringify( + context, + null, + 2 + )}` + ); + return; + } + redirectUrl = appendQueryParamsToURL(redirectUrl, queryParams); return SuperTokens.getInstanceOrThrow().redirectToUrl(redirectUrl, history); }; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - getRedirectUrl = async (context: GetRedirectionURLContextType): Promise => { + getRedirectUrl = async (context: GetRedirectionURLContextType): Promise => { // If getRedirectionURL provided by user. const redirectUrl = await this.config.getRedirectionURL(context); if (redirectUrl !== undefined) { diff --git a/lib/ts/recipe/recipeModule/types.ts b/lib/ts/recipe/recipeModule/types.ts index 16e6a91fb..243c07233 100644 --- a/lib/ts/recipe/recipeModule/types.ts +++ b/lib/ts/recipe/recipeModule/types.ts @@ -22,7 +22,7 @@ export type RecipePostAPIHookFunction = (context: RecipePostAPIHookConte export type RecipeOnHandleEventFunction = (context: EventType) => void; export type UserInput = { - getRedirectionURL?: (context: GetRedirectionURLContextType) => Promise; + getRedirectionURL?: (context: GetRedirectionURLContextType) => Promise; preAPIHook?: RecipePreAPIHookFunction; postAPIHook?: RecipePostAPIHookFunction; onHandleEvent?: RecipeOnHandleEventFunction; @@ -37,7 +37,7 @@ export type Config; export type NormalisedConfig = { - getRedirectionURL: (context: GetRedirectionURLContextType) => Promise; + getRedirectionURL: (context: GetRedirectionURLContextType) => Promise; onHandleEvent: RecipeOnHandleEventFunction; useShadowDom: boolean; rootStyle: string; diff --git a/lib/ts/recipe/recipeModule/utils.ts b/lib/ts/recipe/recipeModule/utils.ts index 6abd98a4b..a816eaefb 100644 --- a/lib/ts/recipe/recipeModule/utils.ts +++ b/lib/ts/recipe/recipeModule/utils.ts @@ -12,7 +12,7 @@ export function normaliseRecipeModuleConfig(config?: Config): if (getRedirectionURL === undefined) { // eslint-disable-next-line @typescript-eslint/no-unused-vars - getRedirectionURL = async (_: unknown): Promise => undefined; + getRedirectionURL = async (_: unknown): Promise => undefined; } if (preAPIHook === undefined) { diff --git a/lib/ts/recipe/session/recipe.tsx b/lib/ts/recipe/session/recipe.tsx index 46f1ffb96..35ef2b1ae 100644 --- a/lib/ts/recipe/session/recipe.tsx +++ b/lib/ts/recipe/session/recipe.tsx @@ -19,6 +19,7 @@ import SessionWebJS from "supertokens-web-js/recipe/session"; import WebJSSessionRecipe from "supertokens-web-js/recipe/session"; +import { logDebugMessage } from "../../logger"; import SuperTokens from "../../superTokens"; import { getLocalStorage, isTest, removeFromLocalStorage, setLocalStorage } from "../../utils"; import RecipeModule from "../recipeModule"; @@ -145,6 +146,17 @@ export default class Session extends RecipeModule> = ({ children, overrideGlobalClaimValidators: props.overrideGlobalClaimValidators, userContext, }); + + if (failureRedirectInfo.redirectPath === null) { + setContext(toSetContext); + logDebugMessage( + `Skipping redirection because the user override returned null for validator ${JSON.stringify( + failureRedirectInfo.failedClaim, + null, + 2 + )}` + ); + return; + } + if (failureRedirectInfo.redirectPath !== undefined) { setContext(toSetContext); return await SuperTokens.getInstanceOrThrow().redirectToUrl( diff --git a/lib/ts/recipe/session/utils.ts b/lib/ts/recipe/session/utils.ts index abc350ef9..5a0a2b679 100644 --- a/lib/ts/recipe/session/utils.ts +++ b/lib/ts/recipe/session/utils.ts @@ -57,7 +57,7 @@ export const getFailureRedirectionInfo = async ({ userContext: any ) => SessionClaimValidator[]; userContext: any; -}): Promise<{ redirectPath?: string; failedClaim?: ClaimValidationError }> => { +}): Promise<{ redirectPath?: string | null; failedClaim?: ClaimValidationError }> => { const globalValidators = getGlobalClaimValidators({ overrideGlobalClaimValidators, userContext, diff --git a/lib/ts/superTokens.tsx b/lib/ts/superTokens.tsx index 3449197cc..a0c4f3d45 100644 --- a/lib/ts/superTokens.tsx +++ b/lib/ts/superTokens.tsx @@ -22,6 +22,7 @@ import { PostSuperTokensInitCallbacks } from "supertokens-web-js/utils/postSuper import { WindowHandlerReference } from "supertokens-web-js/utils/windowHandler"; import { SSR_ERROR } from "./constants"; +import { enableLogging, logDebugMessage } from "./logger"; import Multitenancy from "./recipe/multitenancy/recipe"; import { saveCurrentLanguage, TranslationController } from "./translation/translationHelpers"; import { @@ -90,9 +91,10 @@ export default class SuperTokens { userTranslationFunc: translationConfig.translationFunc, }; - let enableDebugLogs = false; - if (config.enableDebugLogs !== undefined) { - enableDebugLogs = config.enableDebugLogs; + const enableDebugLogs = Boolean(config?.enableDebugLogs); + + if (enableDebugLogs) { + enableLogging(); } this.userGetRedirectionURL = config.getRedirectionURL; @@ -165,7 +167,7 @@ export default class SuperTokens { this.languageTranslations.translationEventSource.emit("TranslationLoaded", store); } - async getRedirectUrl(context: GetRedirectionURLContext): Promise { + async getRedirectUrl(context: GetRedirectionURLContext): Promise { if (this.userGetRedirectionURL) { const userRes = await this.userGetRedirectionURL(context); if (userRes !== undefined) { @@ -197,6 +199,11 @@ export default class SuperTokens { action: "TO_AUTH", showSignIn: options.show === "signin", }); + + if (redirectUrl === null) { + logDebugMessage("Skipping redirection because the user override returned null"); + return; + } redirectUrl = appendQueryParamsToURL(redirectUrl, queryParams); return this.redirectToUrl(redirectUrl, options.history); }; diff --git a/lib/ts/types.ts b/lib/ts/types.ts index 0f7771175..1d3419984 100644 --- a/lib/ts/types.ts +++ b/lib/ts/types.ts @@ -30,7 +30,13 @@ export type GetRedirectionURLContext = { }; export type ValidationFailureCallback = - | (({ userContext, reason }: { userContext: any; reason: any }) => Promise | string | undefined) + | (({ + userContext, + reason, + }: { + userContext: any; + reason: any; + }) => Promise | string | undefined | null) | undefined; export type SessionClaimValidator = SessionClaimValidatorWebJS & { @@ -98,7 +104,7 @@ export type SuperTokensConfig = { translationFunc?: TranslationFunc; }; enableDebugLogs?: boolean; - getRedirectionURL?: (context: GetRedirectionURLContext) => Promise; + getRedirectionURL?: (context: GetRedirectionURLContext) => Promise; }; export type WebJSRecipeInterface = Omit; diff --git a/test/unit/recipe/emailpassword/emailPassword.test.tsx b/test/unit/recipe/emailpassword/emailPassword.test.tsx index 7718ea095..3382b7c7e 100644 --- a/test/unit/recipe/emailpassword/emailPassword.test.tsx +++ b/test/unit/recipe/emailpassword/emailPassword.test.tsx @@ -626,4 +626,18 @@ describe("EmailPassword", function () { } } }); + + it("Initializing EmailPassword and return null from getRedirectionURL", async function () { + EmailPassword.init({ + async getRedirectionURL() { + return null; + }, + }).authReact(SuperTokens.getInstanceOrThrow().appInfo, false); + + assert( + (await EmailPassword.getInstanceOrThrow().getRedirectUrl({ action: "SUCCESS", isNewRecipeUser: false })) === + null + ); + assert((await EmailPassword.getInstanceOrThrow().getRedirectUrl({ action: "RESET_PASSWORD" })) === null); + }); }); diff --git a/test/with-typescript/src/App.tsx b/test/with-typescript/src/App.tsx index 25ee1151f..9931adcc5 100644 --- a/test/with-typescript/src/App.tsx +++ b/test/with-typescript/src/App.tsx @@ -1460,3 +1460,44 @@ Multitenancy.init({ }), }, }); + +// Testing that 'null' is allowed to be returned from getRedirectionURL +SuperTokens.init({ + appInfo: { + appName: "", + apiDomain: "", + websiteDomain: "", + }, + + async getRedirectionURL(context) { + return null; + }, + recipeList: [ + EmailPassword.init({ + async getRedirectionURL(context) { + return null; + }, + }), + ThirdParty.init({ + async getRedirectionURL(context) { + return null; + }, + }), + ThirdPartyEmailPassword.init({ + async getRedirectionURL(context) { + return null; + }, + }), + EmailVerification.init({ + async getRedirectionURL(context) { + return null; + }, + }), + Passwordless.init({ + contactMethod: "EMAIL", + async getRedirectionURL(context) { + return null; + }, + }), + ], +});