From ba3af8bc49f44dd9d8183c90737da7619039260f Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Thu, 21 Nov 2024 11:25:52 -0500 Subject: [PATCH 1/6] add isCombinedFlow method --- packages/clerk-js/src/core/clerk.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 8e862b9309..1454fa9a53 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -351,6 +351,10 @@ export class Clerk implements ClerkInterface { } }; + public isCombinedFlow(): boolean { + return this.#options.signInUrl === this.#options.signUpUrl; + } + public signOut: SignOut = async (callbackOrOptions?: SignOutCallback | SignOutOptions, options?: SignOutOptions) => { if (!this.client || this.client.sessions.length === 0) { return; From f0a5237f947ad6105f26faf4eb56c2f3d808c91e Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Thu, 21 Nov 2024 11:31:47 -0500 Subject: [PATCH 2/6] build signup url --- packages/clerk-js/src/core/clerk.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 1454fa9a53..459462447d 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1971,6 +1971,11 @@ export class Clerk implements ClerkInterface { if (!key || !this.loaded || !this.environment || !this.environment.displayConfig) { return ''; } + + if (this.isCombinedFlow() && key === 'signUpUrl') { + return this.buildUrlWithAuth(`${this.#options.signInUrl}/create}`); + } + const signInOrUpUrl = this.#options[key] || this.environment.displayConfig[key]; const redirectUrls = new RedirectUrls(this.#options, options).toSearchParams(); const initValues = new URLSearchParams(_initValues || {}); From 368eba5f371ddec8707031ad1960d436ca58bcb3 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Thu, 21 Nov 2024 11:49:20 -0500 Subject: [PATCH 3/6] WIP --- packages/clerk-js/src/core/clerk.ts | 14 +++++++++----- .../src/ui/components/SignIn/SignInStart.tsx | 3 ++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 459462447d..08ba5a3be3 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1972,14 +1972,18 @@ export class Clerk implements ClerkInterface { return ''; } - if (this.isCombinedFlow() && key === 'signUpUrl') { - return this.buildUrlWithAuth(`${this.#options.signInUrl}/create}`); - } - const signInOrUpUrl = this.#options[key] || this.environment.displayConfig[key]; + const combinedFlowSignUpUrl = `${this.#options.signInUrl}#/create`; + const baseUrl = this.isCombinedFlow() && key === 'signUpUrl' ? combinedFlowSignUpUrl : signInOrUpUrl; const redirectUrls = new RedirectUrls(this.#options, options).toSearchParams(); const initValues = new URLSearchParams(_initValues || {}); - const url = buildURL({ base: signInOrUpUrl, hashSearchParams: [initValues, redirectUrls] }, { stringify: true }); + const url = buildURL( + { + base: baseUrl, + hashSearchParams: [initValues, redirectUrls], + }, + { stringify: true }, + ); return this.buildUrlWithAuth(url); }; diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx index b5f5bade1c..ed0531e4c5 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx @@ -353,7 +353,8 @@ export function _SignInStart(): JSX.Element { if (isCombinedFlow) { const attribute = getSignUpAttributeFromIdentifier(identifierField); clerk.client.signUp[attribute] = identifierField.value; - return navigate('create'); + const createUrl = clerk.buildSignUpUrl(); + return navigate(createUrl); } handleError(e, [identifierField, instantPasswordField], card.setError); } From 9e6c2b6c7f62faf68c6a2d17bf157ee4c65725e2 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Thu, 21 Nov 2024 18:02:57 -0500 Subject: [PATCH 4/6] WIP --- packages/clerk-js/src/core/clerk.ts | 7 +++--- packages/clerk-js/src/core/constants.ts | 1 + .../src/ui/components/SignIn/SignInStart.tsx | 24 ++++++++++++------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 08ba5a3be3..a7bac6c10b 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -351,7 +351,7 @@ export class Clerk implements ClerkInterface { } }; - public isCombinedFlow(): boolean { + #isCombinedFlow(): boolean { return this.#options.signInUrl === this.#options.signUpUrl; } @@ -1973,13 +1973,12 @@ export class Clerk implements ClerkInterface { } const signInOrUpUrl = this.#options[key] || this.environment.displayConfig[key]; - const combinedFlowSignUpUrl = `${this.#options.signInUrl}#/create`; - const baseUrl = this.isCombinedFlow() && key === 'signUpUrl' ? combinedFlowSignUpUrl : signInOrUpUrl; const redirectUrls = new RedirectUrls(this.#options, options).toSearchParams(); const initValues = new URLSearchParams(_initValues || {}); const url = buildURL( { - base: baseUrl, + base: signInOrUpUrl, + hashPath: this.#isCombinedFlow() && key === 'signUpUrl' ? '/create' : '', hashSearchParams: [initValues, redirectUrls], }, { stringify: true }, diff --git a/packages/clerk-js/src/core/constants.ts b/packages/clerk-js/src/core/constants.ts index ae34145cb6..488568c4e6 100644 --- a/packages/clerk-js/src/core/constants.ts +++ b/packages/clerk-js/src/core/constants.ts @@ -32,6 +32,7 @@ export const ERROR_CODES = { ENTERPRISE_SSO_EMAIL_ADDRESS_DOMAIN_MISMATCH: 'enterprise_sso_email_address_domain_mismatch', ENTERPRISE_SSO_HOSTED_DOMAIN_MISMATCH: 'enterprise_sso_hosted_domain_mismatch', SAML_EMAIL_ADDRESS_DOMAIN_MISMATCH: 'saml_email_address_domain_mismatch', + INVITATION_ACCOUNT_NOT_EXISTS: 'invitation_account_not_exists', } as const; export const SIGN_IN_INITIAL_VALUE_KEYS = ['email_address', 'phone_number', 'username']; diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx index ed0531e4c5..6872151be5 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx @@ -327,18 +327,24 @@ export function _SignInStart(): JSX.Element { (e: ClerkAPIError) => e.code === ERROR_CODES.INVALID_STRATEGY_FOR_USER || e.code === ERROR_CODES.FORM_PASSWORD_INCORRECT, ); + const alreadySignedInError: ClerkAPIError = e.errors.find( (e: ClerkAPIError) => e.code === 'identifier_already_signed_in', ); + const accountDoesNotExistError: ClerkAPIError = e.errors.find( + (e: ClerkAPIError) => + e.code === ERROR_CODES.INVITATION_ACCOUNT_NOT_EXISTS || e.code === ERROR_CODES.FORM_IDENTIFIER_NOT_FOUND, + ); if (instantPasswordError) { await signInWithFields(identifierField); } else if (alreadySignedInError) { const sid = alreadySignedInError.meta!.sessionId!; await clerk.setActive({ session: sid, redirectUrl: afterSignInUrl }); - } else { - if (isCombinedFlow && userSettings.signUp.mode === SIGN_UP_MODES.WAITLIST) { - const attribute = getSignUpAttributeFromIdentifier(identifierField); + } else if (isCombinedFlow && accountDoesNotExistError) { + const attribute = getSignUpAttributeFromIdentifier(identifierField); + + if (userSettings.signUp.mode === SIGN_UP_MODES.WAITLIST) { const waitlistUrl = clerk.buildWaitlistUrl( attribute === 'emailAddress' ? { @@ -350,12 +356,14 @@ export function _SignInStart(): JSX.Element { ); return navigate(waitlistUrl); } - if (isCombinedFlow) { - const attribute = getSignUpAttributeFromIdentifier(identifierField); - clerk.client.signUp[attribute] = identifierField.value; - const createUrl = clerk.buildSignUpUrl(); - return navigate(createUrl); + + clerk.client.signUp[attribute] = identifierField.value; + const paramsToForward = new URLSearchParams(); + if (organizationTicket) { + paramsToForward.set('__clerk_ticket', organizationTicket); } + return navigate(`create?${paramsToForward.toString()}`); + } else { handleError(e, [identifierField, instantPasswordField], card.setError); } }; From bbbed16547f1d103bca59911bafad29403e3f9a6 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 22 Nov 2024 11:22:26 -0500 Subject: [PATCH 5/6] move to clerk provider --- packages/clerk-js/src/core/clerk.ts | 2 +- packages/clerk-js/src/ui/components/SignIn/SignIn.tsx | 5 +++-- packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx | 7 ++++--- packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx | 7 ++++--- packages/types/src/clerk.ts | 3 ++- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index a7bac6c10b..8d5f4694a7 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -352,7 +352,7 @@ export class Clerk implements ClerkInterface { }; #isCombinedFlow(): boolean { - return this.#options.signInUrl === this.#options.signUpUrl; + return this.#options.experimental?.combinedFlow && this.#options.signInUrl === this.#options.signUpUrl; } public signOut: SignOut = async (callbackOrOptions?: SignOutCallback | SignOutOptions, options?: SignOutOptions) => { diff --git a/packages/clerk-js/src/ui/components/SignIn/SignIn.tsx b/packages/clerk-js/src/ui/components/SignIn/SignIn.tsx index 4a4bfc99f6..968ab8526e 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignIn.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignIn.tsx @@ -6,6 +6,7 @@ import { SignInEmailLinkFlowComplete, SignUpEmailLinkFlowComplete } from '../../ import { SignInContext, SignUpContext, + useOptions, useSignInContext, useSignUpContext, withCoreSessionSwitchGuard, @@ -36,6 +37,7 @@ function RedirectToSignIn() { function SignInRoutes(): JSX.Element { const signInContext = useSignInContext(); const signUpContext = useSignUpContext(); + const options = useOptions(); return ( @@ -74,7 +76,7 @@ function SignInRoutes(): JSX.Element { redirectUrl='../factor-two' /> - {signInContext.__experimental?.combinedFlow && ( + {options.experimental?.combinedFlow && ( diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx index 6872151be5..1f46914cec 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx @@ -9,7 +9,7 @@ import { getClerkQueryParam, removeClerkQueryParam } from '../../../utils'; import type { SignInStartIdentifier } from '../../common'; import { getIdentifierControlDisplayValues, groupIdentifiers, withRedirectToAfterSignIn } from '../../common'; import { buildSSOCallbackURL } from '../../common/redirects'; -import { useCoreSignIn, useEnvironment, useSignInContext } from '../../contexts'; +import { useCoreSignIn, useEnvironment, useOptions, useSignInContext } from '../../contexts'; import { Col, descriptors, Flow, localizationKeys } from '../../customizables'; import { Card, @@ -65,9 +65,10 @@ export function _SignInStart(): JSX.Element { const { displayConfig, userSettings } = useEnvironment(); const signIn = useCoreSignIn(); const { navigate } = useRouter(); + const options = useOptions(); const ctx = useSignInContext(); - const { afterSignInUrl, signInUrl, signUpUrl, waitlistUrl, __experimental } = ctx; - const isCombinedFlow = (__experimental?.combinedFlow && signInUrl === signUpUrl) || false; + const { afterSignInUrl, signInUrl, signUpUrl, waitlistUrl } = ctx; + const isCombinedFlow = (options?.experimental?.combinedFlow && signInUrl === signUpUrl) || false; const supportEmail = useSupportEmail(); const identifierAttributes = useMemo( () => groupIdentifiers(userSettings.enabledFirstFactorIdentifiers), diff --git a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx index 49bd7fab0f..d5758243c9 100644 --- a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { ERROR_CODES, SIGN_UP_MODES } from '../../../core/constants'; import { getClerkQueryParam, removeClerkQueryParam } from '../../../utils/getClerkQueryParam'; import { buildSSOCallbackURL, withRedirectToAfterSignUp } from '../../common'; -import { useCoreSignUp, useEnvironment, useSignUpContext } from '../../contexts'; +import { useCoreSignUp, useEnvironment, useOptions, useSignUpContext } from '../../contexts'; import { descriptors, Flex, Flow, localizationKeys, useAppearance, useLocalizations } from '../../customizables'; import { Card, @@ -39,8 +39,9 @@ function _SignUpStart(): JSX.Element { const { attributes } = userSettings; const { setActive } = useClerk(); const ctx = useSignUpContext(); - const { afterSignUpUrl, signInUrl, signUpUrl, unsafeMetadata, __experimental } = ctx; - const isCombinedFlow = (__experimental?.combinedFlow && signInUrl === signUpUrl) || false; + const options = useOptions(); + const { afterSignUpUrl, signInUrl, signUpUrl, unsafeMetadata } = ctx; + const isCombinedFlow = (options.experimental?.combinedFlow && signInUrl === signUpUrl) || false; const [activeCommIdentifierType, setActiveCommIdentifierType] = React.useState( getInitialActiveIdentifier(attributes, userSettings.signUp.progressive), ); diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index 3ee85edcc8..56d03a4be5 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -741,6 +741,7 @@ export type ClerkOptions = ClerkOptionsNavigation & { persistClient: boolean; rethrowOfflineNetworkErrors: boolean; + combinedFlow: boolean; }, Record >; @@ -894,7 +895,7 @@ export type SignInProps = RoutingOptions & { /** * Enable experimental flags to gain access to new features. These flags are not guaranteed to be stable and may change drastically in between patch or minor versions. */ - __experimental?: Record & { newComponents?: boolean; combinedFlow?: boolean; signUpProps?: SignUpProps }; + __experimental?: Record & { newComponents?: boolean; signUpProps?: SignUpProps }; /** * Full URL or path to for the waitlist process. * Used to fill the "Join waitlist" link in the SignUp component. From dd0840039958c4adb8bd30311f8debcd279d98b5 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Fri, 22 Nov 2024 15:23:52 -0500 Subject: [PATCH 6/6] fix modal usage --- packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx index 1f46914cec..90026ed5e9 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx @@ -67,8 +67,8 @@ export function _SignInStart(): JSX.Element { const { navigate } = useRouter(); const options = useOptions(); const ctx = useSignInContext(); - const { afterSignInUrl, signInUrl, signUpUrl, waitlistUrl } = ctx; - const isCombinedFlow = (options?.experimental?.combinedFlow && signInUrl === signUpUrl) || false; + const { afterSignInUrl, signUpUrl, waitlistUrl } = ctx; + const isCombinedFlow = (options?.experimental?.combinedFlow && options.signInUrl === options.signUpUrl) || false; const supportEmail = useSupportEmail(); const identifierAttributes = useMemo( () => groupIdentifiers(userSettings.enabledFirstFactorIdentifiers),