15
15
import android .support .annotation .NonNull ;
16
16
import android .util .Log ;
17
17
import com .google .android .gms .auth .GoogleAuthUtil ;
18
+ import com .google .android .gms .auth .UserRecoverableAuthException ;
18
19
import com .google .android .gms .auth .api .Auth ;
19
20
import com .google .android .gms .auth .api .signin .GoogleSignIn ;
20
21
import com .google .android .gms .auth .api .signin .GoogleSignInAccount ;
@@ -89,7 +90,8 @@ public void onMethodCall(MethodCall call, Result result) {
89
90
90
91
case METHOD_GET_TOKENS :
91
92
String email = call .argument ("email" );
92
- delegate .getTokens (result , email );
93
+ boolean shouldRecoverAuth = call .argument ("shouldRecoverAuth" );
94
+ delegate .getTokens (result , email , shouldRecoverAuth );
93
95
break ;
94
96
95
97
case METHOD_SIGN_OUT :
@@ -134,8 +136,11 @@ public void init(
134
136
/**
135
137
* Gets an OAuth access token with the scopes that were specified during initialization for the
136
138
* user with the specified email address.
139
+ *
140
+ * <p>If shouldRecoverAuth is set to true and user needs to recover authentication for method to
141
+ * complete, the method will attempt to recover authentication and rerun method.
137
142
*/
138
- public void getTokens (final Result result , final String email );
143
+ public void getTokens (final Result result , final String email , final boolean shouldRecoverAuth );
139
144
140
145
/**
141
146
* Signs the user out. Their credentials may remain valid, meaning they'll be able to silently
@@ -161,6 +166,7 @@ public void init(
161
166
*/
162
167
public static final class Delegate implements IDelegate {
163
168
private static final int REQUEST_CODE = 53293 ;
169
+ private static final int REQUEST_CODE_RECOVER_AUTH = 12345 ;
164
170
private static final int REQUEST_CODE_RESOLVE_ERROR = 1001 ;
165
171
166
172
private static final String ERROR_REASON_EXCEPTION = "exception" ;
@@ -170,6 +176,8 @@ public static final class Delegate implements IDelegate {
170
176
private static final String ERROR_REASON_SIGN_IN_CANCELED = "sign_in_canceled" ;
171
177
private static final String ERROR_REASON_SIGN_IN_REQUIRED = "sign_in_required" ;
172
178
private static final String ERROR_REASON_SIGN_IN_FAILED = "sign_in_failed" ;
179
+ private static final String ERROR_FAILURE_TO_RECOVER_AUTH = "failed_to_recover_auth" ;
180
+ private static final String ERROR_USER_RECOVERABLE_AUTH = "user_recoverable_auth" ;
173
181
174
182
private static final String STATE_RESOLVING_ERROR = "resolving_error" ;
175
183
@@ -199,11 +207,15 @@ public GoogleSignInAccount getCurrentAccount() {
199
207
}
200
208
201
209
private void checkAndSetPendingOperation (String method , Result result ) {
210
+ checkAndSetPendingOperation (method , result , null );
211
+ }
212
+
213
+ private void checkAndSetPendingOperation (String method , Result result , Object data ) {
202
214
if (pendingOperation != null ) {
203
215
throw new IllegalStateException (
204
216
"Concurrent operations detected: " + pendingOperation .method + ", " + method );
205
217
}
206
- pendingOperation = new PendingOperation (method , result );
218
+ pendingOperation = new PendingOperation (method , result , data );
207
219
}
208
220
209
221
/**
@@ -308,9 +320,13 @@ public void signIn(Result result) {
308
320
/**
309
321
* Gets an OAuth access token with the scopes that were specified during initialization for the
310
322
* user with the specified email address.
323
+ *
324
+ * <p>If shouldRecoverAuth is set to true and user needs to recover authentication for method to
325
+ * complete, the method will attempt to recover authentication and rerun method.
311
326
*/
312
327
@ Override
313
- public void getTokens (final Result result , final String email ) {
328
+ public void getTokens (
329
+ final Result result , final String email , final boolean shouldRecoverAuth ) {
314
330
// TODO(issue/11107): Add back the checkAndSetPendingOperation once getTokens is properly
315
331
// gated from Dart code. Change result.success/error calls below to use finishWith()
316
332
if (email == null ) {
@@ -342,7 +358,31 @@ public void run(Future<String> tokenFuture) {
342
358
// instead of the value we cached during sign in. At least, that's
343
359
// how it works on iOS.
344
360
result .success (tokenResult );
345
- } catch (ExecutionException e ) {
361
+ } catch (final ExecutionException e ) {
362
+ if (e .getCause () instanceof UserRecoverableAuthException ) {
363
+ if (shouldRecoverAuth ) {
364
+ registrar
365
+ .activity ()
366
+ .runOnUiThread (
367
+ new Runnable () {
368
+ @ Override
369
+ public void run () {
370
+ UserRecoverableAuthException exception =
371
+ (UserRecoverableAuthException ) e .getCause ();
372
+ checkAndSetPendingOperation (METHOD_GET_TOKENS , result , email );
373
+ registrar
374
+ .activity ()
375
+ .startActivityForResult (
376
+ exception .getIntent (), REQUEST_CODE_RECOVER_AUTH );
377
+ }
378
+ });
379
+ } else {
380
+ result .error (ERROR_USER_RECOVERABLE_AUTH , e .getLocalizedMessage (), null );
381
+ }
382
+
383
+ return ;
384
+ }
385
+
346
386
Log .e (TAG , "Exception getting access token" , e );
347
387
result .error (ERROR_REASON_EXCEPTION , e .getCause ().getMessage (), null );
348
388
} catch (InterruptedException e ) {
@@ -439,10 +479,12 @@ private void finishWithError(String errorCode, String errorMessage) {
439
479
private static class PendingOperation {
440
480
final String method ;
441
481
final Result result ;
482
+ final Object data ;
442
483
443
- PendingOperation (String method , Result result ) {
484
+ PendingOperation (String method , Result result , Object data ) {
444
485
this .method = method ;
445
486
this .result = result ;
487
+ this .data = data ;
446
488
}
447
489
}
448
490
@@ -464,6 +506,19 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
464
506
} else if (pendingOperation != null && pendingOperation .method .equals (METHOD_INIT )) {
465
507
finishWithError (ERROR_REASON_CONNECTION_FAILED , String .valueOf (resultCode ));
466
508
}
509
+ return true ;
510
+ } else if (requestCode == REQUEST_CODE_RECOVER_AUTH ) {
511
+ if (resultCode == Activity .RESULT_OK
512
+ && pendingOperation != null
513
+ && pendingOperation .method .equals (METHOD_GET_TOKENS )) {
514
+ getTokens (pendingOperation .result , (String ) pendingOperation .data , false );
515
+ pendingOperation = null ;
516
+ } else {
517
+ finishWithError (
518
+ ERROR_FAILURE_TO_RECOVER_AUTH ,
519
+ "Failed attempt to recover authentication for user " + pendingOperation .data );
520
+ }
521
+
467
522
return true ;
468
523
}
469
524
0 commit comments