Skip to content

Commit 4e1bb2b

Browse files
authored
Add ability for user to delete themselves if permission present (#1307)
feat(clerk-js): Add delete self feature for users - When permissions are present, users will see a section in their settings allowing them to delete their account. - `createOrganizationEnabled` property was also added to users in anticipation of a feature allowing permission-gated org creation that should be landing in the next couple days. - When a user's account is deleted, they are signed out and nav to the home page.
1 parent 9651658 commit 4e1bb2b

File tree

16 files changed

+349
-45
lines changed

16 files changed

+349
-45
lines changed

.changeset/perfect-zebras-cover.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/localizations': minor
3+
'@clerk/clerk-js': minor
4+
'@clerk/types': minor
5+
---
6+
7+
Adds the ability for users to delete their own accounts, as long as they have permission to do so

package-lock.json

Lines changed: 43 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/clerk-js/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
"start": "echo \"Noop\"",
3838
"test": "jest",
3939
"test:ci": "jest --maxWorkers=50%",
40-
"test:coverage": "jest --collectCoverage && open coverage/lcov-report/index.html"
40+
"test:coverage": "jest --collectCoverage && open coverage/lcov-report/index.html",
41+
"watch": "webpack --config webpack.config.js --env production --watch"
4142
},
4243
"dependencies": {
4344
"@clerk/localizations": "^1.18.1",

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ export class User extends BaseResource implements UserResource {
224224
});
225225
};
226226

227+
delete = (): Promise<void> => {
228+
return this._baseDelete({ path: '/me' });
229+
};
230+
227231
getSessions = async (): Promise<SessionWithActivities[]> => {
228232
if (this.cachedSessionsWithActivities) {
229233
return this.cachedSessionsWithActivities;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type {
22
Attributes,
3+
Actions,
34
OAuthProviders,
45
OAuthStrategy,
56
PasswordSettingsData,
@@ -29,6 +30,7 @@ export class UserSettings extends BaseResource implements UserSettingsResource {
2930
saml!: SamlSettings;
3031

3132
attributes!: Attributes;
33+
actions!: Actions;
3234
signIn!: SignInData;
3335
signUp!: SignUpData;
3436
passwordSettings!: PasswordSettingsData;
@@ -65,6 +67,7 @@ export class UserSettings extends BaseResource implements UserSettingsResource {
6567
this.attributes = Object.fromEntries(
6668
Object.entries(data.attributes).map(a => [a[0], { ...a[1], name: a[0] }]),
6769
) as Attributes;
70+
this.actions = data.actions;
6871
this.signIn = data.sign_in;
6972
this.signUp = data.sign_up;
7073
this.passwordSettings = {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useEnvironment, useCoreUser, useCoreClerk } from '../../contexts';
2+
import { localizationKeys, Text } from '../../customizables';
3+
import { ContentPage, Form, FormButtons, useCardState, withCardStateProvider } from '../../elements';
4+
import { handleError } from '../../utils';
5+
import { UserProfileBreadcrumbs } from './UserProfileNavbar';
6+
import { useRouter } from '../../router';
7+
8+
export const DeletePage = withCardStateProvider(() => {
9+
const card = useCardState();
10+
const environment = useEnvironment();
11+
const router = useRouter();
12+
const user = useCoreUser();
13+
const clerk = useCoreClerk();
14+
15+
const deleteUser = async () => {
16+
try {
17+
await user.delete();
18+
if (clerk.client.activeSessions.length > 0) {
19+
await router.navigate(environment.displayConfig.afterSignOutOneUrl);
20+
} else {
21+
await router.navigate(environment.displayConfig.afterSignOutAllUrl);
22+
}
23+
} catch (e) {
24+
handleError(e, [], card.setError);
25+
}
26+
};
27+
28+
return (
29+
<ContentPage
30+
headerTitle={localizationKeys('userProfile.deletePage.title')}
31+
Breadcrumbs={UserProfileBreadcrumbs}
32+
>
33+
<Form.Root onSubmit={deleteUser}>
34+
<Text localizationKey={localizationKeys('userProfile.deletePage.description')} />
35+
<FormButtons
36+
submitLabel={localizationKeys('userProfile.deletePage.confirm')}
37+
colorScheme='danger'
38+
/>
39+
</Form.Root>
40+
</ContentPage>
41+
);
42+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { localizationKeys, Flex, Text, Col, Button } from '../../customizables';
2+
import { ProfileSection } from '../../elements';
3+
import { useRouter } from '../../router';
4+
5+
export const DeleteSection = () => {
6+
const { navigate } = useRouter();
7+
8+
return (
9+
<ProfileSection
10+
title={localizationKeys('userProfile.start.dangerSection.title')}
11+
id='danger'
12+
>
13+
<Flex
14+
justify='between'
15+
sx={t => ({ marginTop: t.space.$2, marginLeft: t.space.$6 })}
16+
>
17+
<Col gap={1}>
18+
<Text
19+
variant='regularMedium'
20+
localizationKey={localizationKeys('userProfile.start.dangerSection.deleteAccountTitle')}
21+
/>
22+
<Text
23+
variant='smallRegular'
24+
colorScheme='neutral'
25+
localizationKey={localizationKeys('userProfile.start.dangerSection.deleteAccountDescription')}
26+
/>
27+
</Col>
28+
<Button
29+
aria-label='Delete account'
30+
colorScheme='danger'
31+
textVariant='buttonExtraSmallBold'
32+
onClick={() => navigate(`delete`)}
33+
localizationKey={localizationKeys('userProfile.start.dangerSection.deleteAccountButton')}
34+
/>
35+
</Flex>
36+
</ProfileSection>
37+
);
38+
};

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import { UsernameSection } from './UsernameSection';
1313
import { UserProfileSection } from './UserProfileSection';
1414
import { getSecondFactors } from './utils';
1515
import { Web3Section } from './Web3Section';
16+
import { DeleteSection } from './DeleteSection';
1617

1718
export const RootPage = withCardStateProvider(() => {
18-
const { attributes, saml, social, instanceIsPasswordBased } = useEnvironment().userSettings;
19+
const { attributes, saml, social, actions, instanceIsPasswordBased } = useEnvironment().userSettings;
1920
const card = useCardState();
2021
const user = useCoreUser();
2122
const showUsername = attributes.username.enabled;
@@ -26,6 +27,7 @@ export const RootPage = withCardStateProvider(() => {
2627
const showWeb3 = attributes.web3_wallet.enabled;
2728
const showPassword = instanceIsPasswordBased;
2829
const showMfa = getSecondFactors(attributes).length > 0;
30+
const showDelete = actions.delete_self;
2931

3032
return (
3133
<Col
@@ -54,6 +56,7 @@ export const RootPage = withCardStateProvider(() => {
5456
{showConnectedAccounts && <ConnectedAccountsSection />}
5557
{showSamlAccounts && <EnterpriseAccountsSection />}
5658
{showWeb3 && <Web3Section />}
59+
{showDelete && <DeleteSection />}
5760
</Col>
5861
<Col
5962
elementDescriptor={descriptors.profilePage}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import { RootPage } from './RootPage';
2020
import { UsernamePage } from './UsernamePage';
2121
import { Web3Page } from './Web3Page';
22+
import { DeletePage } from './DeletePage';
2223

2324
export const UserProfileRoutes = (props: PropsOfComponent<typeof ProfileCardContent>) => {
2425
return (
@@ -102,6 +103,9 @@ export const UserProfileRoutes = (props: PropsOfComponent<typeof ProfileCardCont
102103
<PasswordPage />
103104
</Route>
104105
{/*</Route>*/}
106+
<Route path='delete'>
107+
<DeletePage />
108+
</Route>
105109
</ProfileCardContent>
106110
);
107111
};

0 commit comments

Comments
 (0)