Android Google+集成 - 重复UserRecoverableAuthException

Ark*_*ito 28 android oauth-2.0 google-plus

我们已就此与Google联系,我们正在聊天

对于三星手机之外的设备,这个问题似乎已经解决.

我根据官方说明向应用添加了Google+登录选项.一旦用户选择了他们的帐户,我希望我的服务器检索他们的Google+个人资料信息并在我们的网站上更新他们的个人资料以匹配.

第一部分 - 让用户在本地选择一个Google帐户 - 似乎工作正常.当我尝试为所选帐户请求令牌时,Google身份验证对话框会显示相应的参数; 但是,当我使用该对话框授权应用程序并重新请求令牌时,GoogleAuthUtil.getToken(...)再次抛出UserRecoverableAuthException(NeedPermission而不是GooglePlayServicesAvailabilityException),我得到同样的对话框,要求我批准!

运行Android 4.1.1的Samsung S3(带有3个Google帐户)和运行4.0.3的Acer A100会出现此问题.它不存在于运行2.3.4的HTC Glacier上.相反,HTC Glacier为我提供了有效的身份验证代码. 所有设备都安装了最新版本的Google Play服务,并使用不同的Google+帐户.

有人见过这个吗?我在哪里可以开始调试?

这是完整的代码 - 显然是什么错误?

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;
}
}
Run Code Online (Sandbox Code Playgroud)

Cal*_*ark 13

我有一段时间没有这个问题,并想出了一个合适的解决方案.

String token = GoogleAuthUtil.getToken(this, accountName, scopeString, appActivities);
Run Code Online (Sandbox Code Playgroud)

该行将返回一次性令牌或将触发UserRecoverableAuthException.在Google Plus登录指南中,它表示要打开正确的恢复活动.

startActivityForResult(e.getIntent(), RECOVERABLE_REQUEST_CODE);
Run Code Online (Sandbox Code Playgroud)

当活动返回结果时,它将返回意图中的少量额外内容,这是新令牌所在的位置:

@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");
    }
}
Run Code Online (Sandbox Code Playgroud)

使用额外提供的新oneTimeToken,您可以提交到服务器以正确连接.

我希望这有帮助!


app*_*oid 5

回复太晚了,但对未来有同样关注的人可能会有所帮助.

他们在教程中提到,当你第一次调用GoogleAuthUtil.getToken()时,它总会抛出UserRecoverableAuthException.第二次它会成功.

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;
}
Run Code Online (Sandbox Code Playgroud)

我使用下面的代码从谷歌获取访问代码.

new GetAuthTokenFromGoogle().execute();public void onConnected(Bundle connectionHint) 一次执行此操作一次protected void onActivityResult(int requestCode, int responseCode, Intent intent)

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)  
        { 
        }
    }
Run Code Online (Sandbox Code Playgroud)


Lee*_*Lee 2

编辑(2013 年 8 月 6 日):这似乎已为我修复,无需对我的代码进行任何更改。

我看到的第一个潜在问题是您GoogleAuthUtil.getToken()在收到onConnected()回调后打电话。这是一个问题,因为为您的服务器请求授权代码GoogleAuthUtil.getToken()始终向您的用户显示同意屏幕。因此,您应该只为新用户获取授权代码,并且为了避免向新用户显示两个同意屏幕,您必须获取授权代码并在服务器上交换它,然后再解决PlusClient.

其次,确保您的服务器确实需要 PlusClient 和授权码。如果您打算从 Android 客户端和服务器调用 Google API,则只需获取 PlusClient 和授权代码。正如这个答案中所解释的。

这些问题只会导致显示两个同意对话框(这显然不是无限循环) - 您是否看到两个以上的同意对话框?