Google+ getToken() 抛出 UserRecoverableAuthException: NeedPermission

Google+ getToken() throws UserRecoverableAuthException: NeedPermission

我想获取授权码,以便为我的应用程序启用服务器端 API 访问权限。我在 Unity3D 中使用 google Play games services plugin for Unity 执行此过程。我有从 GoogleAuthUtils class:

调用本机 getToken() 函数的函数
 public string GetToken() {
            string token = null;
            Debug.Log("Before RetrieveUserEmail");
            string email = RetrieveUserEmail() ?? "NULL";
            Debug.Log("After RetrieveUserEmail email: " + email);
            string scope = "oauth2:server:client_id:" + "666666666666-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com"
                + ":api_scope:" + "https://www.googleapis.com/auth/plus.login";
            using (AndroidJavaClass jc_unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"),
                jc_gau = new AndroidJavaClass("com.google.android.gms.auth.GoogleAuthUtil")) {
                using(AndroidJavaObject jo_Activity = jc_unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) {
                    token = jc_gau.CallStatic<string>("getToken", jo_Activity, email, scope);
                }
            }
            Debug.Log("Token " + token);
            return token;
        }

但我得到 AndroidJavaException: com.google.android.gms.auth.UserRecoverableAuthException: NeedPermission

这个函数看起来不错,因为它与

一起使用
string scope = "audience:server:client_id:" + "666666666666-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com"

和 returns 观众令牌。

我不认为我做错了什么。

有什么建议吗?

或者您可以澄清一下,那是使用 URL 调用:

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/plus.login&client_id=666666666666-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com&response_type=code&redirect_uri=http://someurl.com/oauth2callback

将我重定向到 url 喜欢

http://someurl.com/oauth2callback?code=4/YUVerNRxRQ8_XHPJ4USfjhYLCZ-fKoQyD1v5H_cZH_o.IvzKlyDEVOcVrjMoGjtSfTpyjkcImAI&authuser=0&num_sessions=1&session_state=14ac991a51396ecb690abac27e676846c7a8297e..c560&prompt=none

参数代码 = 4/YUVer...

该代码是否与我尝试通过 Unity 功能获得的代码相同?

提前致谢,如有任何帮助,我将不胜感激。

一些值得检查的事情:

  • 您的重定向 URI 是什么?

当您从 Android 发出请求时,您的重定向 URI 将是特定于设备的重定向 URI,urn:ietf:wg:oauth:2.0:oob.

  • 您(或您的用户)要取消授权步骤吗?

您收到的错误表明用户在授权应用程序时点击了取消(或从未点击过确认)。如果您获得了授权代码(4/YUY.... 字符串),则不应发生这种情况。

  • 您交换的代币是正确的代币吗?

authZ、访问令牌、刷新令牌和授权代码有 3 种重要的 OAuth 响应类型。如果您正在尝试交换访问令牌(或刷新令牌)作为授权代码,则响应将没有任何意义。确保您使用的是来自 getToken 的授权代码,并在执行令牌交换时传递正确匹配的重定向 URI。

回到你的评论,这是有道理的:

is that code the SAME one as that I am trying to get via Unity function?

您在 OAuth 服务器重定向后获得的代码是您想要的代码,它是从 getToken 中的 Android 返回的。有关获取/交换代码的更多信息,请查看 Google+ Android Client & Server Sign-In.

上的这篇博客文章

有关如何获取代码的演示,请参阅 Haiku+ Android client。相关代码为:

public String getCodeSynchronous() throws GoogleAuthException, CodeException {
    StringBuilder scopeString = new StringBuilder("");
    for (String scope : Constants.SCOPES) {
        scopeString.append(" ").append(scope);
    }
    String scope = new StringBuilder("oauth2:server:client_id:")
            .append(Constants.SERVER_CLIENT_ID)
            .append(":api_scope:")
            .append(scopeString.toString())
            .toString();
    Bundle appActivities = new Bundle();
    String types = TextUtils.join(" ", Constants.ACTIONS);
    appActivities.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES, types);

    String code = null;
    try {
        code = GoogleAuthUtil.getToken(
                mContext,
                mAccountName,
                scope,
                appActivities);
        // Immediately invalidate so we get a different one if we have to try again.
        GoogleAuthUtil.invalidateToken(mContext, code);
    } catch (IOException e) {
        Log.e(TAG, e.getMessage(), e);
        throw new CodeException("Error: could not establish connection to server.");
    }

    return code;
}

如何在服务器端交换代码,参见Haiku+ Java server:

try {
  // Upgrade the authorization code into an access and refresh token.
  return new GoogleAuthorizationCodeTokenRequest(HaikuPlus.TRANSPORT,
    HaikuPlus.JSON_FACTORY,
    getClientId(),
    getClientSecret(),
    authorization,
    redirectUri).execute();

} catch (TokenResponseException e) {
  //Failed to exchange authorization code.
  logger.log(Level.INFO, "Failed to exchange auth code; return 400", e);
  response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
  return null;
} catch (IOException e) {
  logger.log(Level.INFO, "Failed to exchange auth code; return 400", e);
  response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
  return null;
}

我遇到了同样的问题。我就是这样解决的。

第 1 步: 我按照 Try Sign-In for Android. So I had Sign-In working. I had to follow this tutorial Start Integrating Google Sign-In into Your Android App 的 Google 文档创建了 "Client ID for Android"

第 2 步: 同样,在我创建"Client ID for Android"的同时,我不得不创建"Client ID for Web"。这一步给了我 "Client ID for Web",我稍后会用到它。

第 3 步:

按照本教程获取令牌 ID "Authenticate with a backend server"。在登录时的 onConnected 方法中,添加

GetIdTokenTask getIdTokenTask = new GetIdTokenTask();
getIdTokenTask.execute();

您可以按照教程找到 GetIdTokenTask class 使用后端服务器进行身份验证”。这是 onConnected 方法的示例。

@Override
public void onConnected(Bundle bundle) {
    // onConnected indicates that an account was selected on the device, that the selected
    // account has granted any requested permissions to our app and that we were able to
    // establish a service connection to Google Play services.
    Log.d(TAG, "onConnected:" + bundle);
    mShouldResolve = false;

    if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null) {
        Person currentPerson = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient);

        Log.d(TAG, "onConnected:" + "start GetIdTokenTask");

        GetIdTokenTask getIdTokenTask = new GetIdTokenTask();
        getIdTokenTask.execute();
    } else {
        Log.d(TAG, "onConnected:" + "Cannot get current person's information.");
    }
    // Show the signed-in UI
    showSignedInUI();
}

第 4 步: 配置 SERVER_CLIENT_ID 我们可以从 GetIdTokenTask class[中的第 2 步获得=14=]

这里有一个 GetIdTokenTask 的例子。

private class GetIdTokenTask extends AsyncTask<Void, Void, String> {

    @Override
    protected String doInBackground(Void... params) {
        String accountName = Plus.AccountApi.getAccountName(mGoogleApiClient);
        Account account = new Account(accountName, GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
        String scopes = "audience:server:client_id:" + SERVER_CLIENT_ID; // Not the app's client ID.
        try {
            return GoogleAuthUtil.getToken(getApplicationContext(), account, scopes);
        } catch (IOException e) {
            Log.e(TAG, "Error retrieving ID token.", e);
            return null;
        } catch (GoogleAuthException e) {
            Log.e(TAG, "Error retrieving ID token.", e);
            return null;
        }
    }

    @Override
    protected void onPostExecute(String result) {
        Log.i(TAG, "ID token: " + result);
        if (result != null) {
            // Successfully retrieved ID Token
            // ...
        } else {
            // There was some error getting the ID Token
            // ...
        }
    }

}