From bca151df3c84bc37e99b28199bd3959f7ff73f15 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Mon, 26 May 2025 15:54:41 -0300 Subject: [PATCH 1/4] Pass redirect URL to SSO flow --- .../clerk-js/src/ui/components/SignUp/SignUpStart.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx index 9cfb5c598f4..9b4e5080886 100644 --- a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx @@ -119,6 +119,7 @@ function SignUpStartInternal(): JSX.Element { const hasEmail = !!formState.emailAddress.value; const isProgressiveSignUp = userSettings.signUp.progressive; const isLegalConsentEnabled = userSettings.signUp.legal_consent_enabled; + const oidcPrompt = ctx.oidcPrompt; const fields = determineActiveFields({ attributes, @@ -145,8 +146,13 @@ function SignUpStartInternal(): JSX.Element { setMissingRequirementsWithTicket(true); } + const redirectUrl = ctx.ssoCallbackUrl; + const redirectUrlComplete = ctx.afterSignUpUrl || '/'; + return completeSignUpFlow({ signUp, + redirectUrl, + redirectUrlComplete, verifyEmailPath: 'verify-email-address', verifyPhonePath: 'verify-phone-number', handleComplete: () => { @@ -155,6 +161,7 @@ function SignUpStartInternal(): JSX.Element { return setActive({ session: signUp.createdSessionId, redirectUrl: afterSignUpUrl }); }, navigate, + oidcPrompt, }); }) .catch(err => { @@ -302,7 +309,7 @@ function SignUpStartInternal(): JSX.Element { navigate, redirectUrl, redirectUrlComplete, - oidcPrompt: ctx.oidcPrompt, + oidcPrompt, }), ) .catch(err => handleError(err, fieldsToSubmit, card.setError)) From 7dbe3a8914310c04eabe9922e4dc33bf187ab242 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Mon, 26 May 2025 15:55:30 -0300 Subject: [PATCH 2/4] Initiate enterprise SSO on sign in with ticket --- packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx index 2fe2dd235f9..57e7f32df4f 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx @@ -222,6 +222,10 @@ function SignInStartInternal(): JSX.Element { .then(res => { switch (res.status) { case 'needs_first_factor': + if (res.supportedFirstFactors?.every(ff => ff.strategy === 'enterprise_sso')) { + return authenticateWithEnterpriseSSO(); + } + return navigate('factor-one'); case 'needs_second_factor': return navigate('factor-two'); From dfd911b7832d289b0c90d0d895dde81ca4b6c6ff Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 3 Jun 2025 18:58:14 -0300 Subject: [PATCH 3/4] Do not set card as IDLE when redirectiong to provider --- .../src/ui/components/SignIn/SignInStart.tsx | 15 +++++++++++++-- .../src/ui/components/SignUp/SignUpStart.tsx | 3 +++ 2 files changed, 16 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 57e7f32df4f..d25203c7629 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx @@ -222,7 +222,7 @@ function SignInStartInternal(): JSX.Element { .then(res => { switch (res.status) { case 'needs_first_factor': - if (res.supportedFirstFactors?.every(ff => ff.strategy === 'enterprise_sso')) { + if (hasOnlyEnterpriseSSOFirstFactors(res)) { return authenticateWithEnterpriseSSO(); } @@ -245,6 +245,9 @@ function SignInStartInternal(): JSX.Element { return attemptToRecoverFromSignInError(err); }) .finally(() => { + const isRedirectingToSSOProvider = hasOnlyEnterpriseSSOFirstFactors(signIn); + if (isRedirectingToSSOProvider) return; + status.setIdle(); card.setIdle(); }); @@ -368,7 +371,7 @@ function SignInStartInternal(): JSX.Element { } break; case 'needs_first_factor': - if (res.supportedFirstFactors?.every(ff => ff.strategy === 'enterprise_sso')) { + if (hasOnlyEnterpriseSSOFirstFactors(res)) { await authenticateWithEnterpriseSSO(); break; } @@ -608,6 +611,14 @@ function SignInStartInternal(): JSX.Element { ); } +const hasOnlyEnterpriseSSOFirstFactors = (signIn: SignInResource): boolean => { + if (!signIn?.supportedFirstFactors?.length) { + return false; + } + + return signIn.supportedFirstFactors.every(ff => ff.strategy === 'saml' || ff.strategy === 'enterprise_sso'); +}; + const InstantPasswordRow = ({ field }: { field?: FormControlState<'password'> }) => { const [autofilled, setAutofilled] = useState(false); const ref = useRef(null); diff --git a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx index 9b4e5080886..694a84e8bbe 100644 --- a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx @@ -170,6 +170,9 @@ function SignUpStartInternal(): JSX.Element { handleError(err, [], card.setError); }) .finally(() => { + const isRedirectingToSSOProvider = signUp.missingFields.some(mf => mf === 'saml' || mf === 'enterprise_sso'); + if (isRedirectingToSSOProvider) return; + status.setIdle(); card.setIdle(); }); From ef1ac0cc14d9ab429ae239d2a39c8f4b02fb62fb Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 3 Jun 2025 18:59:34 -0300 Subject: [PATCH 4/4] Add changeset --- .changeset/nasty-items-jog.md | 5 +++++ packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx | 7 +++++-- packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx | 3 +++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 .changeset/nasty-items-jog.md diff --git a/.changeset/nasty-items-jog.md b/.changeset/nasty-items-jog.md new file mode 100644 index 00000000000..910a6a72fc8 --- /dev/null +++ b/.changeset/nasty-items-jog.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Initiate enterprise SSO from ticket flows, such as organization invitations. diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx index d25203c7629..7a69de6312d 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx @@ -245,6 +245,9 @@ function SignInStartInternal(): JSX.Element { return attemptToRecoverFromSignInError(err); }) .finally(() => { + // Keep the card in loading state during SSO redirect to prevent UI flicker + // This is necessary because there's a brief delay between initiating the SSO flow + // and the actual redirect to the external Identity Provider const isRedirectingToSSOProvider = hasOnlyEnterpriseSSOFirstFactors(signIn); if (isRedirectingToSSOProvider) return; @@ -612,11 +615,11 @@ function SignInStartInternal(): JSX.Element { } const hasOnlyEnterpriseSSOFirstFactors = (signIn: SignInResource): boolean => { - if (!signIn?.supportedFirstFactors?.length) { + if (!signIn.supportedFirstFactors?.length) { return false; } - return signIn.supportedFirstFactors.every(ff => ff.strategy === 'saml' || ff.strategy === 'enterprise_sso'); + return signIn.supportedFirstFactors.every(ff => ff.strategy === 'enterprise_sso'); }; const InstantPasswordRow = ({ field }: { field?: FormControlState<'password'> }) => { diff --git a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx index 694a84e8bbe..6ad232059b9 100644 --- a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx @@ -170,6 +170,9 @@ function SignUpStartInternal(): JSX.Element { handleError(err, [], card.setError); }) .finally(() => { + // Keep the card in loading state during SSO redirect to prevent UI flicker + // This is necessary because there's a brief delay between initiating the SSO flow + // and the actual redirect to the external Identity Provider const isRedirectingToSSOProvider = signUp.missingFields.some(mf => mf === 'saml' || mf === 'enterprise_sso'); if (isRedirectingToSSOProvider) return;