Google plus for android: Invalid Credentials

2019-06-16 13:05发布

问题:

I am using the followin code to get an Access Token, after connecting to Google+, to get profile info and e-mail:

String sAccessToken = GoogleAuthUtil.getToken(this,mPlusClient.getAccountName() + "",
                          "oauth2:" + Scopes.PLUS_PROFILE + " 
                          https://www.googleapis.com/auth/userinfo.profile 
                          https://www.googleapis.com/auth/userinfo.email");

This Access Token I am not storing and trying to re-use. Instead I ask for an Access Token everytime, expecting a new(different) Token, so that I don't need to check whether its expired etc..

But I am getting the same Access Token every time I ask for. I uninstalled the app, cleared data of Google+ App and still I get the same Access Token.

The Actual problem is, using this Access Token gives a 401 error - "message": "Invalid Credentials"

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "authError",
    "message": "Invalid Credentials",
    "locationType": "header",
    "location": "Authorization"
   }
  ],
  "code": 401,
  "message": "Invalid Credentials"
 }
}

This is a response on browser.

On a device I get this in Logcat:

02-25 02:09:46.919: W/System.err(3022): java.io.FileNotFoundException: https://www.googleapis.com/oauth2/v1/userinfo
02-25 02:09:46.929: W/System.err(3022):     at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:521)
02-25 02:09:46.929: W/System.err(3022):     at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:258)
02-25 02:09:46.929: W/System.err(3022):     at com.example.gmaillogin.MainActivity$connectAsyncTask.doInBackground(MainActivity.java:269)
02-25 02:09:46.929: W/System.err(3022):     at com.example.gmaillogin.MainActivity$connectAsyncTask.doInBackground(MainActivity.java:1)
02-25 02:09:46.929: W/System.err(3022):     at android.os.AsyncTask$2.call(AsyncTask.java:185)
02-25 02:09:46.929: W/System.err(3022):     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
02-25 02:09:46.929: W/System.err(3022):     at java.util.concurrent.FutureTask.run(FutureTask.java:138)
02-25 02:09:46.929: W/System.err(3022):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
02-25 02:09:46.929: W/System.err(3022):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
02-25 02:09:46.929: W/System.err(3022):     at java.lang.Thread.run(Thread.java:1019)

Line 269:

urlConnection = (HttpURLConnection) url.openConnection();

Complete Code:

URL url = new URL("https://www.googleapis.com/oauth2/v1/userinfo");
String sAccessToken = GoogleAuthUtil.getToken(
                        MainActivity.this,
                        mPlusClient.getAccountName() + "",
                        "oauth2:" + Scopes.PLUS_PROFILE + " 
                                            https://www.googleapis.com/auth/userinfo.profile 
                                            https://www.googleapis.com/auth/userinfo.email");

            Log.d("sAccessToken = ", "" + sAccessToken);
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setRequestProperty("Authorization", "Bearer "
                    + sAccessToken);

            BufferedReader r = new BufferedReader(new InputStreamReader(
                    urlConnection.getInputStream(), "UTF-8"));
            StringBuilder total = new StringBuilder();
            String line;
            while ((line = r.readLine()) != null) {
                total.append(line);
            }
            line = total.toString();

EDIT:

This is definitely because the Access Token is expired. Which was expected, but I thought I will get a new Access Token everytime I call.

But as said here:

If the android device already has an auth token (for the particular GData service you're trying to access), it will be returned to you

So, I have to do this:

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;
          }

like the docs say

But how can I check if the Access Token is expired or invalid?

By catching the Exception - Is the only solution?

Actually the Exception is java.io.FileNotFoundException which doesn't make sense.

回答1:

Covering the bases here: Did you register a Google APIs Console project and create a OAuth 2.0 client ID for your Android app, by specifying your app's package name and the SHA-1 fingerprint of your certificate (test or prod)?

Often the 401 invalid credentials message is due to the APIs Console project either not being configured or one of the settings being invalid.

The steps to walk through that set up process specifically for the Google+ SDK are here: https://developers.google.com/+/mobile/android/getting-started

Edit

The PlusClient can automatically handle your tokens for you. I'd suggest using the methods that the PlusClient provides and allowing the client to manage the tokens rather than doing it manually. If you need email or profile data via the PlusClient see PlusClient.loadPerson() and for email PlusClient.getAccountName(), more details at https://developers.google.com/+/mobile/android/people



回答2:

I had the similar problem. To solve the issue with Access Token expiration, I decode the token on client-side, and check "exp" field in token payload. In that way, I can recognize token expiration before sending to server. More details are on this blog post