刷新访问令牌时“invalid_grant”错误的情况?

4 投票
1 回答
2938 浏览
提问于 2025-04-27 22:01

最近我为这个问题烦恼得快抓狂了。

一些背景信息

  • 我们使用oauth2client库来管理用户的令牌。这些令牌用于定期和同时执行各种后台任务。
  • 每当某个任务准备为用户运行时,我们会从存储中获取凭证对象,如果令牌快到期(大约5分钟内),就会进行刷新。否则,就会重复使用当前的访问令牌。
  • 有时候,一个用户的多个任务会同时运行。
  • 一开始一切都很正常,令牌也能正常刷新。
  • 但偶尔在尝试刷新时,会出现一个“invalid_grant”的错误,这会导致存储中的刷新令牌完全失效。(我希望通过这个问题来弄清楚这种情况是怎么发生的)

我查了一下,发现有很多关于这个问题的讨论和报告。但到目前为止,我找到的都不适用于我们的情况。我会列出我到目前为止查看过的一些情况:

  1. 用户撤销了权限

这个是最明显的,也是文档中描述得最多的,容易重现,但不幸的是,在我们的案例中,用户(或者我们自己在测试时)根本没有撤销权限。

  1. 刷新一个“旧”的访问令牌

起初我以为每个用户一次只能有一个有效的访问令牌。这个说法是错的,在OAuth2 Playground上得到了验证。

  1. 用户有太多活跃的访问令牌

每个用户每个客户端最多只能有25个活跃令牌。一旦达到这个限制,较旧的访问令牌会被悄悄失效,即使它们的到期日期还没有到。

这对我们来说也是死胡同,因为我们的问题发生在刷新时,而不是使用最旧的访问令牌。而且这个限制只影响访问令牌,不影响刷新令牌。

  1. 在短时间内请求刷新次数过多

这个没有文档说明。只是在某些地方提到过,没有具体参考。我尝试在7秒内刷新25次,但一切正常。不过没有参考资料,这就像是在黑暗中摸索。而且我们的后台任务最多也就每几分钟大约10个任务。继续往下看。

  1. 并发刷新导致哪个令牌成功的竞争条件

我在这里问过一个问题,但这不是情况。在AppEngine上测试时,运行了两个同时调度的任务。


我真是束手无策,想要搞清楚这个问题。最烦的是我们无法轻易重现这个问题。我真的很想知道还有什么可能导致这个问题是我没想到的?

这是我们的刷新代码:

def refresh_oauth_credentials(user, credentials, force=False):
    if not credentials:
        return None
    logging.debug(credentials.token_expiry)
    do_refresh = credentials._expires_in() < 300
    if force or do_refresh:
        h = httplib2.Http()
        try:
            logging.debug('Refreshing %s\'s oauth2 credentials...' % user.email)
            credentials.refresh(h)
        except AccessTokenRefreshError:
            logging.warning('Failed to refresh.')
            return None
    return credentials
暂无标签

1 个回答

1

这段话的意思是,刷新令牌可能无效(比如过期、被撤销等等),或者它和访问令牌请求的细节(用户、权限范围)不匹配。所以你在问题中提到的“返回了一个‘invalid_grant’错误,这完全使存储中的刷新令牌无效”,其实是反过来的,也就是说,刷新令牌因为某种原因无效,这才导致了“无效的授权”。

在开发过程中,我经常看到这种情况,特别是当你在测试时为某个用户获取新的刷新令牌时。

撰写回答