@@ -71,20 +71,61 @@ public async Task<User> Connect(EmbeddedWallet embeddedWallet, string email, Aut
7171 RecoveryInput . text = "" ;
7272 RecoveryInput . gameObject . SetActive ( false ) ;
7373 SubmitButton . onClick . RemoveAllListeners ( ) ;
74+ RecoveryCodesCopy . onClick . RemoveAllListeners ( ) ;
7475 EmbeddedWalletCanvas . SetActive ( false ) ;
7576 RecoveryCodesCanvas . SetActive ( false ) ;
7677
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
7897 {
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+ }
87118 }
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 ;
88129 }
89130
90131 public void Cancel ( )
@@ -96,45 +137,26 @@ public void Cancel()
96137
97138 #region Email OTP Flow
98139
99- private async Task < User > LoginWithOTP ( )
140+ private async Task LoginWithOTP ( )
100141 {
101142 if ( _email == null )
102143 throw new UnityException ( "Email is required for OTP login" ) ;
103144
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-
119145 SubmitButton . onClick . AddListener ( OnSubmitOTP ) ;
120146 await OnSendOTP ( ) ;
121147 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 ;
127148 }
128149
129150 private async Task OnSendOTP ( )
130151 {
131152 try
132153 {
133154 ( 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 ) ;
135157 ThirdwebDebug . Log ( $ "finished sending OTP: isNewUser { isNewUser } , isNewDevice { isNewDevice } ") ;
136158 }
137- catch ( System . Exception e )
159+ catch ( Exception e )
138160 {
139161 _exception = e ;
140162 }
@@ -148,15 +170,9 @@ private async void OnSubmitOTP()
148170 try
149171 {
150172 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 ) ;
152174 _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 ) ;
160176 }
161177 catch ( Exception e )
162178 {
@@ -170,78 +186,115 @@ private async void OnSubmitOTP()
170186 }
171187 }
172188
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-
185189 #endregion
186190
187191 #region OAuth2 Flow
188192
189- private async Task < User > LoginWithGoogle ( )
193+ private async Task LoginWithOauth ( string authProviderStr )
190194 {
191195 if ( Application . isMobilePlatform && string . IsNullOrEmpty ( _customScheme ) )
192196 throw new UnityException ( "No custom scheme provided for mobile deeplinks, please set one in your ThirdwebConfig (found in ThirdwebManager)" ) ;
193197
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 ) ;
209199
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 ) ;
213209
214210 string decodedUrl = HttpUtility . UrlDecode ( _callbackUrl ) ;
215211 Uri uri = new ( decodedUrl ) ;
216212 string queryString = uri . Query ;
217213 var queryDict = HttpUtility . ParseQueryString ( queryString ) ;
218214 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+ }
221235 }
222236
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 )
224253 {
225254 string loginUrl = await _embeddedWallet . FetchHeadlessOauthLoginLinkAsync ( authProvider ) ;
226255 string platform = "unity" ;
227256 string redirectUrl = UnityWebRequest . EscapeURL ( Application . isMobilePlatform ? _customScheme : "http://localhost:8789/" ) ;
228257 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 } ";
230259 }
231260
232261 #endregion
233262
234263 #region Custom JWT Flow
235264
236- private async Task < User > LoginWithCustomJwt ( string jwtToken )
265+ private async Task LoginWithCustomJwt ( string jwtToken , string recoveryCode )
237266 {
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 )
243287 {
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 ) ;
245298 }
246299 }
247300
0 commit comments