Skip to content

Commit ab4eb56

Browse files
authoredDec 13, 2023
feat(clerk-js,types): Drop redirectToHome in favour of redirectToAfterSignIn & redirectToAfterSignUp [SDK-875] (#2251)
* feat(clerk-js,types): Dropped redirectToHome and added redirectToAfterSignIn & redirectToAfterSignUp * feat(clerk-js,types): Add defaults for buildRedirectToAfterSignUpUrl & buildRedirectToAfterSignInUrl * feat(clerk-js,types): Added withRedirectToAfterSignIn & withRedirectToAfterSignUp * fix(types): Update types for retheme * chore(repo): Update Changesets * refactor(clerk-js): Remove withRedirectToHomeUserGuard from UserProfile * refactor(clerk-js): Remove withRedirectToHomeOrganizationGuard from OrganizationProfile * fix(clerk-js): Drop withRedirect from ui.retheme * fix(clerk-js): Show warnings when User is not signed in or Organization is not active * test(clerk-js): Update tests for withRedirect * fix(clerk-js,shared): Added isDevelopmentFromPublishableKey & isProductionFromPublishableKey * fix(clerk-react): Add buildAfterSignInUrl & buildAfterSignUpUrl * test(clerk-js): Fix withRedirect tests * test(clerk-js): Fix withRedirect tests * test(clerk-js): Remove unneeded React import
1 parent 385cc77 commit ab4eb56

24 files changed

+324
-239
lines changed
 

‎.changeset/pretty-scissors-thank.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@clerk/clerk-js': major
3+
'@clerk/clerk-react': major
4+
'@clerk/types': patch
5+
---
6+
7+
Drop `redirectToHome` redirect method in favour of `redirectToAfterSignUp` or `redirectToAfterSignIn`.
8+
9+
When the `<SignIn/>` and `<SignUp/>` components are rendered while a user is already logged in, they will now redirect to the configured `afterSignIn` and `afterSignUp` URLs, respectively. Previously, the redirect URL was set to the home URL configured in the dashboard.

‎packages/clerk-js/src/core/clerk.redirects.test.ts

+14-14
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ describe('Clerk singleton - Redirects', () => {
9595
mockNavigate = jest.fn((to: string) => Promise.resolve(to));
9696
});
9797

98-
describe('.redirectTo(SignUp|SignIn|UserProfile|Home|CreateOrganization|OrganizationProfile)', () => {
98+
describe('.redirectTo(SignUp|SignIn|UserProfile|AfterSignIn|AfterSignUp|CreateOrganization|OrganizationProfile)', () => {
9999
let clerkForProductionInstance: Clerk;
100100
let clerkForDevelopmentInstance: Clerk;
101101

@@ -152,12 +152,20 @@ describe('Clerk singleton - Redirects', () => {
152152
expect(mockNavigate).toHaveBeenNthCalledWith(2, '/user-profile');
153153
});
154154

155-
it('redirects to home', async () => {
156-
await clerkForProductionInstance.redirectToHome();
157-
expect(mockNavigate).toHaveBeenNthCalledWith(1, '/home');
155+
it('redirects to afterSignUp', async () => {
156+
await clerkForProductionInstance.redirectToAfterSignUp();
157+
expect(mockNavigate).toHaveBeenNthCalledWith(1, '/');
158158

159-
await clerkForDevelopmentInstance.redirectToHome();
160-
expect(mockNavigate).toHaveBeenNthCalledWith(2, '/home');
159+
await clerkForDevelopmentInstance.redirectToAfterSignUp();
160+
expect(mockNavigate).toHaveBeenNthCalledWith(2, '/');
161+
});
162+
163+
it('redirects to afterSignIn', async () => {
164+
await clerkForProductionInstance.redirectToAfterSignIn();
165+
expect(mockNavigate).toHaveBeenNthCalledWith(1, '/');
166+
167+
await clerkForDevelopmentInstance.redirectToAfterSignIn();
168+
expect(mockNavigate).toHaveBeenNthCalledWith(2, '/');
161169
});
162170

163171
it('redirects to create organization', async () => {
@@ -242,14 +250,6 @@ describe('Clerk singleton - Redirects', () => {
242250
expect(mockHref).toHaveBeenNthCalledWith(2, 'http://another-test.host/user-profile#__clerk_db_jwt[deadbeef]');
243251
});
244252

245-
it('redirects to home', async () => {
246-
await clerkForProductionInstance.redirectToHome();
247-
expect(mockHref).toHaveBeenNthCalledWith(1, 'http://another-test.host/home');
248-
249-
await clerkForDevelopmentInstance.redirectToHome();
250-
expect(mockHref).toHaveBeenNthCalledWith(2, 'http://another-test.host/home#__clerk_db_jwt[deadbeef]');
251-
});
252-
253253
it('redirects to create organization', async () => {
254254
await clerkForProductionInstance.redirectToCreateOrganization();
255255
expect(mockHref).toHaveBeenNthCalledWith(1, 'http://another-test.host/create-organization');

‎packages/clerk-js/src/core/clerk.ts

+37-4
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,8 @@ export class Clerk implements ClerkInterface {
327327
public openSignIn = (props?: SignInProps): void => {
328328
this.assertComponentsReady(this.#componentControls);
329329
if (sessionExistsAndSingleSessionModeEnabled(this, this.#environment) && this.#instanceType === 'development') {
330-
return console.info(warnings.cannotOpenSignUpOrSignUp);
330+
console.info(warnings.cannotOpenSignUpOrSignUp);
331+
return;
331332
}
332333
void this.#componentControls
333334
.ensureMounted({ preloadHint: 'SignIn' })
@@ -372,7 +373,8 @@ export class Clerk implements ClerkInterface {
372373
public openOrganizationProfile = (props?: OrganizationProfileProps): void => {
373374
this.assertComponentsReady(this.#componentControls);
374375
if (noOrganizationExists(this) && this.#instanceType === 'development') {
375-
return console.info(warnings.cannotOpenOrgProfile);
376+
console.info(warnings.cannotOpenOrgProfile);
377+
return;
376378
}
377379
void this.#componentControls
378380
.ensureMounted({ preloadHint: 'OrganizationProfile' })
@@ -442,6 +444,10 @@ export class Clerk implements ClerkInterface {
442444

443445
public mountUserProfile = (node: HTMLDivElement, props?: UserProfileProps): void => {
444446
this.assertComponentsReady(this.#componentControls);
447+
if (noUserExists(this) && this.#instanceType === 'development') {
448+
console.info(warnings.cannotRenderComponentWhenUserDoesNotExist);
449+
return;
450+
}
445451
void this.#componentControls.ensureMounted({ preloadHint: 'UserProfile' }).then(controls =>
446452
controls.mountComponent({
447453
name: 'UserProfile',
@@ -465,6 +471,10 @@ export class Clerk implements ClerkInterface {
465471

466472
public mountOrganizationProfile = (node: HTMLDivElement, props?: OrganizationProfileProps) => {
467473
this.assertComponentsReady(this.#componentControls);
474+
if (noOrganizationExists(this) && this.#instanceType === 'development') {
475+
console.info(warnings.cannotRenderComponentWhenOrgDoesNotExist);
476+
return;
477+
}
468478
void this.#componentControls.ensureMounted({ preloadHint: 'OrganizationProfile' }).then(controls =>
469479
controls.mountComponent({
470480
name: 'OrganizationProfile',
@@ -736,6 +746,22 @@ export class Clerk implements ClerkInterface {
736746
return this.buildUrlWithAuth(this.#environment.displayConfig.homeUrl);
737747
}
738748

749+
public buildAfterSignInUrl(): string {
750+
if (!this.#options.afterSignInUrl) {
751+
return '/';
752+
}
753+
754+
return this.buildUrlWithAuth(this.#options.afterSignInUrl);
755+
}
756+
757+
public buildAfterSignUpUrl(): string {
758+
if (!this.#options.afterSignUpUrl) {
759+
return '/';
760+
}
761+
762+
return this.buildUrlWithAuth(this.#options.afterSignUpUrl);
763+
}
764+
739765
public buildCreateOrganizationUrl(): string {
740766
if (!this.#environment || !this.#environment.displayConfig) {
741767
return '';
@@ -812,9 +838,16 @@ export class Clerk implements ClerkInterface {
812838
return;
813839
};
814840

815-
public redirectToHome = async (): Promise<unknown> => {
841+
public redirectToAfterSignIn = async (): Promise<unknown> => {
842+
if (inBrowser()) {
843+
return this.navigate(this.buildAfterSignInUrl());
844+
}
845+
return;
846+
};
847+
848+
public redirectToAfterSignUp = async (): Promise<unknown> => {
816849
if (inBrowser()) {
817-
return this.navigate(this.buildHomeUrl());
850+
return this.navigate(this.buildAfterSignUpUrl());
818851
}
819852
return;
820853
};

‎packages/clerk-js/src/core/warnings.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ const formatWarning = (msg: string) => {
55
const warnings = {
66
cannotRenderComponentWhenSessionExists:
77
'The <SignUp/> and <SignIn/> components cannot render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, Clerk is redirecting to the Home URL instead.',
8+
cannotRenderSignUpComponentWhenSessionExists:
9+
'The <SignUp/> component cannot render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, Clerk is redirecting to the value setted in `afterSignUp` URL instead.',
10+
cannotRenderSignInComponentWhenSessionExists:
11+
'The <SignIn/> component cannot render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, Clerk is redirecting to the `afterSignIn` URL instead.',
812
cannotRenderComponentWhenUserDoesNotExist:
9-
'<UserProfile/> cannot render unless a user is signed in. Since no user is signed in, Clerk is redirecting to the Home URL instead. (This notice only appears in development.)',
10-
cannotRenderComponentWhenOrgDoesNotExist: `<OrganizationProfile/> cannot render unless an organization is active. Since no organization is currently active, Clerk is redirecting to the Home URL instead.`,
13+
'<UserProfile/> cannot render unless a user is signed in. Since no user is signed in, this is no-op.',
14+
cannotRenderComponentWhenOrgDoesNotExist: `<OrganizationProfile/> cannot render unless an organization is active. Since no organization is currently active, this is no-op.`,
1115
cannotOpenOrgProfile:
1216
'The OrganizationProfile cannot render unless an organization is active. Since no organization is currently active, this is no-op.',
1317
cannotOpenUserProfile:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from 'react';
2+
3+
import { render } from '../../../testUtils';
4+
import { bindCreateFixtures } from '../../utils/test/createFixtures';
5+
import { withRedirect } from '../withRedirect';
6+
7+
const { createFixtures } = bindCreateFixtures('SignIn');
8+
9+
describe('withRedirect', () => {
10+
it('redirects to the redirect url provided when condition is met', async () => {
11+
const { wrapper, fixtures } = await createFixtures(f => {
12+
f.withUser({});
13+
});
14+
15+
const WithHOC = withRedirect(
16+
() => <></>,
17+
() => true,
18+
() => '/',
19+
'Redirecting to /',
20+
);
21+
22+
render(<WithHOC />, { wrapper });
23+
24+
expect(fixtures.router.navigate).toHaveBeenCalledWith('/');
25+
});
26+
27+
it('does no redirects to the redirect url provided when the condition is not met', async () => {
28+
const { wrapper, fixtures } = await createFixtures(f => {
29+
f.withUser({});
30+
});
31+
32+
const WithHOC = withRedirect(
33+
() => <></>,
34+
() => false,
35+
() => '/',
36+
'Redirecting to /',
37+
);
38+
39+
render(<WithHOC />, { wrapper });
40+
41+
expect(fixtures.router.navigate).not.toHaveBeenCalledWith('/');
42+
});
43+
});

‎packages/clerk-js/src/ui/common/__tests__/withRedirectToHome.test.tsx

-102
This file was deleted.

‎packages/clerk-js/src/ui/common/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export * from './Gate';
66
export * from './InfiniteListSpinner';
77
export * from './redirects';
88
export * from './verification';
9-
export * from './withRedirectToHome';
9+
export * from './withRedirect';
1010
export * from './SSOCallback';
1111
export * from './EmailLinkVerify';
1212
export * from './EmailLinkStatusCard';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { isDevelopmentFromPublishableKey } from '@clerk/shared/keys';
2+
import { useClerk } from '@clerk/shared/react';
3+
import type { Clerk, ClerkOptions, EnvironmentResource } from '@clerk/types';
4+
import type { ComponentType } from 'react';
5+
import React from 'react';
6+
7+
import { warnings } from '../../core/warnings';
8+
import type { ComponentGuard } from '../../utils';
9+
import { sessionExistsAndSingleSessionModeEnabled } from '../../utils';
10+
import { useEnvironment, useOptions, useSignInContext, useSignUpContext } from '../contexts';
11+
import { useRouter } from '../router';
12+
import type { AvailableComponentProps } from '../types';
13+
14+
type RedirectUrl = (opts: { clerk: Clerk; environment: EnvironmentResource; options: ClerkOptions }) => string;
15+
16+
export function withRedirect<P extends AvailableComponentProps>(
17+
Component: ComponentType<P>,
18+
condition: ComponentGuard,
19+
redirectUrl: RedirectUrl,
20+
warning?: string,
21+
): (props: P) => null | JSX.Element {
22+
const displayName = Component.displayName || Component.name || 'Component';
23+
Component.displayName = displayName;
24+
25+
const HOC = (props: P) => {
26+
const { navigate } = useRouter();
27+
const clerk = useClerk();
28+
const environment = useEnvironment();
29+
const options = useOptions();
30+
31+
const shouldRedirect = condition(clerk, environment, options);
32+
React.useEffect(() => {
33+
if (shouldRedirect) {
34+
if (warning && isDevelopmentFromPublishableKey(clerk.publishableKey)) {
35+
console.info(warning);
36+
}
37+
// TODO: Fix this properly
38+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
39+
navigate(redirectUrl({ clerk, environment, options }));
40+
}
41+
}, []);
42+
43+
if (shouldRedirect) {
44+
return null;
45+
}
46+
47+
return <Component {...props} />;
48+
};
49+
50+
HOC.displayName = `withRedirect(${displayName})`;
51+
52+
return HOC;
53+
}
54+
55+
export const withRedirectToAfterSignIn = <P extends AvailableComponentProps>(Component: ComponentType<P>) => {
56+
const displayName = Component.displayName || Component.name || 'Component';
57+
Component.displayName = displayName;
58+
59+
const HOC = (props: P) => {
60+
const signInCtx = useSignInContext();
61+
return withRedirect(
62+
Component,
63+
sessionExistsAndSingleSessionModeEnabled,
64+
({ clerk }) => signInCtx.afterSignInUrl || clerk.buildAfterSignInUrl(),
65+
warnings.cannotRenderSignInComponentWhenSessionExists,
66+
)(props);
67+
};
68+
69+
HOC.displayName = `withRedirectToAfterSignIn(${displayName})`;
70+
71+
return HOC;
72+
};
73+
74+
export const withRedirectToAfterSignUp = <P extends AvailableComponentProps>(Component: ComponentType<P>) => {
75+
const displayName = Component.displayName || Component.name || 'Component';
76+
Component.displayName = displayName;
77+
78+
const HOC = (props: P) => {
79+
const signUpCtx = useSignUpContext();
80+
return withRedirect(
81+
Component,
82+
sessionExistsAndSingleSessionModeEnabled,
83+
({ clerk }) => signUpCtx.afterSignUpUrl || clerk.buildAfterSignUpUrl(),
84+
warnings.cannotRenderSignUpComponentWhenSessionExists,
85+
)(props);
86+
};
87+
88+
HOC.displayName = `withRedirectToAfterSignUp(${displayName})`;
89+
90+
return HOC;
91+
};
92+
93+
export const withRedirectToHomeSingleSessionGuard = <P extends AvailableComponentProps>(Component: ComponentType<P>) =>
94+
withRedirect(
95+
Component,
96+
sessionExistsAndSingleSessionModeEnabled,
97+
({ environment }) => environment.displayConfig.homeUrl,
98+
warnings.cannotRenderComponentWhenSessionExists,
99+
);

‎packages/clerk-js/src/ui/common/withRedirectToHome.tsx

-61
This file was deleted.

‎packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfile.tsx

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useOrganization } from '@clerk/shared/react';
22
import type { OrganizationProfileModalProps, OrganizationProfileProps } from '@clerk/types';
33
import React from 'react';
44

5-
import { withOrganizationsEnabledGuard, withRedirectToHomeOrganizationGuard } from '../../common';
5+
import { withOrganizationsEnabledGuard } from '../../common';
66
import { ComponentContext, withCoreUserGuard } from '../../contexts';
77
import { Flow } from '../../customizables';
88
import { ProfileCard, withCardStateProvider } from '../../elements';
@@ -44,10 +44,12 @@ const AuthenticatedRoutes = withCoreUserGuard(() => {
4444
);
4545
});
4646

47-
export const OrganizationProfile = withRedirectToHomeOrganizationGuard(
48-
withOrganizationsEnabledGuard(withCardStateProvider(_OrganizationProfile), 'OrganizationProfile', {
47+
export const OrganizationProfile = withOrganizationsEnabledGuard(
48+
withCardStateProvider(_OrganizationProfile),
49+
'OrganizationProfile',
50+
{
4951
mode: 'redirect',
50-
}),
52+
},
5153
);
5254

5355
export const OrganizationProfileModal = (props: OrganizationProfileModalProps): JSX.Element => {

‎packages/clerk-js/src/ui/components/SignIn/ResetPassword.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from 'react';
22

33
import { clerkInvalidFAPIResponse } from '../../../core/errors';
4-
import { withRedirectToHomeSingleSessionGuard } from '../../common';
54
import { useCoreSignIn, useEnvironment } from '../../contexts';
65
import { Col, descriptors, localizationKeys, useLocalizations } from '../../customizables';
76
import { Card, CardAlert, Form, Header, useCardState, withCardStateProvider } from '../../elements';
@@ -148,4 +147,4 @@ export const _ResetPassword = () => {
148147
);
149148
};
150149

151-
export const ResetPassword = withRedirectToHomeSingleSessionGuard(withCardStateProvider(_ResetPassword));
150+
export const ResetPassword = withCardStateProvider(_ResetPassword);

‎packages/clerk-js/src/ui/components/SignIn/ResetPasswordSuccess.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { withRedirectToHomeSingleSessionGuard } from '../../common';
21
import { Col, descriptors, localizationKeys, Spinner, Text } from '../../customizables';
32
import { Card, CardAlert, Header, useCardState, withCardStateProvider } from '../../elements';
43
import { useSetSessionWithTimeout } from '../../hooks/useSetSessionWithTimeout';
@@ -33,4 +32,4 @@ export const _ResetPasswordSuccess = () => {
3332
);
3433
};
3534

36-
export const ResetPasswordSuccess = withRedirectToHomeSingleSessionGuard(withCardStateProvider(_ResetPasswordSuccess));
35+
export const ResetPasswordSuccess = withCardStateProvider(_ResetPasswordSuccess);

‎packages/clerk-js/src/ui/components/SignIn/SignInAccountSwitcher.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { withRedirectToHomeSingleSessionGuard } from '../../common';
1+
import { withRedirectToAfterSignIn } from '../../common';
22
import { useEnvironment, useSignInContext } from '../../contexts';
33
import { Col, descriptors, Flow, Icon } from '../../customizables';
44
import { Card, CardAlert, Header, PreviewButton, UserPreview, withCardStateProvider } from '../../elements';
@@ -77,6 +77,4 @@ const _SignInAccountSwitcher = () => {
7777
</Flow.Part>
7878
);
7979
};
80-
export const SignInAccountSwitcher = withRedirectToHomeSingleSessionGuard(
81-
withCardStateProvider(_SignInAccountSwitcher),
82-
);
80+
export const SignInAccountSwitcher = withRedirectToAfterSignIn(withCardStateProvider(_SignInAccountSwitcher));

‎packages/clerk-js/src/ui/components/SignIn/SignInFactorOne.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ResetPasswordCodeFactor, SignInFactor } from '@clerk/types';
22
import React from 'react';
33

4-
import { withRedirectToHomeSingleSessionGuard } from '../../common';
4+
import { withRedirectToAfterSignIn } from '../../common';
55
import { useCoreSignIn, useEnvironment } from '../../contexts';
66
import { ErrorCard, LoadingCard, withCardStateProvider } from '../../elements';
77
import { useAlternativeStrategies } from '../../hooks/useAlternativeStrategies';
@@ -197,4 +197,4 @@ export function _SignInFactorOne(): JSX.Element {
197197
}
198198
}
199199

200-
export const SignInFactorOne = withRedirectToHomeSingleSessionGuard(withCardStateProvider(_SignInFactorOne));
200+
export const SignInFactorOne = withRedirectToAfterSignIn(withCardStateProvider(_SignInFactorOne));

‎packages/clerk-js/src/ui/components/SignIn/SignInFactorTwo.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { SignInFactor } from '@clerk/types';
22
import React from 'react';
33

4-
import { withRedirectToHomeSingleSessionGuard } from '../../common';
4+
import { withRedirectToAfterSignIn } from '../../common';
55
import { useCoreSignIn } from '../../contexts';
66
import { LoadingCard, withCardStateProvider } from '../../elements';
77
import { SignInFactorTwoAlternativeMethods } from './SignInFactorTwoAlternativeMethods';
@@ -81,4 +81,4 @@ export function _SignInFactorTwo(): JSX.Element {
8181
}
8282
}
8383

84-
export const SignInFactorTwo = withRedirectToHomeSingleSessionGuard(withCardStateProvider(_SignInFactorTwo));
84+
export const SignInFactorTwo = withRedirectToAfterSignIn(withCardStateProvider(_SignInFactorTwo));
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import { SSOCallback, withRedirectToHomeSingleSessionGuard } from '../../common';
1+
import { SSOCallback, withRedirectToAfterSignIn } from '../../common';
22

3-
export const SignInSSOCallback = withRedirectToHomeSingleSessionGuard(SSOCallback);
3+
export const SignInSSOCallback = withRedirectToAfterSignIn(SSOCallback);

‎packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx

+2-6
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ import { ERROR_CODES } from '../../../core/constants';
66
import { clerkInvalidFAPIResponse } from '../../../core/errors';
77
import { getClerkQueryParam } from '../../../utils';
88
import type { SignInStartIdentifier } from '../../common';
9-
import {
10-
getIdentifierControlDisplayValues,
11-
groupIdentifiers,
12-
withRedirectToHomeSingleSessionGuard,
13-
} from '../../common';
9+
import { getIdentifierControlDisplayValues, groupIdentifiers, withRedirectToAfterSignIn } from '../../common';
1410
import { buildSSOCallbackURL } from '../../common/redirects';
1511
import { useCoreSignIn, useEnvironment, useSignInContext } from '../../contexts';
1612
import { Col, descriptors, Flow, localizationKeys } from '../../customizables';
@@ -390,4 +386,4 @@ const InstantPasswordRow = ({ field }: { field?: FormControlState<'password'> })
390386
);
391387
};
392388

393-
export const SignInStart = withRedirectToHomeSingleSessionGuard(withCardStateProvider(_SignInStart));
389+
export const SignInStart = withRedirectToAfterSignIn(withCardStateProvider(_SignInStart));
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import { SSOCallback, withRedirectToHomeSingleSessionGuard } from '../../common';
1+
import { SSOCallback, withRedirectToAfterSignUp } from '../../common';
22

3-
export const SignUpSSOCallback = withRedirectToHomeSingleSessionGuard(SSOCallback);
3+
export const SignUpSSOCallback = withRedirectToAfterSignUp(SSOCallback);

‎packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React from 'react';
33

44
import { ERROR_CODES } from '../../../core/constants';
55
import { getClerkQueryParam } from '../../../utils/getClerkQueryParam';
6-
import { buildSSOCallbackURL, withRedirectToHomeSingleSessionGuard } from '../../common';
6+
import { buildSSOCallbackURL, withRedirectToAfterSignUp } from '../../common';
77
import { useCoreSignUp, useEnvironment, useSignUpContext } from '../../contexts';
88
import { descriptors, Flex, Flow, localizationKeys, useAppearance, useLocalizations } from '../../customizables';
99
import {
@@ -289,4 +289,4 @@ function _SignUpStart(): JSX.Element {
289289
);
290290
}
291291

292-
export const SignUpStart = withRedirectToHomeSingleSessionGuard(withCardStateProvider(_SignUpStart));
292+
export const SignUpStart = withRedirectToAfterSignUp(withCardStateProvider(_SignUpStart));

‎packages/clerk-js/src/ui/components/UserProfile/UserProfile.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { UserProfileModalProps, UserProfileProps } from '@clerk/types';
22
import React from 'react';
33

4-
import { withRedirectToHomeUserGuard } from '../../common';
54
import { ComponentContext, withCoreUserGuard } from '../../contexts';
65
import { Flow } from '../../customizables';
76
import { ProfileCard, withCardStateProvider } from '../../elements';
@@ -51,7 +50,7 @@ const AuthenticatedRoutes = withCoreUserGuard(() => {
5150
);
5251
});
5352

54-
export const UserProfile = withRedirectToHomeUserGuard(withCardStateProvider(_UserProfile));
53+
export const UserProfile = withCardStateProvider(_UserProfile);
5554

5655
export const UserProfileModal = (props: UserProfileModalProps): JSX.Element => {
5756
const userProfileProps: UserProfileCtx = {

‎packages/react/src/isomorphicClerk.ts

+36-17
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ type IsomorphicLoadedClerk = Without<
7373
| 'buildUserProfileUrl'
7474
| 'buildCreateOrganizationUrl'
7575
| 'buildOrganizationProfileUrl'
76-
| 'buildHomeUrl'
76+
| 'buildAfterSignUpUrl'
77+
| 'buildAfterSignInUrl'
7778
| 'buildUrlWithAuth'
7879
| 'handleRedirectCallback'
7980
| 'handleUnauthenticated'
@@ -111,10 +112,11 @@ type IsomorphicLoadedClerk = Without<
111112
// TODO: Align return type
112113
buildOrganizationProfileUrl: () => string | void;
113114
// TODO: Align return type
114-
buildHomeUrl: () => string | void;
115-
// TODO: Align return type
116115
buildUrlWithAuth: (to: string, opts?: BuildUrlWithAuthParams | undefined) => string | void;
117-
116+
// TODO: Align return type
117+
buildAfterSignInUrl: () => string | void;
118+
// TODO: Align return type
119+
buildAfterSignUpUrl: () => string | void;
118120
// TODO: Align optional props
119121
mountUserButton: (node: HTMLDivElement, props: UserButtonProps) => void;
120122
mountOrganizationList: (node: HTMLDivElement, props: OrganizationListProps) => void;
@@ -260,6 +262,24 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk {
260262
}
261263
};
262264

265+
buildAfterSignInUrl = (): string | void => {
266+
const callback = () => this.clerkjs?.buildAfterSignInUrl() || '';
267+
if (this.clerkjs && this.#loaded) {
268+
return callback();
269+
} else {
270+
this.premountMethodCalls.set('buildAfterSignInUrl', callback);
271+
}
272+
};
273+
274+
buildAfterSignUpUrl = (): string | void => {
275+
const callback = () => this.clerkjs?.buildAfterSignUpUrl() || '';
276+
if (this.clerkjs && this.#loaded) {
277+
return callback();
278+
} else {
279+
this.premountMethodCalls.set('buildAfterSignUpUrl', callback);
280+
}
281+
};
282+
263283
buildUserProfileUrl = (): string | void => {
264284
const callback = () => this.clerkjs?.buildUserProfileUrl() || '';
265285
if (this.clerkjs && this.#loaded) {
@@ -287,15 +307,6 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk {
287307
}
288308
};
289309

290-
buildHomeUrl = (): string | void => {
291-
const callback = () => this.clerkjs?.buildHomeUrl() || '';
292-
if (this.clerkjs && this.#loaded) {
293-
return callback();
294-
} else {
295-
this.premountMethodCalls.set('buildHomeUrl', callback);
296-
}
297-
};
298-
299310
buildUrlWithAuth = (to: string, opts?: BuildUrlWithAuthParams | undefined): string | void => {
300311
const callback = () => this.clerkjs?.buildUrlWithAuth(to, opts) || '';
301312
if (this.clerkjs && this.#loaded) {
@@ -808,13 +819,21 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk {
808819
}
809820
};
810821

811-
redirectToHome = async (): Promise<unknown> => {
812-
const callback = () => this.clerkjs?.redirectToHome();
822+
redirectToAfterSignUp = (): void => {
823+
const callback = () => this.clerkjs?.redirectToAfterSignUp();
813824
if (this.clerkjs && this.#loaded) {
814825
return callback();
815826
} else {
816-
this.premountMethodCalls.set('redirectToHome', callback);
817-
return;
827+
this.premountMethodCalls.set('redirectToAfterSignUp', callback);
828+
}
829+
};
830+
831+
redirectToAfterSignIn = (): void => {
832+
const callback = () => this.clerkjs?.redirectToAfterSignIn();
833+
if (this.clerkjs && this.#loaded) {
834+
callback();
835+
} else {
836+
this.premountMethodCalls.set('redirectToAfterSignIn', callback);
818837
}
819838
};
820839

‎packages/shared/src/__tests__/keys.test.ts

+34-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import {
22
buildPublishableKey,
33
createDevOrStagingUrlCache,
4+
isDevelopmentFromPublishableKey,
45
isDevelopmentFromSecretKey,
6+
isProductionFromPublishableKey,
57
isProductionFromSecretKey,
68
isPublishableKey,
79
parsePublishableKey,
@@ -105,6 +107,34 @@ describe('isDevOrStagingUrl(url)', () => {
105107
});
106108
});
107109

110+
describe('isDevelopmentFromPublishableKey(key)', () => {
111+
const cases: Array<[string, boolean]> = [
112+
['pk_live_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk', false],
113+
['pk_test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk', true],
114+
['live_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk', false],
115+
['test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk', true],
116+
];
117+
118+
test.each(cases)('given %p as a publishable key string, returns %p', (publishableKeyStr, expected) => {
119+
const result = isDevelopmentFromPublishableKey(publishableKeyStr);
120+
expect(result).toEqual(expected);
121+
});
122+
});
123+
124+
describe('isProductionFromPublishableKey(key)', () => {
125+
const cases: Array<[string, boolean]> = [
126+
['pk_live_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk', true],
127+
['pk_test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk', false],
128+
['live_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk', true],
129+
['test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk', false],
130+
];
131+
132+
test.each(cases)('given %p as a publishable key string, returns %p', (publishableKeyStr, expected) => {
133+
const result = isProductionFromPublishableKey(publishableKeyStr);
134+
expect(result).toEqual(expected);
135+
});
136+
});
137+
108138
describe('isDevelopmentFromSecretKey(key)', () => {
109139
const cases: Array<[string, boolean]> = [
110140
['sk_live_Y2xlcmsuY2xlcmsuZGV2JA==', false],
@@ -113,8 +143,8 @@ describe('isDevelopmentFromSecretKey(key)', () => {
113143
['test_Y2xlcmsuY2xlcmsuZGV2JA==', true],
114144
];
115145

116-
test.each(cases)('given %p as a publishable key string, returns %p', (publishableKeyStr, expected) => {
117-
const result = isDevelopmentFromSecretKey(publishableKeyStr);
146+
test.each(cases)('given %p as a secret key string, returns %p', (secretKeyStr, expected) => {
147+
const result = isDevelopmentFromSecretKey(secretKeyStr);
118148
expect(result).toEqual(expected);
119149
});
120150
});
@@ -127,8 +157,8 @@ describe('isProductionFromSecretKey(key)', () => {
127157
['test_Y2xlcmsuY2xlcmsuZGV2JA==', false],
128158
];
129159

130-
test.each(cases)('given %p as a publishable key string, returns %p', (publishableKeyStr, expected) => {
131-
const result = isProductionFromSecretKey(publishableKeyStr);
160+
test.each(cases)('given %p as a secret key string, returns %p', (secretKeyStr, expected) => {
161+
const result = isProductionFromSecretKey(secretKeyStr);
132162
expect(result).toEqual(expected);
133163
});
134164
});

‎packages/shared/src/keys.ts

+8
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ export function createDevOrStagingUrlCache() {
9393
};
9494
}
9595

96+
export function isDevelopmentFromPublishableKey(apiKey: string): boolean {
97+
return apiKey.startsWith('test_') || apiKey.startsWith('pk_test_');
98+
}
99+
100+
export function isProductionFromPublishableKey(apiKey: string): boolean {
101+
return apiKey.startsWith('live_') || apiKey.startsWith('pk_live_');
102+
}
103+
96104
export function isDevelopmentFromSecretKey(apiKey: string): boolean {
97105
return apiKey.startsWith('test_') || apiKey.startsWith('sk_test_');
98106
}

‎packages/types/src/clerk.ts

+14-4
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,14 @@ export interface Clerk {
342342
buildOrganizationProfileUrl(): string;
343343

344344
/**
345-
* Returns the configured home URL of the instance.
345+
* Returns the configured afterSignIn url of the instance.
346346
*/
347-
buildHomeUrl(): string;
347+
buildAfterSignInUrl(): string;
348+
349+
/**
350+
* Returns the configured afterSignIn url of the instance.
351+
*/
352+
buildAfterSignUpUrl(): string;
348353

349354
/**
350355
*
@@ -384,9 +389,14 @@ export interface Clerk {
384389
redirectToCreateOrganization: () => Promise<unknown>;
385390

386391
/**
387-
* Redirects to the configured home URL of the instance.
392+
* Redirects to the configured afterSignIn URL.
393+
*/
394+
redirectToAfterSignIn: () => void;
395+
396+
/**
397+
* Redirects to the configured afterSignUp URL.
388398
*/
389-
redirectToHome: () => Promise<unknown>;
399+
redirectToAfterSignUp: () => void;
390400

391401
/**
392402
* Completes an OAuth or SAML redirection flow started by

0 commit comments

Comments
 (0)
Please sign in to comment.