From d0d24d71f400c8bf06193af6c116a3af89686c98 Mon Sep 17 00:00:00 2001 From: Firekeeper <0xFirekeeper@gmail.com> Date: Wed, 15 May 2024 17:15:43 +0300 Subject: [PATCH] Authenticate (#18) * Authenticate * dupe * nulls and tests * can't always get exactly value as diff due to gas fluctuations * resources * Update IThirdwebWallet.cs * Revert "Update IThirdwebWallet.cs" This reverts commit 619c12d5b6a5ae58f86f223dd3ff0df505cd3eb3. * full payload test --- Thirdweb.Console/Program.cs | 152 +++++++-------- Thirdweb.Tests/Thirdweb.Transactions.Tests.cs | 3 +- Thirdweb.Tests/Thirdweb.Utils.Tests.cs | 173 ++++++++++++++++++ Thirdweb/Thirdweb.Utils/Utils.cs | 47 +++++ Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs | 54 ++++++ .../PrivateKeyWallet/PrivateKeyWallet.cs | 31 ++++ .../SmartWallet/SmartWallet.cs | 31 ++++ 7 files changed, 414 insertions(+), 77 deletions(-) diff --git a/Thirdweb.Console/Program.cs b/Thirdweb.Console/Program.cs index a6207d9..31dd8d7 100644 --- a/Thirdweb.Console/Program.cs +++ b/Thirdweb.Console/Program.cs @@ -52,81 +52,83 @@ // Create smart wallet with InAppWallet signer var smartWallet = await SmartWallet.Create(client: client, personalWallet: inAppWallet, factoryAddress: "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052", gasless: true, chainId: 421614); - -// Grant a session key to pk wallet (advanced use case) -_ = await smartWallet.CreateSessionKey( - signerAddress: await privateKeyWallet.GetAddress(), - approvedTargets: new List() { Constants.ADDRESS_ZERO }, - nativeTokenLimitPerTransactionInWei: "0", - permissionStartTimestamp: "0", - permissionEndTimestamp: (Utils.GetUnixTimeStampNow() + 86400).ToString(), - reqValidityStartTimestamp: "0", - reqValidityEndTimestamp: Utils.GetUnixTimeStampIn10Years().ToString() -); - -// Reconnect to same smart wallet with pk wallet as signer (specifying wallet address override) -smartWallet = await SmartWallet.Create( - client: client, - personalWallet: privateKeyWallet, - factoryAddress: "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052", - gasless: true, - chainId: 421614, - accountAddressOverride: await smartWallet.GetAddress() -); - -// Log addresses -Console.WriteLine($"PrivateKey Wallet: {await privateKeyWallet.GetAddress()}"); -Console.WriteLine($"InAppWallet: {await inAppWallet.GetAddress()}"); -Console.WriteLine($"Smart Wallet: {await smartWallet.GetAddress()}"); - -// Sign, triggering deploy as needed and 1271 verification if it's a smart wallet -var message = "Hello, Thirdweb!"; -var signature = await smartWallet.PersonalSign(message); -Console.WriteLine($"Signed message: {signature}"); - -var balanceBefore = await ThirdwebContract.Read(contract, "balanceOf", await smartWallet.GetAddress()); -Console.WriteLine($"Balance before mint: {balanceBefore}"); - -var writeResult = await ThirdwebContract.Write(smartWallet, contract, "mintTo", 0, await smartWallet.GetAddress(), 100); -Console.WriteLine($"Contract write result: {writeResult}"); - -var balanceAfter = await ThirdwebContract.Read(contract, "balanceOf", await smartWallet.GetAddress()); -Console.WriteLine($"Balance after mint: {balanceAfter}"); - -// Transaction Builder -var preparedTx = await ThirdwebContract.Prepare(wallet: smartWallet, contract: contract, method: "mintTo", weiValue: 0, parameters: new object[] { await smartWallet.GetAddress(), 100 }); -Console.WriteLine($"Prepared transaction: {preparedTx}"); -var estimatedCosts = await ThirdwebTransaction.EstimateGasCosts(preparedTx); -Console.WriteLine($"Estimated ETH gas cost: {estimatedCosts.ether}"); -var totalCosts = await ThirdwebTransaction.EstimateTotalCosts(preparedTx); -Console.WriteLine($"Estimated ETH total cost: {totalCosts.ether}"); -var simulationData = await ThirdwebTransaction.Simulate(preparedTx); -Console.WriteLine($"Simulation data: {simulationData}"); -var txHash = await ThirdwebTransaction.Send(preparedTx); -Console.WriteLine($"Transaction hash: {txHash}"); -var receipt = await ThirdwebTransaction.WaitForTransactionReceipt(client, 421614, txHash); -Console.WriteLine($"Transaction receipt: {JsonConvert.SerializeObject(receipt)}"); - -// Transaction Builder - raw transfer -var rawTx = new TransactionInput -{ - From = await smartWallet.GetAddress(), - To = await smartWallet.GetAddress(), - Value = new HexBigInteger(BigInteger.Zero), - Data = "0x", -}; -var preparedRawTx = await ThirdwebTransaction.Create(client: client, wallet: smartWallet, txInput: rawTx, chainId: 421614); -Console.WriteLine($"Prepared raw transaction: {preparedRawTx}"); -var estimatedCostsRaw = await ThirdwebTransaction.EstimateGasCosts(preparedRawTx); -Console.WriteLine($"Estimated ETH gas cost: {estimatedCostsRaw.ether}"); -var totalCostsRaw = await ThirdwebTransaction.EstimateTotalCosts(preparedRawTx); -Console.WriteLine($"Estimated ETH total cost: {totalCostsRaw.ether}"); -var simulationDataRaw = await ThirdwebTransaction.Simulate(preparedRawTx); -Console.WriteLine($"Simulation data: {simulationDataRaw}"); -var txHashRaw = await ThirdwebTransaction.Send(preparedRawTx); -Console.WriteLine($"Raw transaction hash: {txHashRaw}"); -var receiptRaw = await ThirdwebTransaction.WaitForTransactionReceipt(client, 421614, txHashRaw); -Console.WriteLine($"Raw transaction receipt: {JsonConvert.SerializeObject(receiptRaw)}"); +var res = await smartWallet.Authenticate("http://localhost:8000", 421614); +Console.WriteLine($"Smart wallet auth result: {res}"); + +// // Grant a session key to pk wallet (advanced use case) +// _ = await smartWallet.CreateSessionKey( +// signerAddress: await privateKeyWallet.GetAddress(), +// approvedTargets: new List() { Constants.ADDRESS_ZERO }, +// nativeTokenLimitPerTransactionInWei: "0", +// permissionStartTimestamp: "0", +// permissionEndTimestamp: (Utils.GetUnixTimeStampNow() + 86400).ToString(), +// reqValidityStartTimestamp: "0", +// reqValidityEndTimestamp: Utils.GetUnixTimeStampIn10Years().ToString() +// ); + +// // Reconnect to same smart wallet with pk wallet as signer (specifying wallet address override) +// smartWallet = await SmartWallet.Create( +// client: client, +// personalWallet: privateKeyWallet, +// factoryAddress: "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052", +// gasless: true, +// chainId: 421614, +// accountAddressOverride: await smartWallet.GetAddress() +// ); + +// // Log addresses +// Console.WriteLine($"PrivateKey Wallet: {await privateKeyWallet.GetAddress()}"); +// Console.WriteLine($"InAppWallet: {await inAppWallet.GetAddress()}"); +// Console.WriteLine($"Smart Wallet: {await smartWallet.GetAddress()}"); + +// // Sign, triggering deploy as needed and 1271 verification if it's a smart wallet +// var message = "Hello, Thirdweb!"; +// var signature = await smartWallet.PersonalSign(message); +// Console.WriteLine($"Signed message: {signature}"); + +// var balanceBefore = await ThirdwebContract.Read(contract, "balanceOf", await smartWallet.GetAddress()); +// Console.WriteLine($"Balance before mint: {balanceBefore}"); + +// var writeResult = await ThirdwebContract.Write(smartWallet, contract, "mintTo", 0, await smartWallet.GetAddress(), 100); +// Console.WriteLine($"Contract write result: {writeResult}"); + +// var balanceAfter = await ThirdwebContract.Read(contract, "balanceOf", await smartWallet.GetAddress()); +// Console.WriteLine($"Balance after mint: {balanceAfter}"); + +// // Transaction Builder +// var preparedTx = await ThirdwebContract.Prepare(wallet: smartWallet, contract: contract, method: "mintTo", weiValue: 0, parameters: new object[] { await smartWallet.GetAddress(), 100 }); +// Console.WriteLine($"Prepared transaction: {preparedTx}"); +// var estimatedCosts = await ThirdwebTransaction.EstimateGasCosts(preparedTx); +// Console.WriteLine($"Estimated ETH gas cost: {estimatedCosts.ether}"); +// var totalCosts = await ThirdwebTransaction.EstimateTotalCosts(preparedTx); +// Console.WriteLine($"Estimated ETH total cost: {totalCosts.ether}"); +// var simulationData = await ThirdwebTransaction.Simulate(preparedTx); +// Console.WriteLine($"Simulation data: {simulationData}"); +// var txHash = await ThirdwebTransaction.Send(preparedTx); +// Console.WriteLine($"Transaction hash: {txHash}"); +// var receipt = await ThirdwebTransaction.WaitForTransactionReceipt(client, 421614, txHash); +// Console.WriteLine($"Transaction receipt: {JsonConvert.SerializeObject(receipt)}"); + +// // Transaction Builder - raw transfer +// var rawTx = new TransactionInput +// { +// From = await smartWallet.GetAddress(), +// To = await smartWallet.GetAddress(), +// Value = new HexBigInteger(BigInteger.Zero), +// Data = "0x", +// }; +// var preparedRawTx = await ThirdwebTransaction.Create(client: client, wallet: smartWallet, txInput: rawTx, chainId: 421614); +// Console.WriteLine($"Prepared raw transaction: {preparedRawTx}"); +// var estimatedCostsRaw = await ThirdwebTransaction.EstimateGasCosts(preparedRawTx); +// Console.WriteLine($"Estimated ETH gas cost: {estimatedCostsRaw.ether}"); +// var totalCostsRaw = await ThirdwebTransaction.EstimateTotalCosts(preparedRawTx); +// Console.WriteLine($"Estimated ETH total cost: {totalCostsRaw.ether}"); +// var simulationDataRaw = await ThirdwebTransaction.Simulate(preparedRawTx); +// Console.WriteLine($"Simulation data: {simulationDataRaw}"); +// var txHashRaw = await ThirdwebTransaction.Send(preparedRawTx); +// Console.WriteLine($"Raw transaction hash: {txHashRaw}"); +// var receiptRaw = await ThirdwebTransaction.WaitForTransactionReceipt(client, 421614, txHashRaw); +// Console.WriteLine($"Raw transaction receipt: {JsonConvert.SerializeObject(receiptRaw)}"); // Storage actions diff --git a/Thirdweb.Tests/Thirdweb.Transactions.Tests.cs b/Thirdweb.Tests/Thirdweb.Transactions.Tests.cs index 2f5b9f2..f7fcfcb 100644 --- a/Thirdweb.Tests/Thirdweb.Transactions.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.Transactions.Tests.cs @@ -218,14 +218,13 @@ public async Task EstimateGasCosts_SmartWalletHigherThanPrivateKeyWallet() public async Task EstimateTotalCosts_HigherThanGasCostsByValue() { var transaction = await CreateSampleTransaction(); - _ = transaction.SetValue(new BigInteger(1000)); + _ = transaction.SetValue(new BigInteger(1000000000000000000)); // 100 gwei accounting for fluctuations _ = transaction.SetGasLimit(21000); var totalCosts = await ThirdwebTransaction.EstimateTotalCosts(transaction); var gasCosts = await ThirdwebTransaction.EstimateGasCosts(transaction); Assert.True(totalCosts.wei > gasCosts.wei); - Assert.True(totalCosts.wei - gasCosts.wei == transaction.Input.Value.Value); } [Fact] diff --git a/Thirdweb.Tests/Thirdweb.Utils.Tests.cs b/Thirdweb.Tests/Thirdweb.Utils.Tests.cs index 475b90b..d850bf6 100644 --- a/Thirdweb.Tests/Thirdweb.Utils.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.Utils.Tests.cs @@ -202,4 +202,177 @@ public void FormatERC20_ThrowsOnInvalidWei() var invalidWei = "not_a_number"; Assert.Throws(() => Utils.FormatERC20(invalidWei, 4)); } + + [Fact] + public void GenerateSIWE_ReturnsCorrectValue() + { + var loginPayloadData = new LoginPayloadData + { + Version = "1", + ChainId = "421614", + Nonce = "0", + Address = Constants.ADDRESS_ZERO, + Domain = "thirdweb.com", + IssuedAt = "0", + ExpirationTime = "0", + InvalidBefore = "0" + }; + var expectedSIWE = + "thirdweb.com wants you to sign in with your Ethereum account:\n0x0000000000000000000000000000000000000000\n\n\nVersion: 1\nChain ID: 421614\nNonce: 0\nIssued At: 0\nExpiration Time: 0\nNot Before: 0"; + var siwe = Utils.GenerateSIWE(loginPayloadData); + Assert.Equal(expectedSIWE, siwe); + } + + [Fact] + public void GenerateSIWE_WithAllOptional_ReturnsCorrectValue() + { + var loginPayloadData = new LoginPayloadData + { + Version = "1", + ChainId = "421614", + Nonce = "0", + Address = Constants.ADDRESS_ZERO, + Domain = "thirdweb.com", + IssuedAt = "0", + ExpirationTime = "0", + InvalidBefore = "0", + Statement = "This is a statement", + Uri = "https://thirdweb.com", + Resources = new List() { "resource1", "resource2" } + }; + var expectedSIWE = + "thirdweb.com wants you to sign in with your Ethereum account:\n0x0000000000000000000000000000000000000000\n\nThis is a statement\n\nURI: https://thirdweb.com\nVersion: 1\nChain ID: 421614\nNonce: 0\nIssued At: 0\nExpiration Time: 0\nNot Before: 0\nResources:\n- resource1\n- resource2"; + var siwe = Utils.GenerateSIWE(loginPayloadData); + Assert.Equal(expectedSIWE, siwe); + } + + [Fact] + public void GenerateSIWE_WithResources_ReturnsCorrectValue() + { + var loginPayloadData = new LoginPayloadData + { + Version = "1", + ChainId = "421614", + Nonce = "0", + Address = Constants.ADDRESS_ZERO, + Domain = "thirdweb.com", + IssuedAt = "0", + ExpirationTime = "0", + InvalidBefore = "0", + Resources = new List() { "resource1", "resource2" } + }; + var expectedSIWE = + "thirdweb.com wants you to sign in with your Ethereum account:\n0x0000000000000000000000000000000000000000\n\n\nVersion: 1\nChain ID: 421614\nNonce: 0\nIssued At: 0\nExpiration Time: 0\nNot Before: 0\nResources:\n- resource1\n- resource2"; + var siwe = Utils.GenerateSIWE(loginPayloadData); + Assert.Equal(expectedSIWE, siwe); + } + + [Fact] + public void GenerateSIWE_ThrowsOnNullLoginPayloadData() + { + LoginPayloadData? loginPayloadData = null; + _ = Assert.Throws(() => Utils.GenerateSIWE(loginPayloadData)); + } + + [Fact] + public void GenerateSIWE_ThrowsOnNullDomain() + { + var loginPayloadData = new LoginPayloadData + { + Version = "1", + ChainId = "421614", + Nonce = "0", + Address = Constants.ADDRESS_ZERO, + Domain = null!, + IssuedAt = "0", + ExpirationTime = "0", + InvalidBefore = "0" + }; + _ = Assert.Throws(() => Utils.GenerateSIWE(loginPayloadData)); + } + + [Fact] + public void GenerateSIWE_ThrowsOnNullAddress() + { + var loginPayloadData = new LoginPayloadData + { + Version = "1", + ChainId = "421614", + Nonce = "0", + Address = null!, + Domain = "thirdweb.com", + IssuedAt = "0", + ExpirationTime = "0", + InvalidBefore = "0" + }; + _ = Assert.Throws(() => Utils.GenerateSIWE(loginPayloadData)); + } + + [Fact] + public void GenerateSIWE_ThrowsOnNullVersion() + { + var loginPayloadData = new LoginPayloadData + { + Version = null!, + ChainId = "421614", + Nonce = "0", + Address = Constants.ADDRESS_ZERO, + Domain = "thirdweb.com", + IssuedAt = "0", + ExpirationTime = "0", + InvalidBefore = "0" + }; + _ = Assert.Throws(() => Utils.GenerateSIWE(loginPayloadData)); + } + + [Fact] + public void GenerateSIWE_ThrowsOnNullChainId() + { + var loginPayloadData = new LoginPayloadData + { + Version = "1", + ChainId = null!, + Nonce = "0", + Address = Constants.ADDRESS_ZERO, + Domain = "thirdweb.com", + IssuedAt = "0", + ExpirationTime = "0", + InvalidBefore = "0" + }; + _ = Assert.Throws(() => Utils.GenerateSIWE(loginPayloadData)); + } + + [Fact] + public void GenerateSIWE_ThrowsOnNullNonce() + { + var loginPayloadData = new LoginPayloadData + { + Version = "1", + ChainId = "421614", + Nonce = null!, + Address = Constants.ADDRESS_ZERO, + Domain = "thirdweb.com", + IssuedAt = "0", + ExpirationTime = "0", + InvalidBefore = "0" + }; + _ = Assert.Throws(() => Utils.GenerateSIWE(loginPayloadData)); + } + + [Fact] + public void GenerateSIWE_ThrowsOnNullIssuedAt() + { + var loginPayloadData = new LoginPayloadData + { + Version = "1", + ChainId = "421614", + Nonce = "0", + Address = Constants.ADDRESS_ZERO, + Domain = "thirdweb.com", + IssuedAt = null!, + ExpirationTime = "0", + InvalidBefore = "0" + }; + _ = Assert.Throws(() => Utils.GenerateSIWE(loginPayloadData)); + } } diff --git a/Thirdweb/Thirdweb.Utils/Utils.cs b/Thirdweb/Thirdweb.Utils/Utils.cs index 9d3d94a..1b96423 100644 --- a/Thirdweb/Thirdweb.Utils/Utils.cs +++ b/Thirdweb/Thirdweb.Utils/Utils.cs @@ -138,5 +138,52 @@ public static string FormatERC20(this string wei, int decimalsToDisplay = 4, int return eth.ToString(format); } + + public static string GenerateSIWE(LoginPayloadData loginPayloadData) + { + if (loginPayloadData == null) + { + throw new ArgumentNullException(nameof(loginPayloadData)); + } + else if (string.IsNullOrEmpty(loginPayloadData.Domain)) + { + throw new ArgumentNullException(nameof(loginPayloadData.Domain)); + } + else if (string.IsNullOrEmpty(loginPayloadData.Address)) + { + throw new ArgumentNullException(nameof(loginPayloadData.Address)); + } + else if (string.IsNullOrEmpty(loginPayloadData.Version)) + { + throw new ArgumentNullException(nameof(loginPayloadData.Version)); + } + else if (string.IsNullOrEmpty(loginPayloadData.ChainId)) + { + throw new ArgumentNullException(nameof(loginPayloadData.ChainId)); + } + else if (string.IsNullOrEmpty(loginPayloadData.Nonce)) + { + throw new ArgumentNullException(nameof(loginPayloadData.Nonce)); + } + else if (string.IsNullOrEmpty(loginPayloadData.IssuedAt)) + { + throw new ArgumentNullException(nameof(loginPayloadData.IssuedAt)); + } + + var resourcesString = loginPayloadData.Resources != null ? "\nResources:" + string.Join("", loginPayloadData.Resources.Select(r => $"\n- {r}")) : string.Empty; + var payloadToSign = + $"{loginPayloadData.Domain} wants you to sign in with your Ethereum account:" + + $"\n{loginPayloadData.Address}\n\n" + + $"{(string.IsNullOrEmpty(loginPayloadData.Statement) ? "" : $"{loginPayloadData.Statement}\n")}" + + $"{(string.IsNullOrEmpty(loginPayloadData.Uri) ? "" : $"\nURI: {loginPayloadData.Uri}")}" + + $"\nVersion: {loginPayloadData.Version}" + + $"\nChain ID: {loginPayloadData.ChainId}" + + $"\nNonce: {loginPayloadData.Nonce}" + + $"\nIssued At: {loginPayloadData.IssuedAt}" + + $"{(string.IsNullOrEmpty(loginPayloadData.ExpirationTime) ? "" : $"\nExpiration Time: {loginPayloadData.ExpirationTime}")}" + + $"{(string.IsNullOrEmpty(loginPayloadData.InvalidBefore) ? "" : $"\nNot Before: {loginPayloadData.InvalidBefore}")}" + + resourcesString; + return payloadToSign; + } } } diff --git a/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs b/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs index 4d03811..2832c5d 100644 --- a/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs @@ -1,6 +1,7 @@ using System.Numerics; using Nethereum.ABI.EIP712; using Nethereum.RPC.Eth.DTOs; +using Newtonsoft.Json; namespace Thirdweb { @@ -16,6 +17,7 @@ public Task SignTypedDataV4(T data, TypedData typed where TDomain : IDomain; public Task IsConnected(); public Task SignTransaction(TransactionInput transaction, BigInteger chainId); + public Task Authenticate(string domain, BigInteger chainId, string authPayloadPath = "/auth/payload", string authLoginPath = "/auth/login", HttpClient httpClient = null); } public enum ThirdwebAccountType @@ -23,4 +25,56 @@ public enum ThirdwebAccountType PrivateKeyAccount, SmartAccount } + + [Serializable] + public struct LoginPayload + { + public LoginPayloadData payload; + public string signature; + } + + [Serializable] + public class LoginPayloadData + { + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("domain")] + public string Domain { get; set; } + + [JsonProperty("address")] + public string Address { get; set; } + + [JsonProperty("statement")] + public string Statement { get; set; } + + [JsonProperty("uri", NullValueHandling = NullValueHandling.Ignore)] + public string Uri { get; set; } + + [JsonProperty("version", NullValueHandling = NullValueHandling.Ignore)] + public string Version { get; set; } + + [JsonProperty("chain_id", NullValueHandling = NullValueHandling.Ignore)] + public string ChainId { get; set; } + + [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] + public string Nonce { get; set; } + + [JsonProperty("issued_at", NullValueHandling = NullValueHandling.Ignore)] + public string IssuedAt { get; set; } + + [JsonProperty("expiration_time", NullValueHandling = NullValueHandling.Ignore)] + public string ExpirationTime { get; set; } + + [JsonProperty("invalid_before", NullValueHandling = NullValueHandling.Ignore)] + public string InvalidBefore { get; set; } + + [JsonProperty("resources", NullValueHandling = NullValueHandling.Ignore)] + public List Resources { get; set; } + + public LoginPayloadData() + { + Type = "evm"; + } + } } diff --git a/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs b/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs index 75662eb..bc89740 100644 --- a/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs @@ -8,6 +8,7 @@ using Nethereum.RPC.Eth.Mappers; using Nethereum.Signer; using Nethereum.Signer.EIP712; +using Newtonsoft.Json; namespace Thirdweb { @@ -167,5 +168,35 @@ public virtual Task Disconnect() _ecKey = null; return Task.CompletedTask; } + + public virtual async Task Authenticate(string domain, BigInteger chainId, string authPayloadPath = "/auth/payload", string authLoginPath = "/auth/login", HttpClient httpClient = null) + { + var payloadURL = domain + authPayloadPath; + var loginURL = domain + authLoginPath; + + var payloadBodyRaw = new { address = await GetAddress(), chainId = chainId.ToString() }; + var payloadBody = JsonConvert.SerializeObject(payloadBodyRaw); + + httpClient ??= new HttpClient(); + + using var payloadRequest = new HttpRequestMessage(HttpMethod.Post, payloadURL); + payloadRequest.Content = new StringContent(payloadBody, Encoding.UTF8, "application/json"); + var payloadResponse = await httpClient.SendAsync(payloadRequest); + _ = payloadResponse.EnsureSuccessStatusCode(); + var payloadString = await payloadResponse.Content.ReadAsStringAsync(); + + var loginBodyRaw = JsonConvert.DeserializeObject(payloadString); + var payloadToSign = Utils.GenerateSIWE(loginBodyRaw.payload); + + loginBodyRaw.signature = await PersonalSign(payloadToSign); + var loginBody = JsonConvert.SerializeObject(new { payload = loginBodyRaw }); + + using var loginRequest = new HttpRequestMessage(HttpMethod.Post, loginURL); + loginRequest.Content = new StringContent(loginBody, Encoding.UTF8, "application/json"); + var loginResponse = await httpClient.SendAsync(loginRequest); + _ = loginResponse.EnsureSuccessStatusCode(); + var responseString = await loginResponse.Content.ReadAsStringAsync(); + return responseString; + } } } diff --git a/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs b/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs index 137e45b..ec77b8e 100644 --- a/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs @@ -1,5 +1,6 @@ using System.Numerics; using System.Security.Cryptography; +using System.Text; using Nethereum.ABI.EIP712; using Nethereum.Contracts; using Nethereum.Hex.HexConvertors.Extensions; @@ -443,5 +444,35 @@ public Task Disconnect() _accountContract = null; return Task.CompletedTask; } + + public virtual async Task Authenticate(string domain, BigInteger chainId, string authPayloadPath = "/auth/payload", string authLoginPath = "/auth/login", HttpClient httpClient = null) + { + var payloadURL = domain + authPayloadPath; + var loginURL = domain + authLoginPath; + + var payloadBodyRaw = new { address = await GetAddress(), chainId = chainId.ToString() }; + var payloadBody = JsonConvert.SerializeObject(payloadBodyRaw); + + httpClient ??= new HttpClient(); + + using var payloadRequest = new HttpRequestMessage(HttpMethod.Post, payloadURL); + payloadRequest.Content = new StringContent(payloadBody, Encoding.UTF8, "application/json"); + var payloadResponse = await httpClient.SendAsync(payloadRequest); + _ = payloadResponse.EnsureSuccessStatusCode(); + var payloadString = await payloadResponse.Content.ReadAsStringAsync(); + + var loginBodyRaw = JsonConvert.DeserializeObject(payloadString); + var payloadToSign = Utils.GenerateSIWE(loginBodyRaw.payload); + + loginBodyRaw.signature = await PersonalSign(payloadToSign); + var loginBody = JsonConvert.SerializeObject(new { payload = loginBodyRaw }); + + using var loginRequest = new HttpRequestMessage(HttpMethod.Post, loginURL); + loginRequest.Content = new StringContent(loginBody, Encoding.UTF8, "application/json"); + var loginResponse = await httpClient.SendAsync(loginRequest); + _ = loginResponse.EnsureSuccessStatusCode(); + var responseString = await loginResponse.Content.ReadAsStringAsync(); + return responseString; + } } }