几天后的 Python OAuth 刷新令牌失败,出现“invalid_grant”错误

Posted

技术标签:

【中文标题】几天后的 Python OAuth 刷新令牌失败,出现“invalid_grant”错误【英文标题】:Python OAuth after few days fails refreshing tokens with "invalid_grant" error 【发布时间】:2021-06-03 01:29:21 【问题描述】:

我有一个在 Raspberry Pi 上运行的 Python 应用程序,它可以向我管理的 YouTube 频道进行直播。这是我用来验证的代码:

import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors
import google.auth.transport.requests
import google.oauth2.credentials
import requests

CLIENT_SECRETS_FILE = "client_secrets.json"
YOUTUBE_READ_WRITE_SCOPE = "https://www.googleapis.com/auth/youtube"
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"

def get_authenticated_service(args):
    credentials = None
    credentials_json_file = "youtube-%s.json" % slugify(args.oauth2_name)
    if os.path.exists(credentials_json_file):
        # load credentials from file
        with open(credentials_json_file, encoding='utf-8') as f:
            credentials_json = json.load(f)
        credentials = google.oauth2.credentials.Credentials.from_authorized_user_info(credentials_json)
    if not credentials or not credentials.valid:
        # no credentials file or invalid credentials
        if credentials and credentials.expired and credentials.refresh_token:
            # refresh
            request = google.auth.transport.requests.Request()
            credentials.refresh(request)
        else:
            # re-authenticate
            flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, [YOUTUBE_READ_WRITE_SCOPE])
            credentials = flow.run_console()
        # save credentials to file
        credentials_json = credentials.to_json()
        with open(credentials_json_file, 'w', encoding='utf-8') as f:
            f.write(credentials_json)
    return googleapiclient.discovery.build(
        YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, credentials=credentials)

当我第一次运行我的应用程序时,我必须进行身份验证。凭据存储在 JSON 文件中,如下所示:


  "token": "...", 
  "refresh_token": "...", 
  "token_uri": "https://oauth2.googleapis.com/token", 
  "client_id": "....apps.googleusercontent.com", 
  "client_secret": "...", 
  "scopes": ["https://www.googleapis.com/auth/youtube"],    
  "expiry": "2021-02-28T09:27:44.221302Z"

当我稍后重新运行该应用程序时,我不必重新进行身份验证。效果很好。

但 2-3 天后,我收到此错误:

Traceback (most recent call last):
  File "./create_broadcast.py", line 188, in <module>
    youtube = get_authenticated_service(args)
  File "./create_broadcast.py", line 83, in get_authenticated_service
    credentials.refresh(request)
  File "/home/pi/.local/lib/python3.7/site-packages/google/oauth2/credentials.py", line 214, in refresh
    scopes,
  File "/home/pi/.local/lib/python3.7/site-packages/google/oauth2/_client.py", line 248, in refresh_grant
    response_data = _token_endpoint_request(request, token_uri, body)
  File "/home/pi/.local/lib/python3.7/site-packages/google/oauth2/_client.py", line 124, in _token_endpoint_request
    _handle_error_response(response_body)
  File "/home/pi/.local/lib/python3.7/site-packages/google/oauth2/_client.py", line 60, in _handle_error_response
    raise exceptions.RefreshError(error_details, response_body)
google.auth.exceptions.RefreshError: ('invalid_grant: Token has been expired or revoked.', '\n  "error": "invalid_grant",\n  "error_description": "Token has been expired or revoked."\n')

解决方法是删除凭据文件并重新进行身份验证。但我希望凭据刷新在几天后仍然有效!

我确实安装并运行了 NTP。我没有手动撤销令牌。我没有更改我的 Google 密码。我没有在其他地方生成很多其他令牌。我没有做任何其他地方告诉我导致此错误的事情。

需要注意的一点:该应用未经验证,因为它仅供内部使用。这不应该影响刷新令牌的生命周期,不是吗?

是什么让这种清爽在 1 天或 2 天后起作用,但在 3 天后不再起作用?!

最好的问候, 维克

【问题讨论】:

您是否多次授权?当您刷新访问权限时,它会返回相同的刷新令牌还是不同的刷新令牌。您可以有 50 个刷新令牌的限制,然后第一个提示音将过期。 当我在不到一小时后重新进行身份验证时,不会更新任何凭据。当我在一个多小时后重新进行身份验证时,凭证文件中的令牌和刷新令牌都是新的。不存储较旧的令牌。 【参考方案1】:

我非常认为您的相应 Google 项目会显示 - 从 Developers Console 中的页面OAuth 同意屏幕 上 - 您的应用具有其发布状态设置为测试

根据官方文档,您的刷新令牌受到以下限制:

Refresh token expiration

您必须编写代码来预测授予的刷新令牌可能不再起作用的可能性。由于以下原因之一,刷新令牌可能会停止工作:

[...]

为外部用户类型配置了 OAuth 同意屏幕且发布状态为“正在测试”的 Google Cloud Platform 项目发出了一个在 7 天后到期的刷新令牌。

在您的应用的发布状态设置为生产中——即你的应用被谷歌审核——之前,上述限制意味着你必须运行成功每周完成一次 OAuth 身份验证/授权流程,以获取新的刷新令牌(其有效期仅为 7 天)。

【讨论】:

是新的吗?这不是我以前见过的。 我最近有一个long exchange of comments 讨论这个问题(线程被转移到聊天中)。最初我知道桌面应用程序不需要验证;但是已经创建了一个这种类型的测试项目,并且控制台需要验证该应用程序才能将发布状态更改为生产中。(自己尝试。仔细检查总是更好。) 是的,发布状态是测试。我以前没有读过这个限制。因此,即使我是唯一必须在应用程序中进行身份验证的用户,我也必须进行验证舞蹈?还是我忽略了一些非常明显的单用户身份验证机制? 不,您没有忽略任何其他身份验证机制,因为 OAuth 是唯一的一种。再次强调:验证步骤在生产中是 Google 对其 API 用户提出的一项非常新的要求。 我的应用已通过 Google 验证。起初他们建议我应该为内部用户使用 Cloud Identity 帐户,但当我解释上述内容时,批准通过了。

以上是关于几天后的 Python OAuth 刷新令牌失败,出现“invalid_grant”错误的主要内容,如果未能解决你的问题,请参考以下文章

OWIN 安全 - 如何实现 OAuth2 刷新令牌

刷新 OAuth2 访问令牌的正确范例

使用 spring security 的 oauth2 刷新令牌调用失败,错误:需要 UserDetailsS​​ervice

OAuth - 在“社交登录”中使用刷新令牌

撤销 JWT Oauth2 刷新令牌

Angular 4 和 OAuth - 拦截 401 响应,刷新访问令牌并重试请求