diff --git a/lib/build/SuperTokensBranding.js b/lib/build/SuperTokensBranding.js index c6876b7f2..fad0aaeab 100644 --- a/lib/build/SuperTokensBranding.js +++ b/lib/build/SuperTokensBranding.js @@ -46,6 +46,7 @@ var Redirector = function (props) { successRedirectContext: { action: "SUCCESS", isNewRecipeUser: false, + user: undefined, redirectToPath: genericComponentOverrideContext.getRedirectToPathFromURL(), }, }, diff --git a/lib/build/emailpassword-shared6.js b/lib/build/emailpassword-shared6.js index e93940901..002ea6a7d 100644 --- a/lib/build/emailpassword-shared6.js +++ b/lib/build/emailpassword-shared6.js @@ -1163,7 +1163,7 @@ function useChildProps(recipe$1, state, dispatch, history) { ); var userContext = uiEntry.useUserContext(); var onSignInSuccess = React.useCallback( - function () { + function (response) { return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { return genericComponentOverrideContext.__generator(this, function (_a) { return [ @@ -1174,6 +1174,7 @@ function useChildProps(recipe$1, state, dispatch, history) { successRedirectContext: { action: "SUCCESS", isNewRecipeUser: false, + user: response.user, redirectToPath: genericComponentOverrideContext.getRedirectToPathFromURL(), }, }, @@ -1187,7 +1188,7 @@ function useChildProps(recipe$1, state, dispatch, history) { [recipe$1, userContext, history] ); var onSignUpSuccess = React.useCallback( - function () { + function (response) { return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { return genericComponentOverrideContext.__generator(this, function (_a) { return [ @@ -1198,6 +1199,7 @@ function useChildProps(recipe$1, state, dispatch, history) { successRedirectContext: { action: "SUCCESS", isNewRecipeUser: true, + user: response.user, redirectToPath: genericComponentOverrideContext.getRedirectToPathFromURL(), }, }, diff --git a/lib/build/emailpassword-shared7.js b/lib/build/emailpassword-shared7.js index 6415dc169..aa34f8f06 100644 --- a/lib/build/emailpassword-shared7.js +++ b/lib/build/emailpassword-shared7.js @@ -656,6 +656,10 @@ var FormBase = function (props) { }); }); } + if (result.status !== "OK" && result.reason !== undefined) { + props.onError(result.reason); + generalError = result.reason; + } } return [3 /*break*/, 8]; case 6: diff --git a/lib/build/passwordless-shared3.js b/lib/build/passwordless-shared3.js index e2af4b366..4f2d9039b 100644 --- a/lib/build/passwordless-shared3.js +++ b/lib/build/passwordless-shared3.js @@ -360,6 +360,7 @@ var LinkClickedScreen = function (props) { successRedirectContext: { action: "SUCCESS", isNewRecipeUser: response.createdNewRecipeUser, + user: response.user, redirectToPath: loginAttemptInfo === null || loginAttemptInfo === void 0 ? void 0 @@ -4080,6 +4081,7 @@ function useChildProps(recipe$1, dispatch, state, callingConsumeCodeRef, userCon successRedirectContext: { action: "SUCCESS", isNewRecipeUser: result.createdNewRecipeUser, + user: result.user, redirectToPath: genericComponentOverrideContext.getRedirectToPathFromURL(), }, }, diff --git a/lib/build/recipe/authRecipe/types.d.ts b/lib/build/recipe/authRecipe/types.d.ts index 37b707605..2a2185160 100644 --- a/lib/build/recipe/authRecipe/types.d.ts +++ b/lib/build/recipe/authRecipe/types.d.ts @@ -1,3 +1,4 @@ +import type { User } from "supertokens-web-js/types"; import type { Config as RecipeModuleConfig, NormalisedConfig as NormalisedRecipeModuleConfig, @@ -6,11 +7,19 @@ import type { export declare type UserInput = UserInputRecipeModule; export declare type Config = UserInput & RecipeModuleConfig; export declare type NormalisedConfig = NormalisedRecipeModuleConfig; -export declare type GetRedirectionURLContext = { - action: "SUCCESS"; - isNewRecipeUser: boolean; - redirectToPath?: string; -}; +export declare type GetRedirectionURLContext = + | { + action: "SUCCESS"; + isNewRecipeUser: true; + user: User; + redirectToPath?: string; + } + | { + action: "SUCCESS"; + isNewRecipeUser: false; + user?: User; + redirectToPath?: string; + }; export declare type OnHandleEventContext = { action: "SESSION_ALREADY_EXISTS"; }; 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 41858dc47..7dbc1321c 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signInForm.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signInForm.d.ts @@ -10,7 +10,7 @@ export declare const SignInForm: import("react").ComponentType< config: import("../../../types").NormalisedConfig; signUpClicked?: (() => void) | undefined; forgotPasswordClick: () => void; - onSuccess: () => void; + onSuccess: (result: { user: import("supertokens-web-js/types").User }) => void; } & { header?: JSX.Element | undefined; footer?: JSX.Element | undefined; 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 d57524cfe..bbe4a2eb2 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUpForm.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUpForm.d.ts @@ -9,7 +9,7 @@ export declare const SignUpForm: 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; } & { header?: JSX.Element | undefined; footer?: JSX.Element | undefined; diff --git a/lib/build/recipe/emailpassword/index.d.ts b/lib/build/recipe/emailpassword/index.d.ts index ea05618b9..ad2c6e085 100644 --- a/lib/build/recipe/emailpassword/index.d.ts +++ b/lib/build/recipe/emailpassword/index.d.ts @@ -81,6 +81,11 @@ export default class Wrapper { }[]; fetchResponse: Response; } + | { + status: "SIGN_UP_NOT_ALLOWED"; + reason: string; + fetchResponse: Response; + } >; static signIn(input: { formFields: { @@ -107,6 +112,11 @@ export default class Wrapper { status: "WRONG_CREDENTIALS_ERROR"; fetchResponse: Response; } + | { + status: "SIGN_IN_NOT_ALLOWED"; + reason: string; + fetchResponse: Response; + } >; static doesEmailExist(input: { email: string; options?: RecipeFunctionOptions; userContext?: any }): Promise<{ status: "OK"; diff --git a/lib/build/recipe/emailpassword/types.d.ts b/lib/build/recipe/emailpassword/types.d.ts index 95f0c547d..d819f58c8 100644 --- a/lib/build/recipe/emailpassword/types.d.ts +++ b/lib/build/recipe/emailpassword/types.d.ts @@ -122,7 +122,7 @@ export declare type SignInThemeProps = FormThemeBaseProps & { config: NormalisedConfig; signUpClicked?: () => void; forgotPasswordClick: () => void; - onSuccess: () => void; + onSuccess: (result: { user: User }) => void; }; export declare type SignUpThemeProps = FormThemeBaseProps & { recipeImplementation: RecipeInterface; @@ -130,7 +130,7 @@ export declare type SignUpThemeProps = FormThemeBaseProps & { onError: (error: string) => void; config: NormalisedConfig; signInClicked?: () => void; - onSuccess: () => void; + onSuccess: (result: { user: User }) => void; }; export declare type SignInAndUpThemeProps = { signInForm: SignInThemeProps; diff --git a/lib/build/recipe/thirdpartyemailpassword/index.d.ts b/lib/build/recipe/thirdpartyemailpassword/index.d.ts index 4a67c0fb2..f225db25e 100644 --- a/lib/build/recipe/thirdpartyemailpassword/index.d.ts +++ b/lib/build/recipe/thirdpartyemailpassword/index.d.ts @@ -95,6 +95,11 @@ export default class Wrapper { }[]; fetchResponse: Response; } + | { + status: "SIGN_UP_NOT_ALLOWED"; + reason: string; + fetchResponse: Response; + } >; static emailPasswordSignIn(input: { formFields: { @@ -121,6 +126,11 @@ export default class Wrapper { status: "WRONG_CREDENTIALS_ERROR"; fetchResponse: Response; } + | { + status: "SIGN_IN_NOT_ALLOWED"; + reason: string; + fetchResponse: Response; + } >; static doesEmailExist(input: { email: string; options?: RecipeFunctionOptions; userContext?: any }): Promise<{ status: "OK"; diff --git a/lib/build/recipe/thirdpartypasswordless/index.d.ts b/lib/build/recipe/thirdpartypasswordless/index.d.ts index e8205cb5e..db4806266 100644 --- a/lib/build/recipe/thirdpartypasswordless/index.d.ts +++ b/lib/build/recipe/thirdpartypasswordless/index.d.ts @@ -22,7 +22,7 @@ export default class Wrapper { static init( config: UserInput ): import("../../types").RecipeInitResult< - import("../authRecipe/types").GetRedirectionURLContext, + GetRedirectionURLContext, import("./types").PreAndPostAPIHookAction, OnHandleEventContext, import("./types").NormalisedConfig diff --git a/lib/build/session-shared2.js b/lib/build/session-shared2.js index f57635155..7d78ccc90 100644 --- a/lib/build/session-shared2.js +++ b/lib/build/session-shared2.js @@ -296,6 +296,7 @@ var Session = /** @class */ (function (_super) { successRedirectContext: { action: "SUCCESS", isNewRecipeUser: false, + user: undefined, }, }; _a.label = 13; diff --git a/lib/build/thirdparty-shared2.js b/lib/build/thirdparty-shared2.js index 5a4c17983..6ecaf5bc3 100644 --- a/lib/build/thirdparty-shared2.js +++ b/lib/build/thirdparty-shared2.js @@ -542,6 +542,7 @@ var SignInAndUpCallback$1 = function (props) { successRedirectContext: { action: "SUCCESS", isNewRecipeUser: response.createdNewRecipeUser, + user: response.user, redirectToPath: redirectToPath, }, }, diff --git a/lib/ts/recipe/authRecipe/authWidgetWrapper.tsx b/lib/ts/recipe/authRecipe/authWidgetWrapper.tsx index b826210e3..052000fe9 100644 --- a/lib/ts/recipe/authRecipe/authWidgetWrapper.tsx +++ b/lib/ts/recipe/authRecipe/authWidgetWrapper.tsx @@ -78,6 +78,7 @@ const Redirector = = UserInput & RecipeModuleConfig; export type NormalisedConfig = NormalisedRecipeModuleConfig; -export type GetRedirectionURLContext = { - action: "SUCCESS"; - isNewRecipeUser: boolean; - redirectToPath?: string; -}; +export type GetRedirectionURLContext = + | { + action: "SUCCESS"; + isNewRecipeUser: true; + user: User; + redirectToPath?: string; + } + | { + action: "SUCCESS"; + isNewRecipeUser: false; + user?: User; + redirectToPath?: string; + }; export type OnHandleEventContext = { action: "SESSION_ALREADY_EXISTS"; diff --git a/lib/ts/recipe/emailpassword/components/features/signInAndUp/index.tsx b/lib/ts/recipe/emailpassword/components/features/signInAndUp/index.tsx index cd21f2200..7c9aa95fd 100644 --- a/lib/ts/recipe/emailpassword/components/features/signInAndUp/index.tsx +++ b/lib/ts/recipe/emailpassword/components/features/signInAndUp/index.tsx @@ -41,6 +41,7 @@ import type { } from "../../../types"; import type { Dispatch } from "react"; import type { RecipeInterface } from "supertokens-web-js/recipe/emailpassword"; +import { User } from "supertokens-web-js/types"; export const useFeatureReducer = (recipe: Recipe | undefined) => { return React.useReducer( @@ -111,35 +112,43 @@ export function useChildProps( const recipeImplementation = useMemo(() => recipe && getModifiedRecipeImplementation(recipe.webJSRecipe), [recipe]); const userContext = useUserContext(); - const onSignInSuccess = useCallback(async (): Promise => { - return Session.getInstanceOrThrow().validateGlobalClaimsAndHandleSuccessRedirection( - { - rid: recipe!.config.recipeId, - successRedirectContext: { - action: "SUCCESS", - isNewRecipeUser: false, - redirectToPath: getRedirectToPathFromURL(), + const onSignInSuccess = useCallback( + async (response: { user: User }): Promise => { + return Session.getInstanceOrThrow().validateGlobalClaimsAndHandleSuccessRedirection( + { + rid: recipe!.config.recipeId, + successRedirectContext: { + action: "SUCCESS", + isNewRecipeUser: false, + user: response.user, + redirectToPath: getRedirectToPathFromURL(), + }, }, - }, - userContext, - history - ); - }, [recipe, userContext, history]); + userContext, + history + ); + }, + [recipe, userContext, history] + ); - const onSignUpSuccess = useCallback(async (): Promise => { - return Session.getInstanceOrThrow().validateGlobalClaimsAndHandleSuccessRedirection( - { - rid: recipe!.config.recipeId, - successRedirectContext: { - action: "SUCCESS", - isNewRecipeUser: true, - redirectToPath: getRedirectToPathFromURL(), + const onSignUpSuccess = useCallback( + async (response: { user: User }): Promise => { + return Session.getInstanceOrThrow().validateGlobalClaimsAndHandleSuccessRedirection( + { + rid: recipe!.config.recipeId, + successRedirectContext: { + action: "SUCCESS", + isNewRecipeUser: true, + user: response.user, + redirectToPath: getRedirectToPathFromURL(), + }, }, - }, - userContext, - history - ); - }, [recipe, userContext, history]); + userContext, + history + ); + }, + [recipe, userContext, history] + ); return useMemo(() => { if (recipe === undefined || recipeImplementation === undefined) { diff --git a/lib/ts/recipe/emailpassword/components/library/formBase.tsx b/lib/ts/recipe/emailpassword/components/library/formBase.tsx index 1dabbd92a..10fcb1f57 100644 --- a/lib/ts/recipe/emailpassword/components/library/formBase.tsx +++ b/lib/ts/recipe/emailpassword/components/library/formBase.tsx @@ -170,6 +170,10 @@ export const FormBase: React.FC> = (props) => { os.map((fs) => ({ ...fs, error: errorFields.find((ef: any) => ef.id === fs.id)?.error })) ); } + if (result.status !== "OK" && result.reason !== undefined) { + props.onError(result.reason); + generalError = result.reason; + } } } catch (e) { props.onError("SOMETHING_WENT_WRONG_ERROR"); diff --git a/lib/ts/recipe/emailpassword/index.ts b/lib/ts/recipe/emailpassword/index.ts index a926b5c43..6f5423b40 100644 --- a/lib/ts/recipe/emailpassword/index.ts +++ b/lib/ts/recipe/emailpassword/index.ts @@ -115,6 +115,11 @@ export default class Wrapper { }[]; fetchResponse: Response; } + | { + status: "SIGN_UP_NOT_ALLOWED"; + reason: string; + fetchResponse: Response; + } > { return EmailPassword.getInstanceOrThrow().webJSRecipe.signUp({ ...input, @@ -147,6 +152,11 @@ export default class Wrapper { status: "WRONG_CREDENTIALS_ERROR"; fetchResponse: Response; } + | { + status: "SIGN_IN_NOT_ALLOWED"; + reason: string; + fetchResponse: Response; + } > { return EmailPassword.getInstanceOrThrow().webJSRecipe.signIn({ ...input, diff --git a/lib/ts/recipe/emailpassword/types.ts b/lib/ts/recipe/emailpassword/types.ts index 0c834efab..253329077 100644 --- a/lib/ts/recipe/emailpassword/types.ts +++ b/lib/ts/recipe/emailpassword/types.ts @@ -245,7 +245,7 @@ export type SignInThemeProps = FormThemeBaseProps & { config: NormalisedConfig; signUpClicked?: () => void; forgotPasswordClick: () => void; - onSuccess: () => void; + onSuccess: (result: { user: User }) => void; }; export type SignUpThemeProps = FormThemeBaseProps & { @@ -254,7 +254,7 @@ export type SignUpThemeProps = FormThemeBaseProps & { onError: (error: string) => void; config: NormalisedConfig; signInClicked?: () => void; - onSuccess: () => void; + onSuccess: (result: { user: User }) => void; }; export type SignInAndUpThemeProps = { diff --git a/lib/ts/recipe/passwordless/components/features/linkClickedScreen/index.tsx b/lib/ts/recipe/passwordless/components/features/linkClickedScreen/index.tsx index 19122954a..d36c9c598 100644 --- a/lib/ts/recipe/passwordless/components/features/linkClickedScreen/index.tsx +++ b/lib/ts/recipe/passwordless/components/features/linkClickedScreen/index.tsx @@ -110,6 +110,7 @@ const LinkClickedScreen: React.FC = (props) => { successRedirectContext: { action: "SUCCESS", isNewRecipeUser: response.createdNewRecipeUser, + user: response.user, redirectToPath: loginAttemptInfo?.redirectToPath, }, }, diff --git a/lib/ts/recipe/passwordless/components/features/signInAndUp/index.tsx b/lib/ts/recipe/passwordless/components/features/signInAndUp/index.tsx index 6f5572eeb..49eb6f92f 100644 --- a/lib/ts/recipe/passwordless/components/features/signInAndUp/index.tsx +++ b/lib/ts/recipe/passwordless/components/features/signInAndUp/index.tsx @@ -222,6 +222,7 @@ export function useChildProps( successRedirectContext: { action: "SUCCESS", isNewRecipeUser: result.createdNewRecipeUser, + user: result.user, redirectToPath: getRedirectToPathFromURL(), }, }, diff --git a/lib/ts/recipe/session/recipe.tsx b/lib/ts/recipe/session/recipe.tsx index 40a9ec7df..46f1ffb96 100644 --- a/lib/ts/recipe/session/recipe.tsx +++ b/lib/ts/recipe/session/recipe.tsx @@ -170,6 +170,7 @@ export default class Session extends RecipeModule = (props) => { successRedirectContext: { action: "SUCCESS", isNewRecipeUser: response.createdNewRecipeUser, + user: response.user, redirectToPath, }, }, diff --git a/lib/ts/recipe/thirdpartyemailpassword/index.ts b/lib/ts/recipe/thirdpartyemailpassword/index.ts index f53da7500..f352812c9 100644 --- a/lib/ts/recipe/thirdpartyemailpassword/index.ts +++ b/lib/ts/recipe/thirdpartyemailpassword/index.ts @@ -129,6 +129,11 @@ export default class Wrapper { }[]; fetchResponse: Response; } + | { + status: "SIGN_UP_NOT_ALLOWED"; + reason: string; + fetchResponse: Response; + } > { return ThirdPartyEmailPassword.getInstanceOrThrow().webJSRecipe.emailPasswordSignUp({ ...input, @@ -161,6 +166,11 @@ export default class Wrapper { status: "WRONG_CREDENTIALS_ERROR"; fetchResponse: Response; } + | { + status: "SIGN_IN_NOT_ALLOWED"; + reason: string; + fetchResponse: Response; + } > { return ThirdPartyEmailPassword.getInstanceOrThrow().webJSRecipe.emailPasswordSignIn({ ...input, diff --git a/test/end-to-end/accountlinking.test.js b/test/end-to-end/accountlinking.test.js index b846cb5a8..7adf66ebc 100644 --- a/test/end-to-end/accountlinking.test.js +++ b/test/end-to-end/accountlinking.test.js @@ -269,13 +269,13 @@ describe("SuperTokens Account linking", function () { await tryEmailPasswordSignUp(page, email); const successAdornments = await getInputAdornmentsSuccess(page); - assert.strictEqual(successAdornments.length, 3); + assert.strictEqual(successAdornments.length, 4); const errorAdornments = await getInputAdornmentsError(page); - assert.strictEqual(errorAdornments.length, 1); - let fieldErrors = await getFieldErrors(page); + assert.strictEqual(errorAdornments.length, 0); - assert.deepStrictEqual(fieldErrors, ["This email already exists. Please sign in instead."]); + assert.strictEqual(new URL(page.url()).pathname, "/auth/"); + assert.strictEqual(await getGeneralError(page), "Cannot sign up due to security reasons. Please try logging in, use a different login method or contact support. (ERR_CODE_007)"); }); it("should not allow sign in w/ an unverified emailpassword user in case of conflict", async function () { @@ -311,7 +311,7 @@ describe("SuperTokens Account linking", function () { await submitForm(page); assert.strictEqual(new URL(page.url()).pathname, "/auth/"); - assert.strictEqual(await getGeneralError(page), "Incorrect email and password combination"); + assert.strictEqual(await getGeneralError(page), "Cannot sign in due to security reasons. Please try resetting your password, use a different login method or contact support. (ERR_CODE_008)"); }); it("should not allow sign up w/ an unverified thirdparty user in case of conflict", async function () {