Skip to content

Commit

Permalink
Backend Wallet Auth (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xFirekeeper authored Jan 9, 2025
1 parent 0891058 commit e631a5d
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 16 deletions.
12 changes: 12 additions & 0 deletions Thirdweb.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,18 @@

#endregion

#region Backend Wallet Auth

// var inAppWalletBackend = await InAppWallet.Create(client: client, authProvider: AuthProvider.Backend, walletSecret: "very-secret");
// if (!await inAppWalletBackend.IsConnected())
// {
// _ = await inAppWalletBackend.LoginWithBackend();
// }
// var inAppWalletBackendAddress = await inAppWalletBackend.GetAddress();
// Console.WriteLine($"InAppWallet Backend address: {inAppWalletBackendAddress}");

#endregion

#region Account Linking

// var inAppWalletMain = await InAppWallet.Create(client: client, authProvider: AuthProvider.Telegram);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public partial class EcosystemWallet : IThirdwebWallet
internal readonly string PhoneNumber;
internal readonly string AuthProvider;
internal readonly string LegacyEncryptionKey;
internal readonly string WalletSecret;

internal string Address;

Expand All @@ -46,7 +47,8 @@ internal EcosystemWallet(
string phoneNumber,
string authProvider,
IThirdwebWallet siweSigner,
string legacyEncryptionKey
string legacyEncryptionKey,
string walletSecret
)
{
this.Client = client;
Expand All @@ -59,6 +61,7 @@ string legacyEncryptionKey
this.PhoneNumber = phoneNumber;
this.AuthProvider = authProvider;
this.SiweSigner = siweSigner;
this.WalletSecret = walletSecret;
}

#region Creation
Expand All @@ -75,6 +78,7 @@ string legacyEncryptionKey
/// <param name="storageDirectoryPath">The path to the storage directory.</param>
/// <param name="siweSigner">The SIWE signer wallet for SIWE authentication.</param>
/// <param name="legacyEncryptionKey">The encryption key that is no longer required but was used in the past. Only pass this if you had used custom auth before this was deprecated.</param>
/// <param name="walletSecret">The wallet secret for Backend authentication.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the created in-app wallet.</returns>
/// <exception cref="ArgumentException">Thrown when required parameters are not provided.</exception>
public static async Task<EcosystemWallet> Create(
Expand All @@ -86,7 +90,8 @@ public static async Task<EcosystemWallet> Create(
AuthProvider authProvider = Thirdweb.AuthProvider.Default,
string storageDirectoryPath = null,
IThirdwebWallet siweSigner = null,
string legacyEncryptionKey = null
string legacyEncryptionKey = null,
string walletSecret = null
)
{
if (client == null)
Expand Down Expand Up @@ -117,6 +122,7 @@ public static async Task<EcosystemWallet> Create(
Thirdweb.AuthProvider.Github => "Github",
Thirdweb.AuthProvider.Twitch => "Twitch",
Thirdweb.AuthProvider.Steam => "Steam",
Thirdweb.AuthProvider.Backend => "Backend",
Thirdweb.AuthProvider.Default => string.IsNullOrEmpty(email) ? "Phone" : "Email",
_ => throw new ArgumentException("Invalid AuthProvider"),
};
Expand Down Expand Up @@ -150,15 +156,15 @@ public static async Task<EcosystemWallet> Create(
try
{
var userAddress = await ResumeEnclaveSession(enclaveHttpClient, embeddedWallet, email, phoneNumber, authproviderStr).ConfigureAwait(false);
return new EcosystemWallet(ecosystemId, ecosystemPartnerId, client, embeddedWallet, enclaveHttpClient, email, phoneNumber, authproviderStr, siweSigner, legacyEncryptionKey)
return new EcosystemWallet(ecosystemId, ecosystemPartnerId, client, embeddedWallet, enclaveHttpClient, email, phoneNumber, authproviderStr, siweSigner, legacyEncryptionKey, walletSecret)
{
Address = userAddress
};
}
catch
{
enclaveHttpClient.RemoveHeader("Authorization");
return new EcosystemWallet(ecosystemId, ecosystemPartnerId, client, embeddedWallet, enclaveHttpClient, email, phoneNumber, authproviderStr, siweSigner, legacyEncryptionKey)
return new EcosystemWallet(ecosystemId, ecosystemPartnerId, client, embeddedWallet, enclaveHttpClient, email, phoneNumber, authproviderStr, siweSigner, legacyEncryptionKey, walletSecret)
{
Address = null
};
Expand Down Expand Up @@ -468,6 +474,13 @@ public async Task<List<LinkedAccount>> LinkAccount(
}
serverRes = await ecosystemWallet.PreAuth_Siwe(ecosystemWallet.SiweSigner, chainId.Value).ConfigureAwait(false);
break;
case "Backend":
if (string.IsNullOrEmpty(ecosystemWallet.WalletSecret))
{
throw new ArgumentException("Cannot link account with a Backend wallet without a wallet secret.");
}
serverRes = await ecosystemWallet.PreAuth_Backend(ecosystemWallet.WalletSecret).ConfigureAwait(false);
break;
case "JWT":
if (string.IsNullOrEmpty(jwt))
{
Expand Down Expand Up @@ -692,12 +705,12 @@ public async Task<string> LoginWithOauth(

private async Task<Server.VerifyResult> PreAuth_Siwe(IThirdwebWallet siweSigner, BigInteger chainId)
{
if (this.SiweSigner == null)
if (siweSigner == null)
{
throw new ArgumentNullException(nameof(siweSigner), "SIWE Signer wallet cannot be null.");
}

if (!await this.SiweSigner.IsConnected().ConfigureAwait(false))
if (!await siweSigner.IsConnected().ConfigureAwait(false))
{
throw new InvalidOperationException("SIWE Signer wallet must be connected as this operation requires it to sign a message.");
}
Expand All @@ -716,6 +729,23 @@ public async Task<string> LoginWithSiwe(BigInteger chainId)

#endregion

#region Backend

private async Task<Server.VerifyResult> PreAuth_Backend(string walletSecret)
{
return string.IsNullOrEmpty(walletSecret)
? throw new ArgumentException("Wallet secret cannot be null or empty.", nameof(walletSecret))
: await this.EmbeddedWallet.SignInWithBackendAsync(walletSecret).ConfigureAwait(false);
}

public async Task<string> LoginWithBackend()
{
var serverRes = await this.PreAuth_Backend(this.WalletSecret).ConfigureAwait(false);
return await this.PostAuth(serverRes).ConfigureAwait(false);
}

#endregion

#region Guest

private async Task<Server.VerifyResult> PreAuth_Guest()
Expand Down Expand Up @@ -746,13 +776,12 @@ public async Task<string> LoginWithGuest()

private async Task<Server.VerifyResult> PreAuth_JWT(string jwt)
{
return string.IsNullOrEmpty(jwt) ? throw new ArgumentException(nameof(jwt), "JWT cannot be null or empty.") : await this.EmbeddedWallet.SignInWithJwtAsync(jwt).ConfigureAwait(false);
return string.IsNullOrEmpty(jwt) ? throw new ArgumentException("JWT cannot be null or empty.", nameof(jwt)) : await this.EmbeddedWallet.SignInWithJwtAsync(jwt).ConfigureAwait(false);
}

public async Task<string> LoginWithJWT(string jwt)
{
var serverRes = string.IsNullOrEmpty(jwt) ? throw new ArgumentException("JWT cannot be null or empty.", nameof(jwt)) : await this.EmbeddedWallet.SignInWithJwtAsync(jwt).ConfigureAwait(false);

var serverRes = await this.PreAuth_JWT(jwt).ConfigureAwait(false);
return await this.PostAuth(serverRes).ConfigureAwait(false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ internal abstract class ServerBase
internal abstract Task<LoginPayloadData> FetchSiwePayloadAsync(string address, string chainId);
internal abstract Task<Server.VerifyResult> VerifySiweAsync(LoginPayloadData payload, string signature);

internal abstract Task<Server.VerifyResult> VerifyBackendAsync(string walletSecret);

internal abstract Task<Server.VerifyResult> VerifyGuestAsync(string sessionId);

internal abstract Task<string> SendEmailOtpAsync(string emailAddress);
Expand Down Expand Up @@ -155,8 +157,19 @@ internal override async Task<VerifyResult> VerifySiweAsync(LoginPayloadData payl
return await this.InvokeAuthResultLambdaAsync(authResult).ConfigureAwait(false);
}

// login/guest
// login/backend
internal override async Task<VerifyResult> VerifyBackendAsync(string walletSecret)
{
var uri = MakeUri2024("/login/backend");
var content = MakeHttpContent(new { walletSecret });
var response = await this._httpClient.PostAsync(uri.ToString(), content).ConfigureAwait(false);
await CheckStatusCodeAsync(response).ConfigureAwait(false);

var authResult = await DeserializeAsync<AuthResultType>(response).ConfigureAwait(false);
return await this.InvokeAuthResultLambdaAsync(authResult).ConfigureAwait(false);
}

// login/guest
internal override async Task<VerifyResult> VerifyGuestAsync(string sessionId)
{
var uri = MakeUri2024("/login/guest/callback");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Thirdweb.EWS;

internal partial class EmbeddedWallet
{
public async Task<Server.VerifyResult> SignInWithBackendAsync(string walletSecret)
{
return await this._server.VerifyBackendAsync(walletSecret).ConfigureAwait(false);
}
}
3 changes: 2 additions & 1 deletion Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public enum AuthProvider
Coinbase,
Github,
Twitch,
Steam
Steam,
Backend
}

/// <summary>
Expand Down
14 changes: 9 additions & 5 deletions Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ internal InAppWallet(
string authProvider,
IThirdwebWallet siweSigner,
string address,
string legacyEncryptionKey
string legacyEncryptionKey,
string walletSecret
)
: base(null, null, client, embeddedWallet, httpClient, email, phoneNumber, authProvider, siweSigner, legacyEncryptionKey)
: base(null, null, client, embeddedWallet, httpClient, email, phoneNumber, authProvider, siweSigner, legacyEncryptionKey, walletSecret)
{
this.Address = address;
}
Expand All @@ -33,6 +34,7 @@ string legacyEncryptionKey
/// <param name="storageDirectoryPath">The path to the storage directory.</param>
/// <param name="siweSigner">The SIWE signer wallet for SIWE authentication.</param>
/// <param name="legacyEncryptionKey">The encryption key that is no longer required but was used in the past. Only pass this if you had used custom auth before this was deprecated.</param>
/// <param name="walletSecret">The wallet secret for backend authentication.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the created in-app wallet.</returns>
/// <exception cref="ArgumentException">Thrown when required parameters are not provided.</exception>
public static async Task<InAppWallet> Create(
Expand All @@ -42,11 +44,12 @@ public static async Task<InAppWallet> Create(
AuthProvider authProvider = Thirdweb.AuthProvider.Default,
string storageDirectoryPath = null,
IThirdwebWallet siweSigner = null,
string legacyEncryptionKey = null
string legacyEncryptionKey = null,
string walletSecret = null
)
{
storageDirectoryPath ??= Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Thirdweb", "InAppWallet");
var ecoWallet = await Create(client, null, null, email, phoneNumber, authProvider, storageDirectoryPath, siweSigner, legacyEncryptionKey);
var ecoWallet = await Create(client, null, null, email, phoneNumber, authProvider, storageDirectoryPath, siweSigner, legacyEncryptionKey, walletSecret);
return new InAppWallet(
ecoWallet.Client,
ecoWallet.EmbeddedWallet,
Expand All @@ -56,7 +59,8 @@ public static async Task<InAppWallet> Create(
ecoWallet.AuthProvider,
ecoWallet.SiweSigner,
ecoWallet.Address,
ecoWallet.LegacyEncryptionKey
ecoWallet.LegacyEncryptionKey,
ecoWallet.WalletSecret
);
}
}

0 comments on commit e631a5d

Please sign in to comment.