diff --git a/Thirdweb.Console/Program.cs b/Thirdweb.Console/Program.cs index c3f195de..e8d52ab8 100644 --- a/Thirdweb.Console/Program.cs +++ b/Thirdweb.Console/Program.cs @@ -241,7 +241,7 @@ #region Account Linking -// var inAppWalletMain = await InAppWallet.Create(client: client, authProvider: AuthProvider.Google); +// var inAppWalletMain = await InAppWallet.Create(client: client, authProvider: AuthProvider.Telegram); // if (!await inAppWalletMain.IsConnected()) // { // _ = await inAppWalletMain.LoginWithOauth( @@ -260,12 +260,15 @@ // var oldLinkedAccounts = await inAppWalletMain.GetLinkedAccounts(); // Console.WriteLine($"Old linked accounts: {JsonConvert.SerializeObject(oldLinkedAccounts, Formatting.Indented)}"); -// var inAppWalletToLink = await InAppWallet.Create(client: client, authProvider: AuthProvider.Guest); -// _ = await inAppWalletMain.LinkAccount(walletToLink: inAppWalletToLink); - -// var linkedAccounts = await inAppWalletMain.GetLinkedAccounts(); +// // External wallet variant +// var externalWallet = await PrivateKeyWallet.Generate(client: client); +// var inAppWalletToLink = await InAppWallet.Create(client: client, authProvider: AuthProvider.Siwe, siweSigner: externalWallet); +// var linkedAccounts = await inAppWalletMain.LinkAccount(walletToLink: inAppWalletToLink, chainId: 421614); // Console.WriteLine($"Linked accounts: {JsonConvert.SerializeObject(linkedAccounts, Formatting.Indented)}"); +// var unlinkingResult = await inAppWalletMain.UnlinkAccount(linkedAccounts.First(linkedAccounts => linkedAccounts.Type == "siwe")); +// Console.WriteLine($"Unlinking result: {JsonConvert.SerializeObject(unlinkingResult, Formatting.Indented)}"); + #endregion #region Smart Wallet - Authenticate diff --git a/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs b/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs index 3c5879bd..164d4b1d 100644 --- a/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs @@ -155,6 +155,12 @@ Task> LinkAccount( string payload = null ); + /// + /// Unlinks an account (auth method) from the current wallet. + /// + /// The linked account to unlink. Same type returned by . + Task> UnlinkAccount(LinkedAccount accountToUnlink); + /// /// Returns a list of linked accounts to the current wallet. /// diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs index d970763a..8a2231e4 100644 --- a/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs @@ -332,6 +332,36 @@ public string GenerateExternalLoginLink(string redirectUrl) #region Account Linking + public async Task> UnlinkAccount(LinkedAccount accountToUnlink) + { + if (!await this.IsConnected().ConfigureAwait(false)) + { + throw new InvalidOperationException("Cannot unlink account with a wallet that is not connected. Please login to the wallet before unlinking other wallets."); + } + + var currentAccountToken = this.EmbeddedWallet.GetSessionData()?.AuthToken; + + var serverLinkedAccounts = await this.EmbeddedWallet.UnlinkAccountAsync(currentAccountToken, accountToUnlink).ConfigureAwait(false); + var linkedAccounts = new List(); + foreach (var linkedAccount in serverLinkedAccounts) + { + linkedAccounts.Add( + new LinkedAccount + { + Type = linkedAccount.Type, + Details = new LinkedAccount.LinkedAccountDetails + { + Email = linkedAccount.Details?.Email, + Address = linkedAccount.Details?.Address, + Phone = linkedAccount.Details?.Phone, + Id = linkedAccount.Details?.Id + } + } + ); + } + return linkedAccounts; + } + public async Task> LinkAccount( IThirdwebWallet walletToLink, string otp = null, diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet.Authentication/Server.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet.Authentication/Server.cs index 680c9141..a396ff15 100644 --- a/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet.Authentication/Server.cs +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet.Authentication/Server.cs @@ -6,6 +6,7 @@ namespace Thirdweb.EWS; internal abstract class ServerBase { + internal abstract Task> UnlinkAccountAsync(string currentAccountToken, Server.LinkedAccount linkedAccount); internal abstract Task> LinkAccountAsync(string currentAccountToken, string authTokenToConnect); internal abstract Task> GetLinkedAccountsAsync(string currentAccountToken); @@ -50,6 +51,21 @@ internal Server(ThirdwebClient client, IThirdwebHttpClient httpClient) this._httpClient = httpClient; } + // account/disconnect + internal override async Task> UnlinkAccountAsync(string currentAccountToken, LinkedAccount linkedAccount) + { + var uri = MakeUri2024("/account/disconnect"); + var request = new HttpRequestMessage(HttpMethod.Post, uri) + { + Content = MakeHttpContent(linkedAccount) + }; + var response = await this.SendHttpWithAuthAsync(request, currentAccountToken).ConfigureAwait(false); + await CheckStatusCodeAsync(response).ConfigureAwait(false); + + var res = await DeserializeAsync(response).ConfigureAwait(false); + return res == null || res.LinkedAccounts == null || res.LinkedAccounts.Count == 0 ? throw new InvalidOperationException("No linked accounts returned") : res.LinkedAccounts; + } + // account/connect internal override async Task> LinkAccountAsync(string currentAccountToken, string authTokenToConnect) { diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.AccountLinking.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.AccountLinking.cs index 2990bb9d..0e1335aa 100644 --- a/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.AccountLinking.cs +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.AccountLinking.cs @@ -2,6 +2,22 @@ internal partial class EmbeddedWallet { + public async Task> UnlinkAccountAsync(string currentAccountToken, LinkedAccount linkedAccount) + { + var serverLinkedAccount = new Server.LinkedAccount + { + Type = linkedAccount.Type, + Details = new Server.LinkedAccount.LinkedAccountDetails + { + Email = linkedAccount.Details.Email, + Address = linkedAccount.Details.Address, + Phone = linkedAccount.Details.Phone, + Id = linkedAccount.Details.Id + } + }; + return await this._server.UnlinkAccountAsync(currentAccountToken, serverLinkedAccount).ConfigureAwait(false); + } + public async Task> LinkAccountAsync(string currentAccountToken, string authTokenToConnect) { return await this._server.LinkAccountAsync(currentAccountToken, authTokenToConnect).ConfigureAwait(false); diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs index ca022877..4b4056b2 100644 --- a/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs @@ -31,14 +31,24 @@ public enum AuthProvider /// public struct LinkedAccount { + [JsonProperty("type")] public string Type { get; set; } + + [JsonProperty("details")] public LinkedAccountDetails Details { get; set; } public struct LinkedAccountDetails { + [JsonProperty("email")] public string Email { get; set; } + + [JsonProperty("name")] public string Address { get; set; } + + [JsonProperty("phone")] public string Phone { get; set; } + + [JsonProperty("id")] public string Id { get; set; } } diff --git a/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs b/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs index e671eb35..26d90a48 100644 --- a/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs @@ -380,5 +380,10 @@ public virtual Task> GetLinkedAccounts() throw new InvalidOperationException("GetLinkedAccounts is not supported for private key wallets."); } + public Task> UnlinkAccount(LinkedAccount accountToUnlink) + { + throw new InvalidOperationException("UnlinkAccount is not supported for private key wallets."); + } + #endregion } diff --git a/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs b/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs index 6e52b157..68548c8f 100644 --- a/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs @@ -1167,6 +1167,16 @@ public Task Disconnect() return Task.CompletedTask; } + public async Task> UnlinkAccount(LinkedAccount accountToUnlink) + { + var personalWallet = await this.GetPersonalWallet().ConfigureAwait(false); + if (personalWallet is not InAppWallet and not EcosystemWallet) + { + throw new Exception("SmartWallet.UnlinkAccount is only supported if the signer is an InAppWallet or EcosystemWallet"); + } + return await personalWallet.UnlinkAccount(accountToUnlink).ConfigureAwait(false); + } + public async Task> LinkAccount( IThirdwebWallet walletToLink, string otp = null,