几天后的 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”错误的主要内容,如果未能解决你的问题,请参考以下文章
使用 spring security 的 oauth2 刷新令牌调用失败,错误:需要 UserDetailsService