Skip to content

Commit 19d4307

Browse files
authored
InAppWallet -> Enclave (#87)
Signed-off-by: Firekeeper <0xFirekeeper@gmail.com>
1 parent d7c5d3f commit 19d4307

File tree

9 files changed

+292
-806
lines changed

9 files changed

+292
-806
lines changed

Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.Types.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace Thirdweb;
55

66
public partial class EcosystemWallet
77
{
8-
internal class EnclaveUserStatusResponse
8+
public class UserStatusResponse
99
{
1010
[JsonProperty("linkedAccounts")]
1111
internal List<LinkedAccount> LinkedAccounts { get; set; }

Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs

Lines changed: 211 additions & 119 deletions
Large diffs are not rendered by default.

Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet.Authentication/Server.cs

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ internal abstract class ServerBase
99
internal abstract Task<List<Server.LinkedAccount>> LinkAccountAsync(string currentAccountToken, string authTokenToConnect);
1010
internal abstract Task<List<Server.LinkedAccount>> GetLinkedAccountsAsync(string currentAccountToken);
1111

12-
internal abstract Task<Server.UserWallet> FetchUserDetailsAsync(string emailAddress, string authToken);
13-
internal abstract Task StoreAddressAndSharesAsync(string walletAddress, string authShare, string encryptedRecoveryShare, string authToken);
14-
1512
internal abstract Task<(string authShare, string recoveryShare)> FetchAuthAndRecoverySharesAsync(string authToken);
1613
internal abstract Task<string> FetchAuthShareAsync(string authToken);
1714

@@ -79,46 +76,6 @@ internal override async Task<List<LinkedAccount>> GetLinkedAccountsAsync(string
7976
return res == null || res.LinkedAccounts == null || res.LinkedAccounts.Count == 0 ? [] : res.LinkedAccounts;
8077
}
8178

82-
// embedded-wallet/embedded-wallet-user-details
83-
internal override async Task<UserWallet> FetchUserDetailsAsync(string emailOrPhone, string authToken)
84-
{
85-
Dictionary<string, string> queryParams = new();
86-
if (emailOrPhone == null && authToken == null)
87-
{
88-
throw new InvalidOperationException("Must provide either email address or auth token");
89-
}
90-
91-
queryParams.Add("email", emailOrPhone ?? "uninitialized");
92-
queryParams.Add("clientId", this._clientId);
93-
94-
var uri = MakeUri2023("/embedded-wallet/embedded-wallet-user-details", queryParams);
95-
var response = await this.SendHttpWithAuthAsync(uri, authToken ?? "").ConfigureAwait(false);
96-
await CheckStatusCodeAsync(response).ConfigureAwait(false);
97-
var rv = await DeserializeAsync<UserWallet>(response).ConfigureAwait(false);
98-
return rv;
99-
}
100-
101-
// embedded-wallet/embedded-wallet-shares POST
102-
internal override async Task StoreAddressAndSharesAsync(string walletAddress, string authShare, string encryptedRecoveryShare, string authToken)
103-
{
104-
var encryptedRecoveryShares = new[] { new { share = encryptedRecoveryShare, isClientEncrypted = "true" } };
105-
106-
HttpRequestMessage httpRequestMessage =
107-
new(HttpMethod.Post, MakeUri2023("/embedded-wallet/embedded-wallet-shares"))
108-
{
109-
Content = MakeHttpContent(
110-
new
111-
{
112-
authShare,
113-
maybeEncryptedRecoveryShares = encryptedRecoveryShares,
114-
walletAddress,
115-
}
116-
),
117-
};
118-
var response = await this.SendHttpWithAuthAsync(httpRequestMessage, authToken).ConfigureAwait(false);
119-
await CheckStatusCodeAsync(response).ConfigureAwait(false);
120-
}
121-
12279
// embedded-wallet/embedded-wallet-shares GET
12380
internal override async Task<(string authShare, string recoveryShare)> FetchAuthAndRecoverySharesAsync(string authToken)
12481
{

Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.EmailOTP.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@
22

33
internal partial class EmbeddedWallet
44
{
5-
public async Task<(bool isNewUser, bool isNewDevice)> SendEmailOtpAsync(string emailAddress)
5+
public async Task SendEmailOtpAsync(string emailAddress)
66
{
77
emailAddress = emailAddress.ToLower();
8-
var userWallet = await this._server.FetchUserDetailsAsync(emailAddress, null).ConfigureAwait(false);
98
_ = await this._server.SendEmailOtpAsync(emailAddress).ConfigureAwait(false);
10-
var isNewDevice = userWallet.IsNewUser || this._localStorage.Data?.WalletUserId != userWallet.WalletUserId;
11-
return (userWallet.IsNewUser, isNewDevice);
129
}
1310

1411
public async Task<Server.VerifyResult> VerifyEmailOtpAsync(string emailAddress, string otp)

Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.Misc.cs

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -15,93 +15,11 @@ internal async void UpdateSessionData(LocalStorage.DataStorage data)
1515
await this._localStorage.SaveDataAsync(data).ConfigureAwait(false);
1616
}
1717

18-
internal async Task<VerifyResult> PostAuthSetup(Server.VerifyResult result, string twManagedRecoveryCodeOverride, string authProvider)
19-
{
20-
var mainRecoveryCode = (twManagedRecoveryCodeOverride ?? result.RecoveryCode) ?? throw new InvalidOperationException("Server failed to return recovery code.");
21-
22-
(var account, var deviceShare) = result.IsNewUser
23-
? await this.CreateAccountAsync(result.AuthToken, mainRecoveryCode).ConfigureAwait(false)
24-
: await this.RecoverAccountAsync(result.AuthToken, mainRecoveryCode).ConfigureAwait(false);
25-
var user = this.MakeUserAsync(result.Email, result.PhoneNumber, account, result.AuthToken, result.WalletUserId, deviceShare, authProvider, result.AuthIdentifier);
26-
return new VerifyResult(user, mainRecoveryCode);
27-
}
28-
2918
public async Task SignOutAsync()
3019
{
31-
this._user = null;
3220
await this._localStorage.SaveDataAsync(new LocalStorage.DataStorage(null, null, null, null, null, null, null)).ConfigureAwait(false);
3321
}
3422

35-
public async Task<User> GetUserAsync(string email, string phone, string authProvider)
36-
{
37-
email = email?.ToLower();
38-
39-
if (this._user != null)
40-
{
41-
return this._user;
42-
}
43-
else if (this._localStorage.Data?.AuthToken == null)
44-
{
45-
throw new InvalidOperationException("User is not signed in");
46-
}
47-
48-
var userWallet = await this._server.FetchUserDetailsAsync(null, this._localStorage.Data.AuthToken).ConfigureAwait(false);
49-
switch (userWallet.Status)
50-
{
51-
case "Logged Out":
52-
throw new InvalidOperationException("User is logged out");
53-
case "Logged In, Wallet Uninitialized":
54-
throw new InvalidOperationException("User is logged in but wallet is uninitialized");
55-
case "Logged In, Wallet Initialized":
56-
if (string.IsNullOrEmpty(this._localStorage.Data?.DeviceShare))
57-
{
58-
throw new InvalidOperationException("User is logged in but wallet is uninitialized");
59-
}
60-
61-
var authShare = await this._server.FetchAuthShareAsync(this._localStorage.Data.AuthToken).ConfigureAwait(false);
62-
var emailAddress = userWallet.StoredToken?.AuthDetails.Email;
63-
var phoneNumber = userWallet.StoredToken?.AuthDetails.PhoneNumber;
64-
65-
if ((email != null && email != emailAddress) || (phone != null && phone != phoneNumber))
66-
{
67-
throw new InvalidOperationException("User email or phone number do not match");
68-
}
69-
else if (email == null && this._localStorage.Data.AuthProvider != authProvider)
70-
{
71-
throw new InvalidOperationException($"User auth provider does not match. Expected {this._localStorage.Data.AuthProvider}, got {authProvider}");
72-
}
73-
else if (authShare == null)
74-
{
75-
throw new InvalidOperationException("Server failed to return auth share");
76-
}
77-
78-
this._user = new User(MakeAccountFromShares(new[] { authShare, this._localStorage.Data.DeviceShare }), emailAddress, phoneNumber);
79-
return this._user;
80-
default:
81-
break;
82-
}
83-
throw new InvalidOperationException($"Unexpected user status '{userWallet.Status}'");
84-
}
85-
86-
private User MakeUserAsync(string emailAddress, string phoneNumber, Account account, string authToken, string walletUserId, string deviceShare, string authProvider, string authIdentifier)
87-
{
88-
var data = new LocalStorage.DataStorage(authToken, deviceShare, emailAddress, phoneNumber, walletUserId, authProvider, authIdentifier);
89-
this.UpdateSessionData(data);
90-
this._user = new User(account, emailAddress, phoneNumber);
91-
return this._user;
92-
}
93-
94-
private async Task<(Account account, string deviceShare)> CreateAccountAsync(string authToken, string recoveryCode)
95-
{
96-
var secret = Secrets.Random(KEY_SIZE);
97-
98-
(var deviceShare, var recoveryShare, var authShare) = CreateShares(secret);
99-
var encryptedRecoveryShare = await this.EncryptShareAsync(recoveryShare, recoveryCode).ConfigureAwait(false);
100-
Account account = new(secret);
101-
await this._server.StoreAddressAndSharesAsync(account.Address, authShare, encryptedRecoveryShare, authToken).ConfigureAwait(false);
102-
return (account, deviceShare);
103-
}
104-
10523
internal async Task<(Account account, string deviceShare)> RecoverAccountAsync(string authToken, string recoveryCode)
10624
{
10725
(var authShare, var encryptedRecoveryShare) = await this._server.FetchAuthAndRecoverySharesAsync(authToken).ConfigureAwait(false);

Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.PhoneOTP.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22

33
internal partial class EmbeddedWallet
44
{
5-
public async Task<(bool isNewUser, bool isNewDevice)> SendPhoneOtpAsync(string phoneNumber)
5+
public async Task SendPhoneOtpAsync(string phoneNumber)
66
{
7-
var userWallet = await this._server.FetchUserDetailsAsync(phoneNumber, null).ConfigureAwait(false);
87
_ = await this._server.SendPhoneOtpAsync(phoneNumber).ConfigureAwait(false);
9-
var isNewDevice = userWallet.IsNewUser || this._localStorage.Data?.WalletUserId != userWallet.WalletUserId;
10-
return (userWallet.IsNewUser, isNewDevice);
118
}
129

1310
public async Task<Server.VerifyResult> VerifyPhoneOtpAsync(string phoneNumber, string otp)

Thirdweb/Thirdweb.Wallets/InAppWallet/EmbeddedWallet/EmbeddedWallet.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ internal partial class EmbeddedWallet
55
private readonly LocalStorage _localStorage;
66
private readonly Server _server;
77
private readonly IvGenerator _ivGenerator;
8-
private User _user;
98

109
private const int DEVICE_SHARE_ID = 1;
1110
private const int KEY_SIZE = 256 / 8;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Thirdweb;
4+
5+
/// <summary>
6+
/// Specifies the authentication providers available for the in-app wallet.
7+
/// </summary>
8+
public enum AuthProvider
9+
{
10+
Default,
11+
Google,
12+
Apple,
13+
Facebook,
14+
JWT,
15+
AuthEndpoint,
16+
Discord,
17+
Farcaster,
18+
Telegram,
19+
Siwe,
20+
Line,
21+
Guest,
22+
X,
23+
Coinbase,
24+
Github,
25+
Twitch
26+
}
27+
28+
/// <summary>
29+
/// Represents a linked account.
30+
/// </summary>
31+
public struct LinkedAccount
32+
{
33+
public string Type { get; set; }
34+
public LinkedAccountDetails Details { get; set; }
35+
36+
public struct LinkedAccountDetails
37+
{
38+
public string Email { get; set; }
39+
public string Address { get; set; }
40+
public string Phone { get; set; }
41+
public string Id { get; set; }
42+
}
43+
44+
public override readonly string ToString()
45+
{
46+
return JsonConvert.SerializeObject(this);
47+
}
48+
}

0 commit comments

Comments
 (0)