Skip to content

Commit 31c4bb2

Browse files
[SDK] Add account deletion support when unlinking profiles (#7211)
1 parent 3b1e16c commit 31c4bb2

File tree

13 files changed

+196
-9
lines changed

13 files changed

+196
-9
lines changed

.changeset/account-deletion-unlink.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
**Add account deletion support when unlinking profiles**
6+
7+
Added optional `allowAccountDeletion` parameter to `useUnlinkProfile` hook and `unlinkProfile` function. When set to `true`, this allows deleting the entire account when unlinking the last profile associated with it.
8+
9+
**React Hook Example:**
10+
11+
```tsx
12+
import { useUnlinkProfile } from "thirdweb/react";
13+
14+
const { mutate: unlinkProfile } = useUnlinkProfile();
15+
16+
const handleUnlink = () => {
17+
unlinkProfile({
18+
client,
19+
profileToUnlink: connectedProfiles[0],
20+
allowAccountDeletion: true, // Delete account if last profile
21+
});
22+
};
23+
```
24+
25+
**Direct Function Example:**
26+
27+
```ts
28+
import { unlinkProfile } from "thirdweb/wallets/in-app";
29+
30+
const updatedProfiles = await unlinkProfile({
31+
client,
32+
profileToUnlink: profiles[0],
33+
allowAccountDeletion: true, // Delete account if last profile
34+
});
35+
```

packages/thirdweb/src/react/native/hooks/wallets/useUnlinkProfile.test.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,32 @@ describe("useUnlinkProfile", () => {
4040
client: TEST_CLIENT,
4141
ecosystem: undefined,
4242
profileToUnlink: mockProfile,
43+
allowAccountDeletion: false,
44+
});
45+
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
46+
queryKey: ["profiles"],
47+
});
48+
});
49+
50+
it("should call unlinkProfile with allowAccountDeletion if true", async () => {
51+
const { result } = renderHook(() => useUnlinkProfile(), {
52+
wrapper,
53+
});
54+
const mutationFn = result.current.mutateAsync;
55+
56+
await act(async () => {
57+
await mutationFn({
58+
client: TEST_CLIENT,
59+
profileToUnlink: mockProfile,
60+
allowAccountDeletion: true,
61+
});
62+
});
63+
64+
expect(unlinkProfile).toHaveBeenCalledWith({
65+
client: TEST_CLIENT,
66+
ecosystem: undefined,
67+
profileToUnlink: mockProfile,
68+
allowAccountDeletion: true,
4369
});
4470
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
4571
queryKey: ["profiles"],
@@ -70,6 +96,7 @@ describe("useUnlinkProfile", () => {
7096
?.partnerId,
7197
},
7298
profileToUnlink: mockProfile,
99+
allowAccountDeletion: false,
73100
});
74101
});
75102
});

packages/thirdweb/src/react/native/hooks/wallets/useUnlinkProfile.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ import { useConnectedWallets } from "../../../core/hooks/wallets/useConnectedWal
3131
* };
3232
* ```
3333
*
34+
* ### Unlinking an email account with account deletion
35+
*
36+
* ```jsx
37+
* import { useUnlinkProfile } from "thirdweb/react";
38+
*
39+
* const { mutate: unlinkProfile } = useUnlinkProfile();
40+
*
41+
* const onClick = () => {
42+
* unlinkProfile({
43+
* client,
44+
* // Select the profile you want to unlink
45+
* profileToUnlink: connectedProfiles[0],
46+
* allowAccountDeletion: true, // This will delete the account if it's the last profile linked to the account
47+
* });
48+
* };
49+
* ```
50+
*
3451
* @wallet
3552
*/
3653
export function useUnlinkProfile() {
@@ -40,7 +57,12 @@ export function useUnlinkProfile() {
4057
mutationFn: async ({
4158
client,
4259
profileToUnlink,
43-
}: { client: ThirdwebClient; profileToUnlink: Profile }) => {
60+
allowAccountDeletion = false,
61+
}: {
62+
client: ThirdwebClient;
63+
profileToUnlink: Profile;
64+
allowAccountDeletion?: boolean;
65+
}) => {
4466
const ecosystemWallet = wallets.find((w) => isEcosystemWallet(w));
4567
const ecosystem: Ecosystem | undefined = ecosystemWallet
4668
? {
@@ -53,6 +75,7 @@ export function useUnlinkProfile() {
5375
client,
5476
ecosystem,
5577
profileToUnlink,
78+
allowAccountDeletion,
5679
});
5780
},
5881
onSuccess: () => {

packages/thirdweb/src/react/web/hooks/wallets/useUnlinkProfile.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ describe("useUnlinkProfile", () => {
4040
client: TEST_CLIENT,
4141
ecosystem: undefined,
4242
profileToUnlink: mockProfile,
43+
allowAccountDeletion: false,
4344
});
4445
expect(queryClient.invalidateQueries).toHaveBeenCalledWith({
4546
queryKey: ["profiles"],
@@ -70,6 +71,7 @@ describe("useUnlinkProfile", () => {
7071
?.partnerId,
7172
},
7273
profileToUnlink: mockProfile,
74+
allowAccountDeletion: false,
7375
});
7476
});
7577
});

packages/thirdweb/src/react/web/hooks/wallets/useUnlinkProfile.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ import { useConnectedWallets } from "../../../core/hooks/wallets/useConnectedWal
3131
* };
3232
* ```
3333
*
34+
* ### Unlinking an email account with account deletion
35+
*
36+
* ```jsx
37+
* import { useUnlinkProfile } from "thirdweb/react";
38+
*
39+
* const { mutate: unlinkProfile } = useUnlinkProfile();
40+
*
41+
* const onClick = () => {
42+
* unlinkProfile({
43+
* client,
44+
* // Select the profile you want to unlink
45+
* profileToUnlink: connectedProfiles[0],
46+
* allowAccountDeletion: true, // This will delete the account if it's the last profile linked to the account
47+
* });
48+
* };
49+
* ```
50+
*
3451
* @wallet
3552
*/
3653
export function useUnlinkProfile() {
@@ -40,7 +57,12 @@ export function useUnlinkProfile() {
4057
mutationFn: async ({
4158
client,
4259
profileToUnlink,
43-
}: { client: ThirdwebClient; profileToUnlink: Profile }) => {
60+
allowAccountDeletion = false,
61+
}: {
62+
client: ThirdwebClient;
63+
profileToUnlink: Profile;
64+
allowAccountDeletion?: boolean;
65+
}) => {
4466
const ecosystemWallet = wallets.find((w) => isEcosystemWallet(w));
4567
const ecosystem: Ecosystem | undefined = ecosystemWallet
4668
? {
@@ -53,6 +75,7 @@ export function useUnlinkProfile() {
5375
client,
5476
ecosystem,
5577
profileToUnlink,
78+
allowAccountDeletion,
5679
});
5780
},
5881
onSuccess: () => {

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,37 @@ describe("Account linking functions", () => {
9090
Authorization: "Bearer iaw-auth-token:mock-token",
9191
"Content-Type": "application/json",
9292
},
93-
body: JSON.stringify(profileToUnlink),
93+
body: JSON.stringify({
94+
type: profileToUnlink.type,
95+
details: profileToUnlink.details,
96+
allowAccountDeletion: false,
97+
}),
98+
},
99+
);
100+
expect(result).toEqual(mockLinkedAccounts);
101+
});
102+
103+
it("should successfully unlink an account with allowAccountDeletion", async () => {
104+
const result = await unlinkAccount({
105+
client: mockClient,
106+
profileToUnlink,
107+
storage: mockStorage,
108+
allowAccountDeletion: true,
109+
});
110+
111+
expect(mockFetch).toHaveBeenCalledWith(
112+
"https://embedded-wallet.thirdweb.com/api/2024-05-05/account/disconnect",
113+
{
114+
method: "POST",
115+
headers: {
116+
Authorization: "Bearer iaw-auth-token:mock-token",
117+
"Content-Type": "application/json",
118+
},
119+
body: JSON.stringify({
120+
type: profileToUnlink.type,
121+
details: profileToUnlink.details,
122+
allowAccountDeletion: true,
123+
}),
94124
},
95125
);
96126
expect(result).toEqual(mockLinkedAccounts);

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,13 @@ export async function unlinkAccount({
6666
client,
6767
ecosystem,
6868
profileToUnlink,
69+
allowAccountDeletion = false,
6970
storage,
7071
}: {
7172
client: ThirdwebClient;
7273
ecosystem?: Ecosystem;
7374
profileToUnlink: Profile;
75+
allowAccountDeletion?: boolean;
7476
storage: ClientScopedStorage;
7577
}): Promise<Profile[]> {
7678
const clientFetch = getClientFetch(client, ecosystem);
@@ -90,7 +92,11 @@ export async function unlinkAccount({
9092
{
9193
method: "POST",
9294
headers,
93-
body: stringify(profileToUnlink),
95+
body: stringify({
96+
type: profileToUnlink.type,
97+
details: profileToUnlink.details,
98+
allowAccountDeletion,
99+
}),
94100
},
95101
);
96102

packages/thirdweb/src/wallets/in-app/core/authentication/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,5 @@ export type UnlinkParams = {
259259
client: ThirdwebClient;
260260
ecosystem?: Ecosystem;
261261
profileToUnlink: Profile;
262+
allowAccountDeletion?: boolean;
262263
};

packages/thirdweb/src/wallets/in-app/core/interfaces/connector.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ export interface InAppConnector {
3737
): Promise<AuthLoginReturnType>;
3838
logout(): Promise<LogoutReturnType>;
3939
linkProfile(args: AuthArgsType): Promise<Profile[]>;
40-
unlinkProfile(args: Profile): Promise<Profile[]>;
40+
unlinkProfile(
41+
args: Profile,
42+
allowAccountDeletion?: boolean,
43+
): Promise<Profile[]>;
4144
getProfiles(): Promise<Profile[]>;
4245
storage: ClientScopedStorage;
4346
}

packages/thirdweb/src/wallets/in-app/native/auth/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,10 @@ export async function linkProfile(args: AuthArgsType) {
203203
*/
204204
export async function unlinkProfile(args: UnlinkParams) {
205205
const connector = await getInAppWalletConnector(args.client, args.ecosystem);
206-
return await connector.unlinkProfile(args.profileToUnlink);
206+
return await connector.unlinkProfile(
207+
args.profileToUnlink,
208+
args.allowAccountDeletion,
209+
);
207210
}
208211

209212
/**

0 commit comments

Comments
 (0)