Skip to content

Account Unlinking #107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions Thirdweb.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ Task<List<LinkedAccount>> LinkAccount(
string payload = null
);

/// <summary>
/// Unlinks an account (auth method) from the current wallet.
/// </summary>
/// <param name="accountToUnlink">The linked account to unlink. Same type returned by <see cref="GetLinkedAccounts"/>.</param>
Task<List<LinkedAccount>> UnlinkAccount(LinkedAccount accountToUnlink);

/// <summary>
/// Returns a list of linked accounts to the current wallet.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,36 @@ public string GenerateExternalLoginLink(string redirectUrl)

#region Account Linking

public async Task<List<LinkedAccount>> 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<LinkedAccount>();
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<List<LinkedAccount>> LinkAccount(
IThirdwebWallet walletToLink,
string otp = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Thirdweb.EWS;

internal abstract class ServerBase
{
internal abstract Task<List<Server.LinkedAccount>> UnlinkAccountAsync(string currentAccountToken, Server.LinkedAccount linkedAccount);
internal abstract Task<List<Server.LinkedAccount>> LinkAccountAsync(string currentAccountToken, string authTokenToConnect);
internal abstract Task<List<Server.LinkedAccount>> GetLinkedAccountsAsync(string currentAccountToken);

Expand Down Expand Up @@ -50,6 +51,21 @@ internal Server(ThirdwebClient client, IThirdwebHttpClient httpClient)
this._httpClient = httpClient;
}

// account/disconnect
internal override async Task<List<LinkedAccount>> 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<AccountConnectResponse>(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<List<LinkedAccount>> LinkAccountAsync(string currentAccountToken, string authTokenToConnect)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

internal partial class EmbeddedWallet
{
public async Task<List<Server.LinkedAccount>> 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<List<Server.LinkedAccount>> LinkAccountAsync(string currentAccountToken, string authTokenToConnect)
{
return await this._server.LinkAccountAsync(currentAccountToken, authTokenToConnect).ConfigureAwait(false);
Expand Down
10 changes: 10 additions & 0 deletions Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,24 @@ public enum AuthProvider
/// </summary>
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; }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,5 +380,10 @@ public virtual Task<List<LinkedAccount>> GetLinkedAccounts()
throw new InvalidOperationException("GetLinkedAccounts is not supported for private key wallets.");
}

public Task<List<LinkedAccount>> UnlinkAccount(LinkedAccount accountToUnlink)
{
throw new InvalidOperationException("UnlinkAccount is not supported for private key wallets.");
}

#endregion
}
10 changes: 10 additions & 0 deletions Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,16 @@ public Task Disconnect()
return Task.CompletedTask;
}

public async Task<List<LinkedAccount>> 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<List<LinkedAccount>> LinkAccount(
IThirdwebWallet walletToLink,
string otp = null,
Expand Down
Loading