it-swarm.com.de

Android Google+ Integration - wiederholte UserRecoverableAuthException

Wir haben diesbezüglich Google kontaktiert und wir sind im Chat

Das Problem scheint für Geräte außer Samsung-Telefone behoben zu sein.

Ich füge einer App eine Google+ Anmeldeoption gemäß der offiziellen Anweisungen hinzu. Sobald der Benutzer sein Konto ausgewählt hat, möchte ich, dass mein Server seine Google+ Profilinformationen abruft und sein Profil auf unserer Website entsprechend aktualisiert.

Der erste Teil - der Benutzer muss ein Google-Konto lokal auswählen - scheint gut zu funktionieren. Wenn ich versuche, ein Token für das ausgewählte Konto anzufordern, wird das Google-Dialogfeld "Auth" mit den entsprechenden Parametern angezeigt. Wenn ich jedoch die App mit diesem Dialogfeld autorisiere und das Token erneut anfrage, wirft GoogleAuthUtil.getToken(...) erneut eine UserRecoverableAuthException (NeedPermission, nicht GooglePlayServicesAvailabilityException) ab, und ich erhalte das gleiche Dialogfeld, in dem ich zur Genehmigung auffordert!

Dieses Verhalten tritt auf einem Samsung S3 mit Android 4.1.1 (mit 3 Google-Konten) und einem Acer A100 mit 4.0.3 auf. Es ist NICHT auf einem HTC Glacier mit 2.3.4 vorhanden. Stattdessen gibt mir das HTC Glacier einen gültigen Authentifizierungscode. Auf allen Geräten ist die neueste Version der Google Play-Dienste installiert und es werden unterschiedliche Google+ Konten verwendet.

Wer hat das schon mal gesehen? Wo kann ich mit dem Debuggen beginnen?

Ist hier der vollständige Code - ist etwas offensichtlich falsch?

public class MyGooglePlusClient {
private static final String LOG_TAG = "GPlus";
private static final String SCOPES_LOGIN = Scopes.PLUS_LOGIN + " " + Scopes.PLUS_PROFILE;
private static final String ACTIVITIES_LOGIN = "http://schemas.google.com/AddActivity";
private static MyGooglePlusClient myGPlus = null;
private BaseActivity mRequestingActivity = null;
private String mSelectedAccount = null;

/**
 * Get the GPlus singleton
 * @return GPlus
 */
public synchronized static MyGooglePlusClient getInstance() {
    if (myGPlus == null)
        myGPlus = new MyGooglePlusClient();
    return myGPlus;
}

public boolean login(BaseActivity requester) {
    Log.w(LOG_TAG, "Starting login...");
    if (mRequestingActivity != null) {
        Log.w(LOG_TAG, "Login attempt already in progress.");
        return false; // Cannot launch a new request; already in progress
    }

    mRequestingActivity = requester;
    if (mSelectedAccount == null) {
        Intent intent = AccountPicker.newChooseAccountIntent(null, null, new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE}, false,
                null, GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE, null, null);
        mRequestingActivity.startActivityForResult(intent, BaseActivity.REQUEST_GPLUS_SELECT);
    }
    return true;
}

public void loginCallback(String accountName) {
    mSelectedAccount = accountName;
    authorizeCallback();
}

public void logout() {
    Log.w(LOG_TAG, "Logging out...");
    mSelectedAccount = null;
}

public void authorizeCallback() {
    Log.w(LOG_TAG, "User authorized");

    AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
        @Override
        protected String doInBackground(Void... params) {
            String token = null;
            try {
                Bundle b = new Bundle();
                b.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES, ACTIVITIES_LOGIN);
                token = GoogleAuthUtil.getToken(mRequestingActivity,
                        mSelectedAccount,
                        "oauth2:server:client_id:"+Constants.GOOGLE_PLUS_SERVER_OAUTH_CLIENT
                        +":api_scope:" + SCOPES_LOGIN,
                        b);
            } catch (IOException transientEx) {
                // Network or server error, try later
                Log.w(LOG_TAG, transientEx.toString());
                onCompletedLoginAttempt(false);
            } catch (GooglePlayServicesAvailabilityException e) {
                Log.w(LOG_TAG, "Google Play services not available.");
                Intent recover = e.getIntent();
                mRequestingActivity.startActivityForResult(recover, BaseActivity.REQUEST_GPLUS_AUTHORIZE);
            } catch (UserRecoverableAuthException e) {
                // Recover (with e.getIntent())
                Log.w(LOG_TAG, "User must approve "+e.toString());
                Intent recover = e.getIntent();
                mRequestingActivity.startActivityForResult(recover, BaseActivity.REQUEST_GPLUS_AUTHORIZE);
            } catch (GoogleAuthException authEx) {
                // The call is not ever expected to succeed
                Log.w(LOG_TAG, authEx.toString());
                onCompletedLoginAttempt(false);
            }

            Log.w(LOG_TAG, "Finished with task; token is "+token);
            if (token != null) {
                authorizeCallback(token);
            }

            return token;
        }

    };
    task.execute();
}

public void authorizeCallback(String token) {
    Log.w(LOG_TAG, "Token obtained: "+token);
    // <snipped - do some more stuff involving connecting to the server and resetting the state locally>
}

public void onCompletedLoginAttempt(boolean success) {
    Log.w(LOG_TAG, "Login attempt "+(success ? "succeeded" : "failed"));
    mRequestingActivity.hideProgressDialog();
    mRequestingActivity = null;
}
}
28
Arkaaito

Ich habe dieses Problem schon eine Weile gehabt und eine richtige Lösung gefunden.

String token = GoogleAuthUtil.getToken(this, accountName, scopeString, appActivities);

Diese Zeile gibt entweder das einmalige Token zurück oder löst die UserRecoverableAuthException aus. Im Google Plus-Anmeldehandbuch wird die richtige Wiederherstellungsaktivität geöffnet.

startActivityForResult(e.getIntent(), RECOVERABLE_REQUEST_CODE);

Wenn die Aktivität mit dem Ergebnis zurückkehrt, wird sie mit wenigen Extras in der Absicht zurückgegeben. Dort befindet sich das neue Token:

@Override
protected void onActivityResult(int requestCode, int responseCode, Intent intent) {
    if (requestCode == RECOVERABLE_REQUEST_CODE && responseCode == RESULT_OK) {
        Bundle extra = intent.getExtras();
        String oneTimeToken = extra.getString("authtoken");
    }
}

Mit dem neuen oneTimeToken aus dem Zusatz können Sie sich an den Server senden, um eine Verbindung herzustellen.

Ich hoffe das hilft!

13
Calvin Park

Es ist zu spät, um darauf zu antworten, aber es kann für Menschen, die in Zukunft die gleiche Sorge haben, helfen.

Sie haben im Tutorial erwähnt, dass UserRecoverableAuthException Immer ausgelöst wird, wenn Sie GoogleAuthUtil.getToken () zum ersten Mal aufrufen. Zum zweiten Mal wird es gelingen.

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

ich habe den folgenden Code verwendet, um den Zugangscode von Google zu erhalten.

führe dieses new GetAuthTokenFromGoogle().execute(); einmal von public void onConnected(Bundle connectionHint) und einmal von protected void onActivityResult(int requestCode, int responseCode, Intent intent) aus

private class GetAuthTokenFromGoogle extends AsyncTask<Void, Integer, Void>{
        @Override  
        protected void onPreExecute()  
        {  

        }
        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub

            try {
                accessCode = GoogleAuthUtil.getToken(mContext, Plus.AccountApi.getAccountName(mGoogleApiClient), SCOPE);
                new ValidateTokenWithPhoneOmega().execute();
                Log.d("Token  -- ", accessCode);
            } 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 null;
            } catch (UserRecoverableAuthException e) {
                // Recover
                startActivityForResult(e.getIntent(), RC_ACCESS_CODE);
                e.printStackTrace();
            } catch (GoogleAuthException authEx) {
                // Failure. The call is not expected to ever succeed so it should not be
                // retried.
                authEx.printStackTrace();
                return null;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return null;  
        }

        @Override  
        protected void onPostExecute(Void result)  
        { 
        }
    }
5
appdroid

Ich bin dieses Problem mit einem webbasierten Login umgangen. Ich öffne eine URL wie diese

String url = "https://accounts.google.com/o/oauth2/auth?scope=" + Scopes.PLUS_LOGIN + "&client_id=" + webLoginClientId + "&response_type=code&access_type=offline&approval_Prompt=force&redirect_uri=" + redirect;

Die Weiterleitungs-URL verarbeitet dann die Antwort und kehrt zu meiner App zurück.

In Bezug auf meine Erkenntnisse zur Nutzung der Google Play-Dienste habe ich Folgendes festgestellt:

HTC One ist 3.1.59 (736673-30) - funktioniert nicht Galaxy Note ist 3.1.59 (736673-36) - funktioniert nicht Nexus S ist 3.1.59 (736673-34) - funktioniert 

Ich möchte gerne in den Chat eingebunden werden, der jedoch nicht hoch genug ist.

2
user2608643

Bearbeiten (6. August 2013): Dies scheint für mich behoben worden zu sein, ohne dass mein Code geändert wurde.

Das erste mögliche Problem, das ich sehen kann, ist, dass Sie GoogleAuthUtil.getToken() aufrufen, nachdem Sie den onConnected()-Callback erhalten haben. Dies ist ein Problem, da durch das Anfordern eines Autorisierungscodes für Ihren Server mit GoogleAuthUtil.getToken() always Ihren Benutzern ein Einverständnisbildschirm angezeigt wird. Sie sollten also nur einen Autorisierungscode für neue Benutzer erhalten. Damit neue Benutzer nicht auf zwei Einwilligungsfenstern angezeigt werden, müssen Sie einen Autorisierungscode abrufen und auf Ihrem Server austauschen, bevor Sie Verbindungsfehler von PlusClient beheben.

Stellen Sie zweitens sicher, dass Sie tatsächlich einen PlusClient und einen Autorisierungscode für Ihre Server benötigen. Sie benötigen nur dann einen PlusClient und einen Autorisierungscode, wenn Sie die Google-APIs vom Android-Client und von Ihrem Server aus anrufen möchten. Wie in diese Antwort erklärt.

Diese Probleme würden nur dazu führen, dass zwei Zustimmungsdialoge angezeigt werden (was eindeutig keine Endlosschleife ist) - sehen Sie mehr als zwei Zustimmungsdialoge? 

2
Lee

Ich habe das gleiche Problem in letzter Zeit erlebt - es scheint gerätespezifisch zu sein (ich hatte es jedes Mal auf einem S3, aber auf einem anderen S3, auf dem dasselbe Betriebssystem ausgeführt wurde, geschah es nicht, selbst mit demselben Konto). Meine Vermutung ist, dass es sich um einen Fehler in einer Client-App handelt, entweder in der G + App oder in der Google Play Services-App. Ich habe es geschafft, das Problem auf einem meiner Geräte zu lösen, indem ich das Gerät zurückgesetzt habe (ein Motorola Defy) und dann die Google Play Services-App erneut installiere. Dies ist jedoch eine völlig sinnlose Lösung, die den Benutzern mitzuteilen ist.

2
Adam

Ich hatte ein ähnliches Problem, als eine scheinbare Auth-Schleife {read: spamming} diese " Signing In ... " - und Permission-Anforderungsdialogfelder erzeugte und die besprochene Ausnahme auch wiederholt ausgab.

Das Problem erscheint in einem leicht modifizierten Beispielcode, den ich (und andere wie ich, ich vermute) "cargo-culted" from AndroidHive . Die Lösung, die für mich funktionierte, war es sollte sichergestellt sein, dass immer nur eine Task zum Abrufen von Hintergrundtoken im Hintergrund ausgeführt wird.

Damit mein Code leichter zu folgen ist, hier der Autorh-Fluss in meiner App (der fast identisch ist mit dem Beispielcode in AndoidHive): Activity -> onConnected(...) -> getProfileInformation() -> getOneTimeToken().

Hier wird getOneTimeToken() aufgerufen:

private void getProfileInformation() {
    try {
        if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null) {
            Person currentPerson = Plus.PeopleApi
                    .getCurrentPerson(mGoogleApiClient);
            String personName = currentPerson.getDisplayName();
            String personPhotoUrl = currentPerson.getImage().getUrl();
            String personGooglePlusProfile = currentPerson.getUrl();
            String email = Plus.AccountApi.getAccountName(mGoogleApiClient);
            getOneTimeToken(); // <-------
            ...

Hier ist meine getOneTimeToken():

private void getOneTimeToken(){
    if (task==null){
    task = new AsyncTask<Void, Void, String>() {
        @Override
        protected String doInBackground(Void... params) {
            LogHelper.log('d',LOGTAG, "Executing background task....");
            Bundle appActivities = new Bundle();
            appActivities.putString(
                         GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES,
                         ACTIVITIES_LOGIN);
            String scopes = "oauth2:server" + 
                            ":client_id:" + SERVER_CLIENT_ID + 
                            ":api_scope:" + SCOPES_LOGIN;
            String token = null;
            try {
                token = GoogleAuthUtil.getToken(
                        ActivityPlus.this,
                        Plus.AccountApi.getAccountName(mGoogleApiClient),
                        scopes,
                        appActivities
                );
            } catch (IOException transientEx) {
                /* Original comment removed*/
                LogHelper.log('e',LOGTAG, transientEx.toString());
            } catch (UserRecoverableAuthException e) {
                /* Original comment removed*/
                LogHelper.log('e',LOGTAG, e.toString());
                startActivityForResult(e.getIntent(), AUTH_CODE_REQUEST);
            } catch (GoogleAuthException authEx) {
                /* Original comment removed*/
                LogHelper.log('e',LOGTAG, authEx.toString());
            } catch (IllegalStateException stateEx){
                LogHelper.log('e',LOGTAG, stateEx.toString());
            }
            LogHelper.log('d',LOGTAG, "Background task finishing....");
            return token;
        }

        @Override
        protected void onPostExecute(String token) {
            LogHelper.log('i',LOGTAG, "Access token retrieved: " + token);
        }

    };
    }
    LogHelper.log('d',LOGTAG, "Task setup successful.");
    if(task.getStatus() != AsyncTask.Status.RUNNING){
        task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); //double safety!
    } else
        LogHelper.log('d',LOGTAG, 
                       "Attempted to restart task while it is running!");
}

Bitte beachten Sie, dass ich eine doppelte Sicherheit (wahrscheinlich redundant) gegen die mehrfach ausgeführte Aufgabe habe:

  1. if(task .getStatus() != AsyncTask.Status.RUNNING){...} - Stellt sicher, dass die Task nicht ausgeführt wird, bevor sie ausgeführt wird.
  2. task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);- stellt sicher, dass Kopien dieser Task "synchronisiert" werden (d. h. eine Warteschlange ist vorhanden, sodass nur eine Task dieses Typs zu einem bestimmten Zeitpunkt ausgeführt werden kann).

P.S. Kleinere Klarstellung: LogHelper.log('e',...) ist äquivalent zu Log.e(...) usw.

1
Dev-iL

sie sollten im UI-Thread aktiv werden

try {
    ....
} catch (IOException transientEx) {
    ....
} catch (final UserRecoverableAuthException e) {
    ....
    runOnUiThread(new Runnable() {
        public void run() {         
            startActivityForResult(e1.getIntent(), AUTH_CODE_REQUEST);
        }
    });
}
0
ling07

Hatte den gleichen Fehler mit der Endlosschleife der Erlaubnisanfrage. Für mich war es, weil die Zeit auf meinem Handy verschoben wurde. Wenn ich die Erkennungszeit automatisch überprüfe, ist dieser Fehler verschwunden. Hoffe das hilft!

0
user2410066