Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(clerk-js): Add support for combined flow in buildUrl #4626

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,10 @@ export class Clerk implements ClerkInterface {
}
};

#isCombinedFlow(): boolean {
return this.#options.experimental?.combinedFlow && this.#options.signInUrl === this.#options.signUpUrl;
}

public signOut: SignOut = async (callbackOrOptions?: SignOutCallback | SignOutOptions, options?: SignOutOptions) => {
if (!this.client || this.client.sessions.length === 0) {
return;
Expand Down Expand Up @@ -1981,10 +1985,18 @@ export class Clerk implements ClerkInterface {
if (!key || !this.loaded || !this.environment || !this.environment.displayConfig) {
return '';
}

const signInOrUpUrl = this.#options[key] || this.environment.displayConfig[key];
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: signInOrUpUrl,
hashPath: this.#isCombinedFlow() && key === 'signUpUrl' ? '/create' : '',
hashSearchParams: [initValues, redirectUrls],
},
{ stringify: true },
);
return this.buildUrlWithAuth(url);
};

Expand Down
1 change: 1 addition & 0 deletions packages/clerk-js/src/core/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
Expand Down
5 changes: 3 additions & 2 deletions packages/clerk-js/src/ui/components/SignIn/SignIn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SignInEmailLinkFlowComplete, SignUpEmailLinkFlowComplete } from '../../
import {
SignInContext,
SignUpContext,
useOptions,
useSignInContext,
useSignUpContext,
withCoreSessionSwitchGuard,
Expand Down Expand Up @@ -36,6 +37,7 @@ function RedirectToSignIn() {
function SignInRoutes(): JSX.Element {
const signInContext = useSignInContext();
const signUpContext = useSignUpContext();
const options = useOptions();

return (
<Flow.Root flow='signIn'>
Expand Down Expand Up @@ -74,7 +76,7 @@ function SignInRoutes(): JSX.Element {
redirectUrl='../factor-two'
/>
</Route>
{signInContext.__experimental?.combinedFlow && (
{options.experimental?.combinedFlow && (
<Route path='create'>
<Route
path='verify-email-address'
Expand Down Expand Up @@ -148,7 +150,6 @@ function SignInRoot() {
value={{
componentName: 'SignUp',
...signInContext.__experimental?.signUpProps,
...(signInContext.__experimental?.combinedFlow ? { __experimental: { combinedFlow: true } } : {}),
}}
>
<SignInRoutes />
Expand Down
30 changes: 20 additions & 10 deletions packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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, signUpUrl, waitlistUrl } = ctx;
const isCombinedFlow = (options?.experimental?.combinedFlow && options.signInUrl === options.signUpUrl) || false;
const supportEmail = useSupportEmail();
const identifierAttributes = useMemo<SignInStartIdentifier[]>(
() => groupIdentifiers(userSettings.enabledFirstFactorIdentifiers),
Expand Down Expand Up @@ -327,18 +328,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'
? {
Expand All @@ -350,11 +357,14 @@ export function _SignInStart(): JSX.Element {
);
return navigate(waitlistUrl);
}
if (isCombinedFlow) {
const attribute = getSignUpAttributeFromIdentifier(identifierField);
clerk.client.signUp[attribute] = identifierField.value;
return navigate('create');

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);
}
};
Expand Down
7 changes: 4 additions & 3 deletions packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<ActiveIdentifier>(
getInitialActiveIdentifier(attributes, userSettings.signUp.progressive),
);
Expand Down
3 changes: 2 additions & 1 deletion packages/types/src/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,7 @@ export type ClerkOptions = ClerkOptionsNavigation &
{
persistClient: boolean;
rethrowOfflineNetworkErrors: boolean;
combinedFlow: boolean;
},
Record<string, any>
>;
Expand Down Expand Up @@ -896,7 +897,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<string, any> & { newComponents?: boolean; combinedFlow?: boolean; signUpProps?: SignUpProps };
__experimental?: Record<string, any> & { newComponents?: boolean; signUpProps?: SignUpProps };
/**
* Full URL or path to for the waitlist process.
* Used to fill the "Join waitlist" link in the SignUp component.
Expand Down
Loading