Home » Android » android – UserRecoverableAuthException: NeedPermission

android – UserRecoverableAuthException: NeedPermission

Posted by: admin April 23, 2020 Leave a comment

Questions:

I tried to follow tutorial: https://developers.google.com/android/guides/http-auth.

Code:

token = GoogleAuthUtil.getToken(getApplicationContext(),
                        mEmail, mScope);

Manifest:

<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.NETWORK"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.INTERNET"/>

Errors:

01-17 18:37:38.230: W/System.err(3689): com.google.android.gms.auth.UserRecoverableAuthException: NeedPermission
01-17 18:37:38.230: W/System.err(3689):     at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
01-17 18:37:38.230: W/System.err(3689):     at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
01-17 18:37:38.230: W/System.err(3689):     at com.example.mgoogleauth.MainActivity$GetIOStreamTask.doInBackground(MainActivity.java:39)
01-17 18:37:38.230: W/System.err(3689):     at com.example.mgoogleauth.MainActivity$GetIOStreamTask.doInBackground(MainActivity.java:1)
01-17 18:37:38.230: W/System.err(3689):     at android.os.AsyncTask$2.call(AsyncTask.java:287)
01-17 18:37:38.230: W/System.err(3689):     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
01-17 18:37:38.230: W/System.err(3689):     at java.util.concurrent.FutureTask.run(FutureTask.java:137)
01-17 18:37:38.230: W/System.err(3689):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
01-17 18:37:38.230: W/System.err(3689):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
01-17 18:37:38.230: W/System.err(3689):     at java.lang.Thread.run(Thread.java:856)
How to&Answers:

Try following the Drive quickstart for Android, it is a step-by-step guide showing how to authorize and upload a file to Drive: https://developers.google.com/drive/quickstart-android

To be more specific, it looks like you are not catching the UserRecoverableException and triggering the intent to have the user authorize the app.
This is documented in the Google Play Services docs you linked and handled in the quickstart sample as follows:

...
} catch (UserRecoverableAuthIOException e) {
  startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
}
... 

Answer:

the method getAndUseAuthTokenBlocking() of the official GoogleAuthUtil tutorial explains pretty well how to handle the exception:

// Example of how to use the GoogleAuthUtil in a blocking, non-main thread context
   void getAndUseAuthTokenBlocking() {
       try {
          // Retrieve a token for the given account and scope. It will always return either
          // a non-empty String or throw an exception.
          final String token = GoogleAuthUtil.getToken(Context, String, String)(context, email, scope);
          // Do work with token.
          ...
          if (server indicates token is invalid) {
              // invalidate the token that we found is bad so that GoogleAuthUtil won't
              // return it next time (it may have cached it)
              GoogleAuthUtil.invalidateToken(Context, String)(context, token);
              // consider retrying getAndUseTokenBlocking() once more
              return;
          }
          return;
       } catch (GooglePlayServicesAvailabilityException playEx) {
         Dialog alert = GooglePlayServicesUtil.getErrorDialog(
             playEx.getConnectionStatusCode(),
             this,
             MY_ACTIVITYS_AUTH_REQUEST_CODE);
         ...
       } catch (UserRecoverableAuthException userAuthEx) {
          // Start the user recoverable action using the intent returned by
          // getIntent()
          myActivity.startActivityForResult(
                  userAuthEx.getIntent(),
                  MY_ACTIVITYS_AUTH_REQUEST_CODE);
          return;
       } catch (IOException transientEx) {
          // network or server error, the call is expected to succeed if you try again later.
          // Don't attempt to call again immediately - the request is likely to
          // fail, you'll hit quotas or back-off.
          ...
          return;
       } catch (GoogleAuthException authEx) {
          // Failure. The call is not expected to ever succeed so it should not be
          // retried.
          ...
          return;
       }
   }

Answer:

I had the same error, in my case I was using a wrong scope, I just change

https://www.googleapis.com/auth/plus.login

for

https://www.googleapis.com/auth/userinfo.profile

Answer:

On this docs page
https://developers.google.com/+/mobile/android/sign-in the example has a good explanation for this exception.

In particular, it looks like this line should be noted:

Requesting an authorization code will always throw UserRecoverableAuthException on the first call to GoogleAuthUtil.getToken

catch (UserRecoverableAuthException e) {
  // Requesting an authorization code will always throw
  // UserRecoverableAuthException on the first call to GoogleAuthUtil.getToken
  // because the user must consent to offline access to their data.  After
  // consent is granted control is returned to your activity in onActivityResult
  // and the second call to GoogleAuthUtil.getToken will succeed.
  startActivityForResult(e.getIntent(), AUTH_CODE_REQUEST_CODE);
  return;
}

Answer:

The docs have been recently updated and now work to support SDK M(requesting permission) and also show the OAuth dialog.

NOTE
Google Docs are often not up to date but they seem to pay attention when you report an issue. The example was updated with a week of me sending a feedback. So if you see a non-working example, send feedback!

https://developers.google.com/drive/v3/web/quickstart/android

Answer:

I found the answers here are passive solutions and not preventive.
According to my very short experience, UserRecoverableAuthException: NeedPermission will be thrown in the following 3 cases:

#1 Appropriate Scope is not requested when signing in to Google

Check that the scopes you are requesting are correct. Android should ask the user to allow permission after the authentication process via permission request dialog. This will prevent UserRecoverableAuthException when calling API.

GoogleSignInOptions o = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestScopes(new Scope(DriveScopes.DRIVE)) // request permission for Drive API
            .requestScopes(new Scope(SheetsScopes.SPREADSHEETS)) // request permission for Sheets API
            .requestEmail()
            .build();

#2 User has denied permission

The user has pushed “DENY” button on permission request dialog.

#3 User has rejected permissions from your app

As for Android 8.1.x, there is a menu where you can reject permissions for individual apps. Not sure about other versions, though.

Settings > Google > Connected apps

UserRecoverableAuthException thrown by #2 and #3 is unavoidable because those are the result of user activity. But isn’t that meaningless to show permission request dialog again with below code despite the user’s rejection?

} catch (UserRecoverableAuthIOException e) {
  startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
}