diff --git a/.changeset/tender-icons-bake.md b/.changeset/tender-icons-bake.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/tender-icons-bake.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index ed0f6e621d4..ccdea2bbdb7 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -53,6 +53,7 @@ import type { } from '@clerk/types'; import type { MountComponentRenderer } from '../ui/Components'; +import { handleMissingRequirements } from '../ui/components/SignUp/util'; import { appendAsQueryParams, buildURL, @@ -834,6 +835,8 @@ export default class Clerk implements ClerkInterface { const { externalAccount } = signUp.verifications; const su = { status: signUp.status, + missingFields: signUp.missingFields, + unverifiedFields: signUp.unverifiedFields, externalAccountStatus: externalAccount.status, externalAccountErrorCode: externalAccount.error?.code, externalAccountSessionId: externalAccount.error?.meta?.sessionId, @@ -873,6 +876,22 @@ export default class Clerk implements ClerkInterface { buildURL({ base: displayConfig.signUpUrl, hashPath: '/continue' }, { stringify: true }), ); + const navigateToNextStepSignUp = () => { + if (su.unverifiedFields.length > 0 && su.missingFields.length === 0) { + return handleMissingRequirements({ + signUp, + verifyEmailPath: + params.verifyEmailAddressUrl || + buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-email-address' }, { stringify: true }), + verifyPhonePath: + params.verifyPhoneNumberUrl || + buildURL({ base: displayConfig.signUpUrl, hashPath: '/verify-phone-number' }, { stringify: true }), + navigate, + }); + } + return navigateToContinueSignUp(); + }; + const userExistsButNeedsToSignIn = su.externalAccountStatus === 'transferable' && su.externalAccountErrorCode === 'external_account_exists'; @@ -902,7 +921,7 @@ export default class Clerk implements ClerkInterface { beforeEmit: navigateAfterSignUp, }); case 'missing_requirements': - return navigateToContinueSignUp(); + return navigateToNextStepSignUp(); default: clerkOAuthCallbackDidNotCompleteSignInSignUp('sign in'); } @@ -938,7 +957,7 @@ export default class Clerk implements ClerkInterface { } if (su.externalAccountStatus === 'verified' && su.status === 'missing_requirements') { - return navigateToContinueSignUp(); + return navigateToNextStepSignUp(); } return navigateToSignIn(); diff --git a/packages/clerk-js/src/ui/components/SignUp/SignUp.tsx b/packages/clerk-js/src/ui/components/SignUp/SignUp.tsx index d85ff69995e..2384a4056e5 100644 --- a/packages/clerk-js/src/ui/components/SignUp/SignUp.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/SignUp.tsx @@ -44,6 +44,8 @@ function SignUpRoutes(): JSX.Element { redirectUrl={signUpContext.redirectUrl} secondFactorUrl={signUpContext.secondFactorUrl} continueSignUpUrl='../continue' + verifyEmailAddressUrl='../verify-email-address' + verifyPhoneNumberUrl='../verify-phone-number' /> diff --git a/packages/clerk-js/src/ui/components/SignUp/util.ts b/packages/clerk-js/src/ui/components/SignUp/util.ts index 6d9742ad30b..4b6ca9efabe 100644 --- a/packages/clerk-js/src/ui/components/SignUp/util.ts +++ b/packages/clerk-js/src/ui/components/SignUp/util.ts @@ -10,6 +10,32 @@ type CompleteSignUpFlowProps = { redirectUrlComplete?: string; }; +export const handleMissingRequirements = ({ + signUp, + verifyEmailPath, + verifyPhonePath, + navigate, + redirectUrl = '', + redirectUrlComplete = '', +}: CompleteSignUpFlowProps): Promise | undefined => { + if (signUp.missingFields.some(mf => mf === 'saml')) { + return signUp.authenticateWithRedirect({ + strategy: 'saml', + redirectUrl, + redirectUrlComplete, + continueSignUp: true, + }); + } + + if (signUp.unverifiedFields?.includes('email_address') && verifyEmailPath) { + return navigate(verifyEmailPath); + } + if (signUp.unverifiedFields?.includes('phone_number') && verifyPhonePath) { + return navigate(verifyPhonePath); + } + return; +}; + export const completeSignUpFlow = ({ signUp, verifyEmailPath, @@ -19,24 +45,18 @@ export const completeSignUpFlow = ({ redirectUrl = '', redirectUrlComplete = '', }: CompleteSignUpFlowProps): Promise | undefined => { - if (signUp.status === 'complete') { - return handleComplete && handleComplete(); - } else if (signUp.status === 'missing_requirements') { - if (signUp.missingFields.some(mf => mf === 'saml')) { - return signUp.authenticateWithRedirect({ - strategy: 'saml', + switch (signUp.status) { + case 'complete': + return handleComplete && handleComplete(); + case 'missing_requirements': + return handleMissingRequirements({ + signUp, + verifyEmailPath, + verifyPhonePath, + navigate, redirectUrl, redirectUrlComplete, - continueSignUp: true, }); - } - - if (signUp.unverifiedFields?.includes('email_address') && verifyEmailPath) { - return navigate(verifyEmailPath); - } - if (signUp.unverifiedFields?.includes('phone_number') && verifyPhonePath) { - return navigate(verifyPhonePath); - } } return; }; diff --git a/packages/types/src/clerk.ts b/packages/types/src/clerk.ts index 155edd9c5a6..59110b940fa 100644 --- a/packages/types/src/clerk.ts +++ b/packages/types/src/clerk.ts @@ -451,6 +451,16 @@ export type HandleOAuthCallbackParams = { * Full URL or path to navigate after an incomplete sign up. */ continueSignUpUrl?: string | null; + + /** + * Full URL or path to navigate after requesting email verification. + */ + verifyEmailAddressUrl?: string | null; + + /** + * Full URL or path to navigate after requesting phone verification. + */ + verifyPhoneNumberUrl?: string | null; }; /**