![](/img/trans.png)
[英]Google+ getToken() throws UserRecoverableAuthException: NeedPermission
[英]Android Google+ integration - repeated UserRecoverableAuthException
我們已就此與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;
}
}
我有一段時間沒有這個問題,並想出了一個合適的解決方案。
String token = GoogleAuthUtil.getToken(this, accountName, scopeString, appActivities);
該行將返回一次性令牌或將觸發UserRecoverableAuthException。 在Google Plus登錄指南中,它表示要打開正確的恢復活動。
startActivityForResult(e.getIntent(), RECOVERABLE_REQUEST_CODE);
當活動返回結果時,它將在intent中返回少量額外內容,這是新令牌所在的位置:
@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");
}
}
使用額外提供的新oneTimeToken,您可以提交到服務器以正確連接。
我希望這有幫助!
回復太晚了,但對未來有同樣關注的人可能會有所幫助。
他們在教程中提到,當你第一次調用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;
}
我使用下面的代碼從谷歌獲取訪問代碼。
執行這個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)
{
}
}
編輯(2013年8月6日):這似乎已經修復,我的代碼沒有任何更改。
我可以看到的第一個潛在問題是,在獲得onConnected()
回調后,您正在調用GoogleAuthUtil.getToken()
。 這是一個問題,因為使用GoogleAuthUtil.getToken()
為您的服務器請求授權代碼將始終向您的用戶顯示同意屏幕。 因此,您應該只獲取新用戶的授權代碼,並且為了避免向新用戶顯示兩個同意屏幕,您必須獲取授權代碼並在解決來自PlusClient
任何連接失敗之前在服務器上進行交換。
其次,確保您確實需要服務器的PlusClient和授權代碼。 如果您打算從Android客戶端和服務器調用Google API,則只需要獲取PlusClient和授權代碼。 如本答案所述 。
這些問題只會導致顯示兩個同意對話框(顯然不是無限循環) - 您是否看到了兩個以上的同意對話框?
我通過使用基於Web的登錄解決了這個問題。 我打開這樣的網址
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;
重定向url然后處理響應並返回到我的應用程序。
關於我使用Google Play服務的調查結果,我發現:
HTC One是3.1.59(736673-30) - 不工作Galaxy Note是3.1.59(736673-36) - 不工作Nexus S是3.1.59(736673-34) - 工程
而且我想參與正在進行的聊天,但是我沒有足夠的聲譽來這樣做。
我最近遇到了同樣的問題 - 它似乎是特定於設備的(我在每個S3上都會發生這種情況,但是在運行相同操作系統的另一個S3上,它沒有發生,即使使用相同的帳戶)。 我的預感是它是客戶端應用程序中的一個錯誤,無論是G +應用程序還是Google Play服務應用程序。 我設法通過工廠重置它(摩托羅拉Defy)解決了我的一個設備上的問題,然后重新安裝了Google Play服務應用程序,但這是一個完全沒用的解決方案告訴用戶。
我有一個類似的問題,其中一個明顯的auth循環不斷創建{read:spamming}這些“ Signing In ... ”和Permission請求對話框,同時也反復給出討論的異常。
這個問題出現在一些稍微修改過的示例代碼中,我(以及其他像我一樣,我懷疑)來自AndroidHive的 “貨物剔除” 。 對我有用的解決方案是確保在任何給定時間只有一個后台令牌檢索任務在后台運行。
為了使我的代碼更容易理解,這里是我的應用程序中的auth流程(幾乎與AndoidHive上的示例代碼相同): Activity
- > onConnected(...)
- > getProfileInformation()
- > getOneTimeToken()
。
這是調用getOneTimeToken()
的地方:
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(); // <-------
...
這是我的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!");
}
請注意,我對多次執行的任務有{可能冗余}雙重安全性:
if(task .getStatus() != AsyncTask.Status.RUNNING){...}
- 在嘗試執行任務之前確保任務未運行。 task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
- 確保此任務的副本是“同步的”(即隊列已到位,以便在給定時間只能執行此類任務)。 PS Minor澄清: LogHelper.log('e',...)
等同於Log.e(...)
等。
你應該在UI線程中啟動
try {
....
} catch (IOException transientEx) {
....
} catch (final UserRecoverableAuthException e) {
....
runOnUiThread(new Runnable() {
public void run() {
startActivityForResult(e1.getIntent(), AUTH_CODE_REQUEST);
}
});
}
有無限循環的權限請求有相同的錯誤。 對我來說,這是因為手機上的時間被轉移了。 當我自動檢查檢測時間時,這個bug就消失了。 希望這可以幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.