@@ -71,20 +71,61 @@ public async Task<User> Connect(EmbeddedWallet embeddedWallet, string email, Aut
71
71
RecoveryInput . text = "" ;
72
72
RecoveryInput . gameObject . SetActive ( false ) ;
73
73
SubmitButton . onClick . RemoveAllListeners ( ) ;
74
+ RecoveryCodesCopy . onClick . RemoveAllListeners ( ) ;
74
75
EmbeddedWalletCanvas . SetActive ( false ) ;
75
76
RecoveryCodesCanvas . SetActive ( false ) ;
76
77
77
- switch ( authOptions . authProvider )
78
+ try
79
+ {
80
+ string authProvider = authOptions . authProvider switch
81
+ {
82
+ AuthProvider . EmailOTP => "EmailOTP" ,
83
+ AuthProvider . Google => "Google" ,
84
+ AuthProvider . Apple => "Apple" ,
85
+ AuthProvider . Facebook => "Facebook" ,
86
+ AuthProvider . CustomAuth => "CustomAuth" ,
87
+ _ => throw new UnityException ( $ "Unsupported auth provider: { authOptions . authProvider } ") ,
88
+ } ;
89
+ return await _embeddedWallet . GetUserAsync ( _email , authProvider ) ;
90
+ }
91
+ catch ( Exception e )
92
+ {
93
+ ThirdwebDebug . Log ( $ "Could not recreate user automatically, proceeding with auth: { e . Message } ") ;
94
+ }
95
+
96
+ try
78
97
{
79
- case AuthProvider . EmailOTP :
80
- return await LoginWithOTP ( ) ;
81
- case AuthProvider . Google :
82
- return await LoginWithGoogle ( ) ;
83
- case AuthProvider . CustomAuth :
84
- return await LoginWithCustomJwt ( authOptions . authToken ) ;
85
- default :
86
- throw new UnityException ( $ "Unsupported auth provider: { authOptions . authProvider } ") ;
98
+ switch ( authOptions . authProvider )
99
+ {
100
+ case AuthProvider . EmailOTP :
101
+ await LoginWithOTP ( ) ;
102
+ break ;
103
+ case AuthProvider . Google :
104
+ await LoginWithOauth ( "Google" ) ;
105
+ break ;
106
+ case AuthProvider . Apple :
107
+ await LoginWithOauth ( "Apple" ) ;
108
+ break ;
109
+ case AuthProvider . Facebook :
110
+ await LoginWithOauth ( "Facebook" ) ;
111
+ break ;
112
+ case AuthProvider . CustomAuth :
113
+ await LoginWithCustomJwt ( authOptions . authToken , authOptions . recoveryCode ) ;
114
+ break ;
115
+ default :
116
+ throw new UnityException ( $ "Unsupported auth provider: { authOptions . authProvider } ") ;
117
+ }
87
118
}
119
+ catch ( Exception e )
120
+ {
121
+ _exception = e ;
122
+ }
123
+
124
+ await new WaitUntil ( ( ) => _user != null || _exception != null ) ;
125
+ EmbeddedWalletCanvas . SetActive ( false ) ;
126
+ if ( _exception != null )
127
+ throw _exception ;
128
+ return _user ;
88
129
}
89
130
90
131
public void Cancel ( )
@@ -96,45 +137,26 @@ public void Cancel()
96
137
97
138
#region Email OTP Flow
98
139
99
- private async Task < User > LoginWithOTP ( )
140
+ private async Task LoginWithOTP ( )
100
141
{
101
142
if ( _email == null )
102
143
throw new UnityException ( "Email is required for OTP login" ) ;
103
144
104
- try
105
- {
106
- _user = await _embeddedWallet . GetUserAsync ( _email ) ;
107
- }
108
- catch ( Exception e )
109
- {
110
- ThirdwebDebug . Log ( $ "Could not recreate user automatically, proceeding with auth: { e } ") ;
111
- }
112
-
113
- if ( _user != null )
114
- {
115
- ThirdwebDebug . Log ( $ "Logged In Existing User - Email: { _user . EmailAddress } , User Address: { _user . Account . Address } ") ;
116
- return _user ;
117
- }
118
-
119
145
SubmitButton . onClick . AddListener ( OnSubmitOTP ) ;
120
146
await OnSendOTP ( ) ;
121
147
EmbeddedWalletCanvas . SetActive ( true ) ;
122
- await new WaitUntil ( ( ) => _user != null || _exception != null ) ;
123
- EmbeddedWalletCanvas . SetActive ( false ) ;
124
- if ( _exception != null )
125
- throw _exception ;
126
- return _user ;
127
148
}
128
149
129
150
private async Task OnSendOTP ( )
130
151
{
131
152
try
132
153
{
133
154
( bool isNewUser , bool isNewDevice , bool needsRecoveryCode ) = await _embeddedWallet . SendOtpEmailAsync ( _email ) ;
134
- RecoveryInput . gameObject . SetActive ( needsRecoveryCode && ! isNewUser && isNewDevice ) ;
155
+ if ( needsRecoveryCode && ! isNewUser && isNewDevice )
156
+ DisplayRecoveryInput ( false ) ;
135
157
ThirdwebDebug . Log ( $ "finished sending OTP: isNewUser { isNewUser } , isNewDevice { isNewDevice } ") ;
136
158
}
137
- catch ( System . Exception e )
159
+ catch ( Exception e )
138
160
{
139
161
_exception = e ;
140
162
}
@@ -148,15 +170,9 @@ private async void OnSubmitOTP()
148
170
try
149
171
{
150
172
string otp = OTPInput . text ;
151
- ( var res , bool ? wasEmailed ) = await _embeddedWallet . VerifyOtpAsync ( _email , otp , string . IsNullOrEmpty ( RecoveryInput . text ) ? null : RecoveryInput . text ) ;
173
+ var res = await _embeddedWallet . VerifyOtpAsync ( _email , otp , string . IsNullOrEmpty ( RecoveryInput . text ) ? null : RecoveryInput . text ) ;
152
174
_user = res . User ;
153
- if ( res . MainRecoveryCode != null && wasEmailed . HasValue && wasEmailed . Value == false )
154
- {
155
- List < string > recoveryCodes = new ( ) { res . MainRecoveryCode } ;
156
- if ( res . BackupRecoveryCodes != null )
157
- recoveryCodes . AddRange ( res . BackupRecoveryCodes ) ;
158
- ShowRecoveryCodes ( recoveryCodes ) ;
159
- }
175
+ ShowRecoveryCodes ( res ) ;
160
176
}
161
177
catch ( Exception e )
162
178
{
@@ -170,78 +186,115 @@ private async void OnSubmitOTP()
170
186
}
171
187
}
172
188
173
- private void ShowRecoveryCodes ( List < string > recoveryCodes )
174
- {
175
- string recoveryCodesString = string . Join ( "\n " , recoveryCodes . Select ( ( code , i ) => $ "{ i + 1 } . { code } ") ) ;
176
- string message = $ "Please save the following recovery codes in a safe place:\n \n { recoveryCodesString } ";
177
- ThirdwebDebug . Log ( message ) ;
178
- RecoveryCodesText . text = message ;
179
- string messageToSave = JsonConvert . SerializeObject ( recoveryCodes ) ;
180
- RecoveryCodesCopy . onClick . RemoveAllListeners ( ) ;
181
- RecoveryCodesCopy . onClick . AddListener ( ( ) => GUIUtility . systemCopyBuffer = messageToSave ) ;
182
- RecoveryCodesCanvas . SetActive ( true ) ;
183
- }
184
-
185
189
#endregion
186
190
187
191
#region OAuth2 Flow
188
192
189
- private async Task < User > LoginWithGoogle ( )
193
+ private async Task LoginWithOauth ( string authProviderStr )
190
194
{
191
195
if ( Application . isMobilePlatform && string . IsNullOrEmpty ( _customScheme ) )
192
196
throw new UnityException ( "No custom scheme provided for mobile deeplinks, please set one in your ThirdwebConfig (found in ThirdwebManager)" ) ;
193
197
194
- try
195
- {
196
- string loginUrl = await GetLoginLink ( ) ;
197
- string redirectUrl = Application . isMobilePlatform ? _customScheme : "http://localhost:8789/" ;
198
- CrossPlatformBrowser browser = new ( ) ;
199
- var browserResult = await browser . Login ( loginUrl , redirectUrl ) ;
200
- if ( browserResult . status != BrowserStatus . Success )
201
- _exception = new UnityException ( $ "Failed to login with Google: { browserResult . status } | { browserResult . error } ") ;
202
- else
203
- _callbackUrl = browserResult . callbackUrl ;
204
- }
205
- catch ( Exception e )
206
- {
207
- _exception = e ;
208
- }
198
+ string loginUrl = await GetLoginLink ( authProviderStr ) ;
209
199
210
- await new WaitUntil ( ( ) => _callbackUrl != null || _exception != null ) ;
211
- if ( _exception != null )
212
- throw _exception ;
200
+ string redirectUrl = Application . isMobilePlatform ? _customScheme : "http://localhost:8789/" ;
201
+ CrossPlatformBrowser browser = new ( ) ;
202
+ var browserResult = await browser . Login ( loginUrl , redirectUrl ) ;
203
+ if ( browserResult . status != BrowserStatus . Success )
204
+ _exception = new UnityException ( $ "Failed to login with { authProviderStr } : { browserResult . status } | { browserResult . error } ") ;
205
+ else
206
+ _callbackUrl = browserResult . callbackUrl ;
207
+
208
+ await new WaitUntil ( ( ) => _callbackUrl != null ) ;
213
209
214
210
string decodedUrl = HttpUtility . UrlDecode ( _callbackUrl ) ;
215
211
Uri uri = new ( decodedUrl ) ;
216
212
string queryString = uri . Query ;
217
213
var queryDict = HttpUtility . ParseQueryString ( queryString ) ;
218
214
string authResultJson = queryDict [ "authResult" ] ;
219
- var user = await _embeddedWallet . SignInWithGoogleAsync ( authResultJson ) ;
220
- return user ;
215
+
216
+ bool needsRecoveryCode = await _embeddedWallet . IsRecoveryCodeNeededAsync ( authResultJson ) ;
217
+
218
+ if ( needsRecoveryCode )
219
+ {
220
+ SubmitButton . onClick . AddListener ( ( ) => OnSubmitRecoveryOauth ( authProviderStr , authResultJson ) ) ;
221
+ DisplayRecoveryInput ( true ) ;
222
+ }
223
+ else
224
+ {
225
+ try
226
+ {
227
+ var res = await _embeddedWallet . SignInWithOauth ( authProviderStr , authResultJson , null ) ;
228
+ _user = res . User ;
229
+ }
230
+ catch ( Exception e )
231
+ {
232
+ _exception = e ;
233
+ }
234
+ }
221
235
}
222
236
223
- private async Task < string > GetLoginLink ( string authProvider = "Google" )
237
+ private async void OnSubmitRecoveryOauth ( string authProviderStr , string authResult )
238
+ {
239
+ try
240
+ {
241
+ string recoveryCode = RecoveryInput . text ;
242
+ var res = await _embeddedWallet . SignInWithOauth ( authProviderStr , authResult , recoveryCode ) ;
243
+ _user = res . User ;
244
+ ShowRecoveryCodes ( res ) ;
245
+ }
246
+ catch ( Exception e )
247
+ {
248
+ _exception = e ;
249
+ }
250
+ }
251
+
252
+ private async Task < string > GetLoginLink ( string authProvider )
224
253
{
225
254
string loginUrl = await _embeddedWallet . FetchHeadlessOauthLoginLinkAsync ( authProvider ) ;
226
255
string platform = "unity" ;
227
256
string redirectUrl = UnityWebRequest . EscapeURL ( Application . isMobilePlatform ? _customScheme : "http://localhost:8789/" ) ;
228
257
string developerClientId = UnityWebRequest . EscapeURL ( ThirdwebManager . Instance . SDK . session . Options . clientId ) ;
229
- return $ "{ loginUrl } ?platform={ platform } &redirectUrl={ redirectUrl } &developerClientId={ developerClientId } ";
258
+ return $ "{ loginUrl } ?platform={ platform } &redirectUrl={ redirectUrl } &developerClientId={ developerClientId } &authOption= { authProvider } ";
230
259
}
231
260
232
261
#endregion
233
262
234
263
#region Custom JWT Flow
235
264
236
- private async Task < User > LoginWithCustomJwt ( string jwtToken )
265
+ private async Task LoginWithCustomJwt ( string jwtToken , string recoveryCode )
237
266
{
238
- try
239
- {
240
- return await _embeddedWallet . SignInWithJwtAuthAsync ( jwtToken ) ;
241
- }
242
- catch
267
+ var res = await _embeddedWallet . SignInWithJwtAuthAsync ( jwtToken , recoveryCode ) ;
268
+ _user = res . User ;
269
+ ShowRecoveryCodes ( res ) ;
270
+ }
271
+
272
+ #endregion
273
+
274
+ #region Common
275
+
276
+ private void DisplayRecoveryInput ( bool hideOtpInput )
277
+ {
278
+ if ( hideOtpInput )
279
+ OTPInput . gameObject . SetActive ( false ) ;
280
+ RecoveryInput . gameObject . SetActive ( true ) ;
281
+ EmbeddedWalletCanvas . SetActive ( true ) ;
282
+ }
283
+
284
+ private void ShowRecoveryCodes ( EmbeddedWallet . VerifyResult res )
285
+ {
286
+ if ( res . MainRecoveryCode != null && res . WasEmailed . HasValue && res . WasEmailed . Value == false )
243
287
{
244
- throw ;
288
+ List < string > recoveryCodes = new ( ) { res . MainRecoveryCode } ;
289
+ if ( res . BackupRecoveryCodes != null )
290
+ recoveryCodes . AddRange ( res . BackupRecoveryCodes ) ;
291
+ string recoveryCodesString = string . Join ( "\n " , recoveryCodes . Select ( ( code , i ) => $ "{ i + 1 } . { code } ") ) ;
292
+ string message = $ "Please save the following recovery codes in a safe place:\n \n { recoveryCodesString } ";
293
+ ThirdwebDebug . Log ( message ) ;
294
+ RecoveryCodesText . text = message ;
295
+ string messageToSave = JsonConvert . SerializeObject ( recoveryCodes ) ;
296
+ RecoveryCodesCopy . onClick . AddListener ( ( ) => GUIUtility . systemCopyBuffer = messageToSave ) ;
297
+ RecoveryCodesCanvas . SetActive ( true ) ;
245
298
}
246
299
}
247
300
0 commit comments