diff --git a/.changeset/factor-two-redirect-signed-in-forward.md b/.changeset/factor-two-redirect-signed-in-forward.md new file mode 100644 index 00000000000..cde1a41ef36 --- /dev/null +++ b/.changeset/factor-two-redirect-signed-in-forward.md @@ -0,0 +1,5 @@ +--- +'@clerk/ui': patch +--- + +Redirect signed-in users forward to afterSignInUrl when landing on factor-two without a pending 2FA session, instead of redirecting back to sign-in start diff --git a/.changeset/fix-factor-two-infinite-spinner.md b/.changeset/fix-factor-two-infinite-spinner.md new file mode 100644 index 00000000000..a55b2122ffd --- /dev/null +++ b/.changeset/fix-factor-two-infinite-spinner.md @@ -0,0 +1,5 @@ +--- +'@clerk/ui': patch +--- + +Fix infinite loading spinner when navigating to factor-two sign-in route without an active 2FA session diff --git a/commitlint.config.ts b/commitlint.config.ts index 5448a7e00d0..58cd2776348 100644 --- a/commitlint.config.ts +++ b/commitlint.config.ts @@ -31,7 +31,7 @@ const Configuration = { 'body-max-line-length': [1, 'always', '150'], 'scope-empty': [2, 'never'], 'scope-enum': [2, 'always', [...getPackageNames(), 'repo', 'release', 'e2e', '*', 'ci']], - 'subject-case': [2, 'always', ['camel-case', 'lower-case', 'sentence-case']], + 'subject-case': [1, 'always', ['camel-case', 'lower-case', 'sentence-case']], }, }; diff --git a/packages/ui/src/components/SignIn/SignInFactorTwo.tsx b/packages/ui/src/components/SignIn/SignInFactorTwo.tsx index caa771ed0e3..90bfddd6df7 100644 --- a/packages/ui/src/components/SignIn/SignInFactorTwo.tsx +++ b/packages/ui/src/components/SignIn/SignInFactorTwo.tsx @@ -1,8 +1,12 @@ +import { useClerk } from '@clerk/shared/react'; +import React from 'react'; + import { withCardStateProvider } from '@/ui/elements/contexts'; import { LoadingCard } from '@/ui/elements/LoadingCard'; import { withRedirectToAfterSignIn, withRedirectToSignInTask } from '../../common'; -import { useCoreSignIn } from '../../contexts'; +import { useCoreSignIn, useSignInContext } from '../../contexts'; +import { useRouter } from '../../router'; import { SignInFactorTwoAlternativeMethods } from './SignInFactorTwoAlternativeMethods'; import { SignInFactorTwoBackupCodeCard } from './SignInFactorTwoBackupCodeCard'; import { SignInFactorTwoEmailCodeCard } from './SignInFactorTwoEmailCodeCard'; @@ -12,7 +16,10 @@ import { SignInFactorTwoTOTPCard } from './SignInFactorTwoTOTPCard'; import { useSecondFactorSelection } from './useSecondFactorSelection'; function SignInFactorTwoInternal(): JSX.Element { + const clerk = useClerk(); const signIn = useCoreSignIn(); + const router = useRouter(); + const { afterSignInUrl } = useSignInContext(); const { currentFactor, factorAlreadyPrepared, @@ -22,6 +29,26 @@ function SignInFactorTwoInternal(): JSX.Element { toggleAllStrategies, } = useSecondFactorSelection(signIn.supportedSecondFactors); + React.useEffect(() => { + if (clerk.__internal_setActiveInProgress) { + return; + } + + // If the sign-in doesn't need second factor verification, redirect away. + // Don't redirect for 'complete' status - setActive will handle navigation. + if (signIn.status === null || signIn.status === 'needs_identifier' || signIn.status === 'needs_first_factor') { + // If the user is already signed in (e.g. multi-session app, page reload after + // successful verification), redirect forward to afterSignInUrl instead of + // back to sign-in start. + if (clerk.isSignedIn) { + void router.navigate(afterSignInUrl); + } else { + void router.navigate('../'); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps -- Only run on mount and when setActiveInProgress changes + }, [clerk.__internal_setActiveInProgress]); + if (!currentFactor) { return ; }