Skip to content

Commit d6b5006

Browse files
fix(clerk-js): Deprecate afterSignOutUrl from UserButton (#3544)
Co-authored-by: Nikos Douvlis <nikosdouvlis@gmail.com>
1 parent 9fa8831 commit d6b5006

File tree

9 files changed

+99
-14
lines changed

9 files changed

+99
-14
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/types': minor
4+
---
5+
6+
Deprecate `afterSignOutUrl` and `afterMultiSessionSingleSignOutUrl` from UserButton.
7+
8+
Developers can now configure these directly in `ClerkProvider` and have them work properly without in UserButton, UserProfile and in impersonation mode.

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,14 @@ export class Clerk implements ClerkInterface {
870870
return this.buildUrlWithAuth(this.#options.afterSignOutUrl);
871871
}
872872

873+
public buildAfterMultiSessionSingleSignOutUrl(): string {
874+
if (!this.#options.afterMultiSessionSingleSignOutUrl) {
875+
return this.buildAfterSignOutUrl();
876+
}
877+
878+
return this.buildUrlWithAuth(this.#options.afterMultiSessionSingleSignOutUrl);
879+
}
880+
873881
public buildCreateOrganizationUrl(): string {
874882
if (!this.environment || !this.environment.displayConfig) {
875883
return '';

packages/clerk-js/src/ui/components/ImpersonationFab/ImpersonationFab.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { useClerk, useSession } from '@clerk/shared/react';
1+
import { useClerk, useSession, useUser } from '@clerk/shared/react';
2+
import type { ActiveSessionResource } from '@clerk/types';
23
import type { PointerEventHandler } from 'react';
34
import React, { useEffect, useRef } from 'react';
45

56
import { getFullName, getIdentifier } from '../../../utils/user';
6-
import { withCoreUserGuard } from '../../contexts';
7+
import { useSignOutContext, withCoreUserGuard } from '../../contexts';
78
import type { LocalizationKey } from '../../customizables';
89
import {
910
Col,
@@ -17,6 +18,7 @@ import {
1718
useLocalizations,
1819
} from '../../customizables';
1920
import { Portal } from '../../elements/Portal';
21+
import { useMultipleSessions } from '../../hooks/useMultipleSessions';
2022
import { Eye } from '../../icons';
2123
import type { PropsOfComponent } from '../../styledSystem';
2224
import { InternalThemeProvider, mqu } from '../../styledSystem';
@@ -59,7 +61,17 @@ type FabContentProps = { title: LocalizationKey; signOutText: LocalizationKey };
5961

6062
const FabContent = ({ title, signOutText }: FabContentProps) => {
6163
const { session } = useSession();
64+
const { user } = useUser();
6265
const { signOut } = useClerk();
66+
const { otherSessions } = useMultipleSessions({ user });
67+
const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();
68+
69+
const handleSignOutSessionClicked = (session: ActiveSessionResource) => () => {
70+
if (otherSessions.length === 0) {
71+
return signOut(navigateAfterSignOut);
72+
}
73+
return signOut(navigateAfterMultiSessionSingleSignOutUrl, { sessionId: session.id });
74+
};
6375

6476
return (
6577
<Col
@@ -88,10 +100,10 @@ const FabContent = ({ title, signOutText }: FabContentProps) => {
88100
},
89101
})}
90102
localizationKey={signOutText}
91-
onClick={async () => {
103+
onClick={
92104
// clerk-js has been loaded at this point so we can safely access session
93-
await signOut({ sessionId: session!.id });
94-
}}
105+
handleSignOutSessionClicked(session!)
106+
}
95107
/>
96108
</Col>
97109
);

packages/clerk-js/src/ui/components/UserButton/useMultisessionActions.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { useClerk, useSessionList } from '@clerk/shared/react';
1+
import { useClerk } from '@clerk/shared/react';
22
import type { ActiveSessionResource, UserButtonProps, UserResource } from '@clerk/types';
33

44
import { windowNavigate } from '../../../utils/windowNavigate';
55
import { useCardState } from '../../elements';
6+
import { useMultipleSessions } from '../../hooks/useMultipleSessions';
67
import { useRouter } from '../../router';
78
import { sleep } from '../../utils';
89

@@ -19,10 +20,8 @@ type UseMultisessionActionsParams = {
1920
export const useMultisessionActions = (opts: UseMultisessionActionsParams) => {
2021
const { setActive, signOut, openUserProfile } = useClerk();
2122
const card = useCardState();
22-
const { sessions } = useSessionList();
23+
const { activeSessions, otherSessions } = useMultipleSessions({ user: opts.user });
2324
const { navigate } = useRouter();
24-
const activeSessions = sessions?.filter(s => s.status === 'active') as ActiveSessionResource[];
25-
const otherSessions = activeSessions.filter(s => s.user?.id !== opts.user?.id);
2625

2726
const handleSignOutSessionClicked = (session: ActiveSessionResource) => () => {
2827
if (otherSessions.length === 0) {

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import { useSignOutContext } from '../../contexts';
44
import { Col, localizationKeys, Text, useLocalizations } from '../../customizables';
55
import type { FormProps } from '../../elements';
66
import { Form, FormButtons, FormContainer, useCardState, withCardStateProvider } from '../../elements';
7+
import { useMultipleSessions } from '../../hooks/useMultipleSessions';
78
import { handleError, useFormControl } from '../../utils';
89

910
type DeleteUserFormProps = FormProps;
1011
export const DeleteUserForm = withCardStateProvider((props: DeleteUserFormProps) => {
1112
const { onReset } = props;
1213
const card = useCardState();
13-
const { navigateAfterSignOut } = useSignOutContext();
14+
const { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl } = useSignOutContext();
1415
const { user } = useUser();
1516
const { t } = useLocalizations();
17+
const { otherSessions } = useMultipleSessions({ user });
1618

1719
const confirmationField = useFormControl('deleteConfirmation', '', {
1820
type: 'text',
@@ -36,7 +38,12 @@ export const DeleteUserForm = withCardStateProvider((props: DeleteUserFormProps)
3638
}
3739

3840
await user.delete();
39-
await navigateAfterSignOut();
41+
42+
// TODO: Investigate if we need to call `setActive` with {session: null}
43+
if (otherSessions.length === 0) {
44+
return navigateAfterSignOut();
45+
}
46+
await navigateAfterMultiSessionSingleSignOutUrl();
4047
} catch (e) {
4148
handleError(e, [], card.setError);
4249
}

packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { deprecatedObjectProperty } from '@clerk/shared/deprecated';
12
import { useClerk } from '@clerk/shared/react';
23
import { snakeToCamel } from '@clerk/shared/underscore';
34
import type { HandleOAuthCallbackParams, OrganizationResource, UserResource } from '@clerk/types';
@@ -185,15 +186,17 @@ export const useSignInContext = (): SignInContextType => {
185186

186187
export type SignOutContextType = {
187188
navigateAfterSignOut: () => any;
189+
navigateAfterMultiSessionSingleSignOutUrl: () => any;
188190
};
189191

190192
export const useSignOutContext = (): SignOutContextType => {
191193
const { navigate } = useRouter();
192194
const clerk = useClerk();
193195

194196
const navigateAfterSignOut = () => navigate(clerk.buildAfterSignOutUrl());
197+
const navigateAfterMultiSessionSingleSignOutUrl = () => navigate(clerk.buildAfterMultiSessionSingleSignOutUrl());
195198

196-
return { navigateAfterSignOut };
199+
return { navigateAfterSignOut, navigateAfterMultiSessionSingleSignOutUrl };
197200
};
198201

199202
type PagesType = {
@@ -244,10 +247,22 @@ export const useUserButtonContext = () => {
244247
const signInUrl = ctx.signInUrl || options.signInUrl || displayConfig.signInUrl;
245248
const userProfileUrl = ctx.userProfileUrl || displayConfig.userProfileUrl;
246249

250+
if (ctx.afterSignOutUrl) {
251+
deprecatedObjectProperty(ctx, 'afterSignOutUrl', `Move 'afterSignOutUrl' to '<ClerkProvider/>`);
252+
}
253+
247254
const afterSignOutUrl = ctx.afterSignOutUrl || clerk.buildAfterSignOutUrl();
248255
const navigateAfterSignOut = () => navigate(afterSignOutUrl);
249256

250-
const afterMultiSessionSingleSignOutUrl = ctx.afterMultiSessionSingleSignOutUrl || afterSignOutUrl;
257+
if (ctx.afterSignOutUrl) {
258+
deprecatedObjectProperty(
259+
ctx,
260+
'afterMultiSessionSingleSignOutUrl',
261+
`Move 'afterMultiSessionSingleSignOutUrl' to '<ClerkProvider/>`,
262+
);
263+
}
264+
const afterMultiSessionSingleSignOutUrl =
265+
ctx.afterMultiSessionSingleSignOutUrl || clerk.buildAfterMultiSessionSingleSignOutUrl();
251266
const navigateAfterMultiSessionSingleSignOut = () => clerk.redirectWithAuth(afterMultiSessionSingleSignOutUrl);
252267

253268
const afterSwitchSessionUrl = ctx.afterSwitchSessionUrl || displayConfig.afterSwitchSessionUrl;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { useSessionList } from '@clerk/shared/react';
2+
import type { ActiveSessionResource, UserResource } from '@clerk/types';
3+
4+
type UseMultipleSessionsParam = {
5+
user: UserResource | null | undefined;
6+
};
7+
8+
const useMultipleSessions = (params: UseMultipleSessionsParam) => {
9+
const { sessions } = useSessionList();
10+
const activeSessions = sessions?.filter(s => s.status === 'active') as ActiveSessionResource[];
11+
const otherSessions = activeSessions.filter(s => s.user?.id !== params.user?.id);
12+
13+
return {
14+
activeSessions,
15+
otherSessions,
16+
};
17+
};
18+
19+
export { useMultipleSessions };

packages/types/src/clerk.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type { OAuthProvider, OAuthScope } from './oauth';
2020
import type { OrganizationResource } from './organization';
2121
import type { OrganizationCustomRoleKey } from './organizationMembership';
2222
import type {
23+
AfterMultiSessionSingleSignOutUrl,
2324
AfterSignOutUrl,
2425
LegacyRedirectProps,
2526
RedirectOptions,
@@ -387,6 +388,11 @@ export interface Clerk {
387388
*/
388389
buildAfterSignOutUrl(): string;
389390

391+
/**
392+
* Returns the configured afterMultiSessionSingleSignOutUrl of the instance.
393+
*/
394+
buildAfterMultiSessionSingleSignOutUrl(): string;
395+
390396
/**
391397
*
392398
* Redirects to the provided url after decorating it with the auth token for development instances.
@@ -564,7 +570,8 @@ export type ClerkOptions = ClerkOptionsNavigation &
564570
SignUpForceRedirectUrl &
565571
SignUpFallbackRedirectUrl &
566572
LegacyRedirectProps &
567-
AfterSignOutUrl & {
573+
AfterSignOutUrl &
574+
AfterMultiSessionSingleSignOutUrl & {
568575
appearance?: Appearance;
569576
localization?: LocalizationResource;
570577
polling?: boolean;
@@ -879,11 +886,13 @@ export type UserButtonProps = UserButtonProfileMode & {
879886
defaultOpen?: boolean;
880887
/**
881888
* Full URL or path to navigate after sign out is complete
889+
* @deprecated Configure `afterSignOutUrl` as a global configuration, either in <ClerkProvider/> or in await Clerk.load()
882890
*/
883891
afterSignOutUrl?: string;
884892
/**
885893
* Full URL or path to navigate after signing out the current user is complete.
886894
* This option applies to multi-session applications.
895+
* @deprecated Configure `afterMultiSessionSingleSignOutUrl` as a global configuration, either in <ClerkProvider/> or in await Clerk.load()
887896
*/
888897
afterMultiSessionSingleSignOutUrl?: string;
889898
/**

packages/types/src/redirects.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ export type AfterSignOutUrl = {
77
afterSignOutUrl?: string | null;
88
};
99

10+
export type AfterMultiSessionSingleSignOutUrl = {
11+
/**
12+
* Full URL or path to navigate after signing out the current user is complete.
13+
* This option applies to multi-session applications.
14+
*/
15+
afterMultiSessionSingleSignOutUrl?: string | null;
16+
};
17+
1018
/**
1119
* @deprecated This is deprecated and will be removed in a future release.
1220
*/

0 commit comments

Comments
 (0)