简体   繁体   English

获取Google一次性授权码

[英]Getting Google one-time authorization code

I am having trouble getting a one-time authorization code from Google. 我无法从Google获取一次性授权码。 I am attempting to get the authorization code from an Android client so that I can send it to my Rails backend (web client). 我试图从Android客户端获取授权代码,以便我可以将其发送到我的Rails后端(Web客户端)。

In my Google Cloud Developer Console I have an application with two Client IDs: 在我的Google Cloud Developer Console中,我有一个带有两个客户端ID的应用程序:

  1. Client ID for web application (for my rails backend) Web应用程序的客户端ID(用于我的rails后端)

  2. Client ID for Android application (for my android client). Android应用程序的客户端ID(对于我的Android客户端)。 The SHA1 used is from ~/.android/debug.keystore 使用的SHA1来自〜/ .android / debug.keystore

Suppose the Web Application Client ID is 12345.apps.googleusercontent.com 假设Web应用程序客户端ID为12345.apps.googleusercontent.com

Suppose the Android Client ID is 67890.apps.googleusercontent.com 假设Android客户端ID为67890.apps.googleusercontent.com

This is some of my code: 这是我的一些代码:

private final static String WEB_CLIENT_ID = "12345.apps.googleusercontent.com";
private final static String GOOGLE_CALENDAR_API_SCOPE = "audience:server:client_id:" + WEB_CLIENT_ID;

private void getAndUseAuthToken(final String email) {
        AsyncTask task = new AsyncTask<String, Void, String>() {
            @Override
            protected String doInBackground(String... emails) {
                try {
                    return GoogleAuthUtil.getToken(AddExternalCalendarsActivity.this, emails[0], GOOGLE_CALENDAR_API_SCOPE);
                } catch (UserRecoverableAuthException e) {
                    startActivityForResult(e.getIntent(), IntentConstants.REQUEST_GOOGLE_AUTHORIZATION);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (GoogleAuthException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                return null;
            }

            @Override
            protected void onPostExecute(String authToken) {
                if (authToken != null) {
                    saveTokenAndGetCalendars(email, authToken);
                }
            }
        };

        String[] emails = new String[1];
        emails[0] = email;
        task.execute(emails);
    }

Some additional notes 一些额外的说明

  • I am hitting the GoogleAuthException and receiving "Unknown" as the message detail 我正在点击GoogleAuthException并收到“Unknown”作为消息详细信息

  • I'm unable to add additional members in the permissions of the Google Cloud Console for this project - when adding a new member a popup appears with "Server Error. Whoops! Our bad.". 我无法在此项目的Google云端控制台权限中添加其他成员 - 添加新成员时,弹出窗口显示“服务器错误。哎呀!我们不好。”。 I have sent feedback to Google twice. 我已经两次向Google发送反馈。

  • I'm referring to this documentation . 我指的是这个文档 Notice the quote below. 请注意下面的引用。 By "fixed", are they saying that I do not need to prepend audience:server:client_id in my GOOGLE_CALENDAR_API_SCOPE variable? 通过“修复”,他们是否说我不需要在GOOGLE_CALENDAR_API_SCOPE变量中添加受众:server:client_id I've tried both with and without and still getting the same GoogleAuthException. 我已经尝试过和不使用,但仍然得到相同的GoogleAuthException。

In this situation, the Android app can call the GoogleAuthUtil.getToken() method on behalf of any of the Google accounts on the device, and with a scope argument value of audience:server:client_id:9414861317621.apps.googleusercontent.com. 在这种情况下,Android应用可以代表设备上的任何Google帐户调用GoogleAuthUtil.getToken()方法,并且范围参数值为audience:server:client_id:9414861317621.apps.googleusercontent.com。 The prefix audience:server:client_id: is fixed, and the rest of the scope string is the client ID of the web component. 前缀audience:server:client_id:是固定的,作用域字符串的其余部分是Web组件的客户端ID。

  • If I use this scope, I can authenticate with google from device. 如果我使用此范围,我可以使用谷歌设备进行身份验证。 However, the documentation I've read suggests that I need to use the server web client id (which is in the same google console project as the android client id) in the scope in order for the server to hit the google api on behalf of the user who authorized it on the android client: 但是,我读过的文档表明,我需要在范围内使用服务器Web客户端ID(与Android客户端ID在同一个google控制台项目中),以便服务器代表google api在Android客户端上授权它的用户:

    private final static String GOOGLE_CALENDAR_API_SCOPE = "oauth2:https://www.googleapis.com/auth/calendar";

UPDATE 1 更新1

I originally added in answer: The reason for my first problem - I am hitting the GoogleAuthException and receiving "Unknown" as the message detail - was a mistake I made when adding the android client id in the cloud console. 我最初在回答中添加: 我的第一个问题的原因 - 我正在点击GoogleAuthException并接收“Unknown”作为消息详细信息 - 这是我在云控制台中添加android客户端ID时犯的错误。 The SHA1 was correct but I did not type the package name correctly. SHA1是正确的但我没有正确输入包名称。 I used com.company.app when my android package is actually com.company.android.app. 当我的android包实际上是com.company.android.app时,我使用了com.company.app。 The code in the original question works fine. 原始问题中的代码运行正常。 Just make sure you have all the necessary clients in your Google Cloud Console project. 只需确保您的Google Cloud Console项目中包含所有必需的客户。

But another problem still exists. 但另一个问题仍然存在。 When I send the one-time authorization token returned from GoogleAuthUtil.getToken() to the Rails backend, and then try to exchange it for an access_token and refresh_token, I get the follow: 当我将从GoogleAuthUtil.getToken()返回的一次性授权令牌发送到Rails后端,然后尝试将其交换为access_token和refresh_token时,我得到以下内容:

Signet::AuthorizationError:
  Authorization failed.  Server message:
  {
      "error" : "invalid_grant"
  }

This google documentation and several SO posts suggests that I need to set access_type=offline . 这个谷歌文档和几个SO帖子表明我需要设置access_type=offline But I think that is when you are requesting the one-time authorization code and offline access from a Web Server. 但我认为,当您从Web服务器请求一次性授权代码和脱机访问时。 I'm trying to request the one-time authorization code from an Android client and send it to the web server. 我正在尝试从Android客户端请求一次性授权代码并将其发送到Web服务器。

Is this possible with GoogleAuthUtil.getToken() ? 这可能与GoogleAuthUtil.getToken()有关吗?

UPDATE 2 更新2

Google Plus login must be in the scope even if you're only trying to access the calendar: 即使您只是尝试访问日历,Google Plus登录也必须在范围内:

private final static String GOOGLE_CALENDAR_API_SCOPE = "oauth2:server:client_id:" + WEB_CLIENT_ID + ":api_scope:https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/calendar";

This SO post was helpful. 这篇SO帖子很有帮助。 Also, Google's Cross Client identity documentation does state: 此外, Google的Cross Client身份文档确实说明:

[Note: This policy in being rolled out gradually. [注:此政策正在逐步推出。 For the moment, when access tokens are involved, it only applies when the requested scopes include https://www.googleapis.com/auth/plus.login.] 目前,当涉及访问令牌时,它仅适用于请求的范围包括https://www.googleapis.com/auth/plus.login。

I'll summarize in an answer if the token exchange works on Rails backend. 如果令牌交换在Rails后端工作,我会在答案中总结。

Two things solved this for me: 有两件事为我解决了这个问题:

  1. Make sure the Android and Web Client IDs are setup in correctly in the same Google Cloud Console project. 确保在同一个Google Cloud Console项目中正确设置了Android和Web客户端ID。

  2. Use the correct scope. 使用正确的范围。 Plus login is required even if you're only accessing the calendar api: 即使您只访问日历api,也需要登录:

    // the id of the web server that is exchanging the auth code for access and refresh tokens //正在交换用于访问和刷新令牌的身份验证代码的Web服务器的ID

    private final static String WEB_CLIENT_ID = "12345.apps.googleusercontent.com"; private final static String WEB_CLIENT_ID =“12345.apps.googleusercontent.com”; private final static String GOOGLE_CALENDAR_API_SCOPE = "oauth2:server:client_id:" + WEB_CLIENT_ID + ":api_scope: https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/calendar "; private final static String GOOGLE_CALENDAR_API_SCOPE =“oauth2:server:client_id:”+ WEB_CLIENT_ID +“:api_scope: https//www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/calendar ” ;

Here's what I've done: 这就是我所做的:

final String authToken = GoogleAuthUtil.getTokenWithNotification (this.context, account.name, "oauth2:" + AuthUtils.profileScope, new Bundle (),
                Contract.authority, new Bundle ());

But you plain getToken should work the same for a foreground activity. 但是你的普通getToken应该对前台活动的工作方式相同。

Take this token, send it to the server in an a way you can use it like an HTTP header (over HTTPS). 获取此令牌,将其发送到服务器,您可以像HTTP标头一样使用它(通过HTTPS)。 As long as the server scope is a subset of the scope used to acquire the token, you shouldn't have a problem. 只要服务器作用域是用于获取令牌的作用域的子集,就不应该有问题。 The server uses the server id and the android client uses the android client id. 服务器使用服务器ID,android客户端使用android客户端ID。

You should set your scope to either: 您应该将范围设置为:

oauth2:https://www.googleapis.com/auth/calendar

or 要么

oauth2:https://www.googleapis.com/auth/calendar.readonly

See https://developers.google.com/google-apps/calendar/auth 请参阅https://developers.google.com/google-apps/calendar/auth

Edit: OK I see what you are trying to do. 编辑:好的,我看到你要做的事情。 The scope value is a space separated list so you would likely need to append audience:server:client_id: to your scope like: 范围值是一个以空格分隔的列表,因此您可能需要将受众:server:client_id:附加到您的范围,如:

"oauth2:https://www.googleapis.com/auth/calendar audience:server:client_id:12345-your-web-component-client-id"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM