Google Contacts API - 无法刷新访问令牌

Posted

技术标签:

【中文标题】Google Contacts API - 无法刷新访问令牌【英文标题】:Google Contacts API - failing to refresh access token 【发布时间】:2014-12-04 20:28:10 【问题描述】:

我们将 Google Contacts API 与 OAuth2 结合使用:

credential = new GoogleCredential.Builder().setTransport(new NetHttpTransport())
        .setJsonFactory(new JacksonFactory())
        .setClientSecrets(OAuth2ClientId(), OAuth2ClientSecret())
        .addRefreshListener(new CredentialRefreshListener() ...);

myService = new ContactsService("My-App");
myService.setOAuth2Credentials(credential);

我们经常收到 GData 库无法处理的“401 Unauthorized”响应。 当WWW-Authenticate 标头丢失时,AuthenticationException 会抛出 NPE。

Caused by: java.lang.NullPointerException: No authentication header information
        at com.google.gdata.util.AuthenticationException.initFromAuthHeader(AuthenticationException.java:96) ~[gdata-core-1.0-1.47.1.jar:na]
        at com.google.gdata.util.AuthenticationException.<init>(AuthenticationException.java:67) ~[gdata-core-1.0-1.47.1.jar:na]
        at com.google.gdata.client.http.HttpGDataRequest.handleErrorResponse(HttpGDataRequest.java:608) ~[gdata-core-1.0-1.47.1.jar:na]
        at com.google.gdata.client.http.GoogleGDataRequest.handleErrorResponse(GoogleGDataRequest.java:564) ~[gdata-core-1.0-1.47.1.jar:na]
        at com.google.gdata.client.http.HttpGDataRequest.checkResponse(HttpGDataRequest.java:560) ~[gdata-core-1.0-1.47.1.jar:na]
        at com.google.gdata.client.http.HttpGDataRequest.execute(HttpGDataRequest.java:538) ~[gdata-core-1.0-1.47.1.jar:na]
        at com.google.gdata.client.http.GoogleGDataRequest.execute(GoogleGDataRequest.java:536) ~[gdata-core-1.0-1.47.1.jar:na]
        at com.google.gdata.client.Service.getFeed(Service.java:1135) ~[gdata-core-1.0-1.47.1.jar:1.47.1]
        at com.google.gdata.client.Service.getFeed(Service.java:1077) ~[gdata-core-1.0-1.47.1.jar:1.47.1]
        at com.google.gdata.client.GoogleService.getFeed(GoogleService.java:676) ~[gdata-core-1.0-1.47.1.jar:1.47.1]
        at com.google.gdata.client.Service.query(Service.java:1237) ~[gdata-core-1.0-1.47.1.jar:1.47.1]
        at com.google.gdata.client.Service.query(Service.java:1178) ~[gdata-core-1.0-1.47.1.jar:1.47.1]

我们设法添加了一个包装器,可以尝试在此 NPE 上刷新令牌。它有帮助,但是刷新失败的情况仍然很多:

credential.refreshToken() == false

当我们在调试器中运行refreshToken() 时,我们看到executeRefreshToken() 没有异常地执行,但返回了tokenResponse==null。结果refreshToken() 返回false 并且没有向侦听器传递任何原因

try 
    TokenResponse tokenResponse = executeRefreshToken();
    if (tokenResponse != null) 
      setFromTokenResponse(tokenResponse);
      for (CredentialRefreshListener refreshListener : refreshListeners) 
        refreshListener.onTokenResponse(this, tokenResponse);
      
      return true;
    
   catch (TokenResponseException e) 
    boolean statusCode4xx = 400 <= e.getStatusCode() && e.getStatusCode() < 500;
    // check if it is a normal error response
    if (e.getDetails() != null && statusCode4xx) 
      // We were unable to get a new access token (e.g. it may have been revoked), we must now
      // indicate that our current token is invalid.
      setAccessToken(null);
      setExpiresInSeconds(null);
    
    for (CredentialRefreshListener refreshListener : refreshListeners) 
      refreshListener.onTokenErrorResponse(this, e.getDetails());
    
    if (statusCode4xx) 
      throw e;
    
  
  return false;

我们的令牌始终适用于多个范围:https://www.googleapis.com/auth/userinfo.email https://www.google.com/m8/feeds https://www.googleapis.com/auth/calendar https://mail.google.com/ https://www.googleapis.com/auth/tasks

更新:我们已成功迁移到 People API,新的 API 也被我们的统一 API 联系人 API https://docs.aurinko.io/article/25-contacts-api使用。

【问题讨论】:

澄清一下,有时刷新有效,但有时无效?一旦刷新停止工作,它会再次工作吗? AKA,它是临时状态还是永久状态? 好的,我们已经对此进行了更多试验。联系人与日历和任务的行为似乎有点不同。看起来 Calendar&Tasks credential.refreshToken() 可能会无缘无故地失败,但如果您等待并重试,它实际上可能会刷新令牌。对于联系人,它是 NPE“无身份验证标头信息”,但如果您在某些情况下再次尝试,它最终会刷新令牌,而在其他情况下,它总是会失败。 我正在解决这个问题。我当然看到 NPE 被抛出,但仍在努力让刷新失败。 Eric 我更新了这个问题。您希望我可以与您分享一组访问/刷新令牌,这些令牌经常发生。我想知道令牌刷新是否受到谷歌的速率限制,因为如果我们尝试更多次刷新,我们通常最终会成功。 这可能是一个单独的问题,但我也注意到对于 Gmail/IMAP XOAUTH,Gmail/IMAP 经常拒绝新刷新的令牌。感觉刷新的令牌没有足够快地传播到 Gmail 服务器,当我们尝试使用它时他们还不知道它。同样,我们在使用令牌之前放置了一些包装器,这似乎有帮助。所以总的来说,我们的令牌刷新过程现在被包装到我们处理 NPE、重试然后等待然后再次重试的代码中。不是很优雅。 【参考方案1】:

Contacts API 目前存在一个错误,其中某些 HTTP User-Agent 字符串会导致 401 响应以 html 页面而不是 XML 响应的形式返回,并且缺少 AuthenticationException 类所在的 WWW-Authenticate 标头依靠。 GContacts-Java 是这些特殊的用户代理字符串之一。一种解决方法是在创建客户端后更改其用户代理:

ContactsService service = new ContactsService(applicationName);
service.getRequestFactory().setHeader("User-Agent", applicationName);

这应该消除 NPE,并允许客户端库自动检测过期令牌并自动刷新它们。

【讨论】:

谢谢埃里克。我会试一试。并将针对 Gmail 令牌问题提出另一个问题。 给你***.com/questions/26616591/… Eric,Google Contacts API 似乎又出现了 NullPointerException“无身份验证标头信息”问题。在实施您的解决方法后,我们已经有很长时间没有看到它了,但我们在所有日志中再次看到了这个错误。 这里也一样。现在每次通话都失败。任何知道如何处理这个问题的人都可以在某个地方放置一个带有解决方法的 GIST 吗? 现在是 2017 年,问题还没有解决。

以上是关于Google Contacts API - 无法刷新访问令牌的主要内容,如果未能解决你的问题,请参考以下文章

Google Contacts API - 获取图像

html Google Contacts API v3 Javascript示例

带有 Google JavaScript 客户端库的 Google Contacts API

从 Oauth2 Google Contacts API 获取用户信息

通过 OAuth 2.0 和私钥(即服务帐户)访问 Google Contacts Api

在 Google Contacts API 3.0 版上使用 oauth2 检索刷新令牌