【问题标题】:Google Contacts API returning 401 even though the App is authenticated即使应用程序已通过身份验证,Google 联系人 API 也会返回 401
【发布时间】:2018-01-05 14:56:24
【问题描述】:

所以,我正在开发一个应用程序的 Android 端,我需要让用户将图像分享给他或她的 Google 联系人。为此,我显然需要用户授权才能访问他或她的 Google 联系人。我们实现的方法如下:

  • 在 Android 上,我使用的是 Google 的标准身份验证登录 API(使用 OAuth 2.0)
  • 在后端,我使用 Contacts API 检索联系人以将联系人发送到设备(我知道您可能想知道为什么我不直接在移动设备上使用联系人 API,但原始 API 安排已是别人写的)

所以,我只是按照 Google 的 OAuth 安排规定的流程进行操作。首先,我请求 API 授权授权代码。接下来,我发送此代码以获取访问令牌,然后最终将此访问令牌发送到我的服务器以通过联系人 API 进行调用。

我在请求用户授权时得到的请求访问屏幕是:

最后,Contacts API 发送以下请求(多个示例请求):

https://www.google.com/m8/feeds/groups/default/full?alt=json&access_token=ya29.GluXBJvdEgHD4XrieSgukvfvE0-XlNSwxDF8aaLMLHaXxE3Qd6G2Gy2h18WUOvOg--U0ffobyEzJiXIWtW2sRtdND8B6zcCVrvQm0kfOSBAJcdA4-9i6JZ7CH8y1&v=3.0

但是返回的响应是401。为什么会未经认证,我不明白。我已经调试了几个小时,并检查了 OAuth 过程是否完全正常。

还有一件事,我正在使用以下范围登录 Google:

GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestServerAuthCode(CLIENT_ID, true)
                .requestScopes(new Scope(Scopes.DRIVE_APPFOLDER), new Scope(Scopes.PROFILE))
                .requestEmail()
                .requestProfile()
                .requestIdToken(CLIENT_ID)
                .build();

有没有人可能是什么问题?

编辑:

我将提供更多详细信息来证明 API 密钥、OAuth ID 等的设置已正确完成。

  • 首先,我使用 Web 应用程序 OAuth 客户端 ID 向 Google 请求授权码,如官方指南 here 中所述。
  • 我的应用程序的 SHA-1 调试指纹已添加到我的项目中,因此 Google 不会对此抱怨。
  • 我在收到访问令牌后使用的重定向 URL 也已添加到我的 Web OAuth 客户端。
  • 我在服务器端使用的联系人 API 已为我的项目启用。

【问题讨论】:

    标签: android authentication oauth google-signin


    【解决方案1】:

    正如我所怀疑的,事实证明这是Scope 的问题。

    首先,让我告诉您,我通常喜欢 Google 出色的文档和 API,但在某些情况下,看似微不足道的细节被认为是理所当然的,开发人员想自己调试它们。我觉得,这是其中一个问题。

    因此,任何启动 Google 登录集成的人都将阅读官方文档和指南,并且在文档中提及和使用范围,但类似以下内容:

    GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                    .requestServerAuthCode(CLIENT_ID, true)
                    .requestScopes(new Scope(Scopes.DRIVE_APPFOLDER), new Scope(Scopes.PROFILE), new Scope(Scopes.EMAIL))
                    .requestEmail()
                    .requestProfile()
                    .requestIdToken(CLIENT_ID)
                    .build();
    

    这是来自一个示例 sn-p。当然,开发人员从示例 sn-p 复制并进一步扩展它。那么,作为开发人员,我该如何扩展呢?

    嗯,对于初学者来说,我显然可以通过更多范围,对吧?

    没错,我做到了,但仍然被这个问题所困扰。毕竟,我所要做的就是添加适当的范围权限。怎么会有人受到这样的打击?让我向您展示Scopes 类的样子:

    public final class Scopes {
        public static final java.lang.String PROFILE = "profile";
        public static final java.lang.String EMAIL = "email";
        public static final java.lang.String PLUS_LOGIN = "https://www.googleapis.com/auth/plus.login";
        public static final java.lang.String PLUS_ME = "https://www.googleapis.com/auth/plus.me";
        public static final java.lang.String GAMES = "https://www.googleapis.com/auth/games";
        public static final java.lang.String CLOUD_SAVE = "https://www.googleapis.com/auth/datastoremobile";
        public static final java.lang.String APP_STATE = "https://www.googleapis.com/auth/appstate";
        public static final java.lang.String DRIVE_FILE = "https://www.googleapis.com/auth/drive.file";
        public static final java.lang.String DRIVE_APPFOLDER = "https://www.googleapis.com/auth/drive.appdata";
        public static final java.lang.String FITNESS_ACTIVITY_READ = "https://www.googleapis.com/auth/fitness.activity.read";
        public static final java.lang.String FITNESS_ACTIVITY_READ_WRITE = "https://www.googleapis.com/auth/fitness.activity.write";
        public static final java.lang.String FITNESS_LOCATION_READ = "https://www.googleapis.com/auth/fitness.location.read";
        public static final java.lang.String FITNESS_LOCATION_READ_WRITE = "https://www.googleapis.com/auth/fitness.location.write";
        public static final java.lang.String FITNESS_BODY_READ = "https://www.googleapis.com/auth/fitness.body.read";
        public static final java.lang.String FITNESS_BODY_READ_WRITE = "https://www.googleapis.com/auth/fitness.body.write";
        public static final java.lang.String FITNESS_NUTRITION_READ = "https://www.googleapis.com/auth/fitness.nutrition.read";
        public static final java.lang.String FITNESS_NUTRITION_READ_WRITE = "https://www.googleapis.com/auth/fitness.nutrition.write";
    
        private Scopes() { /* compiled code */ }
    }
    

    所以,开发人员认为,

    “这些都是支持的范围,我所要做的就是参考一个 有效范围为Scopes.SCOPE_NAME"

    但你猜怎么着? Scopes 的故事还有更多内容!

    事实证明,您可以使用您所定位的 Google API 端点创建自己的范围。就我而言,我使用的是联系人 API,因此新代码如下所示:

    GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                    .requestServerAuthCode(CLIENT_ID, true)
                    .requestScopes(new Scope(Scopes.DRIVE_APPFOLDER), new Scope(Scopes.PROFILE), new Scope(Scopes.EMAIL), new Scope("https://www.google.com/m8/feeds/"))
                    .requestEmail()
                    .requestProfile()
                    .requestIdToken(CLIENT_ID)
                    .build();
    

    你有没有注意到额外的作用域:

    new Scope("https://www.google.com/m8/feeds/")
    

    是的,就是这样。这就是答案。

    那么,到底是什么让我的蜘蛛侠感觉刺痛了?

    您可以看到,在 Google 提供的上述权限页面中,它没有提及任何有关联系人的内容。但是,我想访问联系人。这是一个明显的危险信号!

    添加附加范围后,它显示“想要管理你的联系人”。(你的啊哈!时刻)

    最后一件事 -

    我是如何知道 Scope 的 URI 的?

    嗯,这很容易。您最有可能在您所针对的相应 API 的文档/指南中找到它。另外,请在提供的示例中注意它。因此,下次您遇到身份验证问题时,首先要寻找正确的范围。

    【讨论】:

      猜你喜欢
      • 2020-12-02
      • 2023-04-07
      • 1970-01-01
      • 2017-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-28
      • 2016-09-05
      相关资源
      最近更新 更多