diff --git a/Thirdweb.Console/Program.cs b/Thirdweb.Console/Program.cs index a9737bc..73a83e6 100644 --- a/Thirdweb.Console/Program.cs +++ b/Thirdweb.Console/Program.cs @@ -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); diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs index 8918153..622b5b5 100644 --- a/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs @@ -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; @@ -46,7 +47,8 @@ internal EcosystemWallet( string phoneNumber, string authProvider, IThirdwebWallet siweSigner, - string legacyEncryptionKey + string legacyEncryptionKey, + string walletSecret ) { this.Client = client; @@ -59,6 +61,7 @@ string legacyEncryptionKey this.PhoneNumber = phoneNumber; this.AuthProvider = authProvider; this.SiweSigner = siweSigner; + this.WalletSecret = walletSecret; } #region Creation @@ -75,6 +78,7 @@ string legacyEncryptionKey /// The path to the storage directory. /// The SIWE signer wallet for SIWE authentication. /// 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. + /// The wallet secret for Backend authentication. /// A task that represents the asynchronous operation. The task result contains the created in-app wallet. /// Thrown when required parameters are not provided. public static async Task Create( @@ -86,7 +90,8 @@ public static async Task Create( AuthProvider authProvider = Thirdweb.AuthProvider.Default, string storageDirectoryPath = null, IThirdwebWallet siweSigner = null, - string legacyEncryptionKey = null + string legacyEncryptionKey = null, + string walletSecret = null ) { if (client == null) @@ -117,6 +122,7 @@ public static async Task 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"), }; @@ -150,7 +156,7 @@ public static async Task 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 }; @@ -158,7 +164,7 @@ public static async Task Create( 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 }; @@ -468,6 +474,13 @@ public async Task> 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)) { @@ -692,12 +705,12 @@ public async Task LoginWithOauth( private async Task 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."); } @@ -716,6 +729,23 @@ public async Task LoginWithSiwe(BigInteger chainId) #endregion + #region Backend + + private async Task 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 LoginWithBackend() + { + var serverRes = await this.PreAuth_Backend(this.WalletSecret).ConfigureAwait(false); + return await this.PostAuth(serverRes).ConfigureAwait(false); + } + + #endregion + #region Guest private async Task PreAuth_Guest() @@ -746,13 +776,12 @@ public async Task LoginWithGuest() private async Task 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 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); } diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet.Authentication/Server.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet.Authentication/Server.cs index a396ff1..f4743fe 100644 --- a/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet.Authentication/Server.cs +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet.Authentication/Server.cs @@ -16,6 +16,8 @@ internal abstract class ServerBase internal abstract Task FetchSiwePayloadAsync(string address, string chainId); internal abstract Task VerifySiweAsync(LoginPayloadData payload, string signature); + internal abstract Task VerifyBackendAsync(string walletSecret); + internal abstract Task VerifyGuestAsync(string sessionId); internal abstract Task SendEmailOtpAsync(string emailAddress); @@ -155,8 +157,19 @@ internal override async Task VerifySiweAsync(LoginPayloadData payl return await this.InvokeAuthResultLambdaAsync(authResult).ConfigureAwait(false); } - // login/guest + // login/backend + internal override async Task 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(response).ConfigureAwait(false); + return await this.InvokeAuthResultLambdaAsync(authResult).ConfigureAwait(false); + } + + // login/guest internal override async Task VerifyGuestAsync(string sessionId) { var uri = MakeUri2024("/login/guest/callback"); diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.Backend.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.Backend.cs new file mode 100644 index 0000000..d31e1cd --- /dev/null +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.Backend.cs @@ -0,0 +1,9 @@ +namespace Thirdweb.EWS; + +internal partial class EmbeddedWallet +{ + public async Task SignInWithBackendAsync(string walletSecret) + { + return await this._server.VerifyBackendAsync(walletSecret).ConfigureAwait(false); + } +} diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs index 061e788..76d45d9 100644 --- a/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs @@ -23,7 +23,8 @@ public enum AuthProvider Coinbase, Github, Twitch, - Steam + Steam, + Backend } /// diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs index 8a7f8b8..168db0f 100644 --- a/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs @@ -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; } @@ -33,6 +34,7 @@ string legacyEncryptionKey /// The path to the storage directory. /// The SIWE signer wallet for SIWE authentication. /// 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. + /// The wallet secret for backend authentication. /// A task that represents the asynchronous operation. The task result contains the created in-app wallet. /// Thrown when required parameters are not provided. public static async Task Create( @@ -42,11 +44,12 @@ public static async Task 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, @@ -56,7 +59,8 @@ public static async Task Create( ecoWallet.AuthProvider, ecoWallet.SiweSigner, ecoWallet.Address, - ecoWallet.LegacyEncryptionKey + ecoWallet.LegacyEncryptionKey, + ecoWallet.WalletSecret ); } }