YouTube 数据 API v3 刷新令牌在发布状态设置为测试的应用上不断过期
YouTube Data API v3 Refresh Token Keeps Expiring on App with Publishing Status Set to Testing
我想做什么:
我正在尝试构建一个 Python 3.9 程序来使用 OAuth2 凭据(设置为“测试”发布状态,作为“网络”每天调用 YouTube 数据 API v3应用程序”类型和“外部”用户类型),通过存储刷新令牌以在我每次进行唯一调用时获取新的访问令牌。
我一直在使用 YouTube Data API v3 official documentation, the Python code examples from the Google API repository on GitHub, along with this OAuth token solution I found from Corey Schafer on YouTube。
到目前为止我尝试了什么:
这是我的 Python 代码(为了匿名起见,我打乱了播放列表 ID,但是如果您为自己创建的频道输入自己的播放列表 ID,代码 运行 就可以了有凭据):
# YouTube Data API v3
# Pulling data for the brand account
import os
import pickle
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
credentials = None
# youtube_data_token_brand.pickle stores the user's credentials from previously successful logins
if os.path.exists('youtube_data_token_brand.pickle'):
print('Loading Credentials From File...')
with open('youtube_data_token_brand.pickle', 'rb') as token:
credentials = pickle.load(token)
# If there are no valid credentials available, then either refresh the token or log in.
if not credentials or not credentials.valid:
if credentials and credentials.expired and credentials.refresh_token:
print('Refreshing Access Token...')
credentials.refresh(Request())
else:
print('Fetching New Tokens...')
flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets_youtube_data_brand.json',
scopes=[
'https://www.googleapis.com/auth/youtube.readonly'
]
)
flow.run_local_server(port=8080, prompt='consent',
authorization_prompt_message='')
credentials = flow.credentials
# Save the credentials for the next run
with open('youtube_data_token_brand.pickle', 'wb') as f:
print('Saving Credentials for Future Use...')
pickle.dump(credentials, f)
youtube = build('youtube', 'v3', credentials=credentials)
request = youtube.playlistItems().list(
part="status, contentDetails", playlistId='UUlG34RnfYmCsNFgxmTmYjPA', maxResults=28
)
response = request.execute()
for item in response["items"]:
vid_id = (item["contentDetails"]["videoId"])
yt_link = f"https://youtu.be/{vid_id}"
print(yt_link)
我得到的结果:
我的程序 运行s 大约一个星期,然后我得到以下错误(同样,为了匿名我已经编辑了部分文件路径):
/Users/…/PycharmProjects/GoogleAPIs/HelloYouTubeDataAPIOAuth.py
Loading Credentials From File...
Refreshing Access Token...
Traceback (most recent call last):
File "/Users/.../PycharmProjects/GoogleAPIs/HelloYouTubeDataAPIOAuth.py", line 23, in <module>
credentials.refresh(Request())
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/credentials.py", line 200, in refresh
access_token, refresh_token, expiry, grant_response = _client.refresh_grant(
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/_client.py", line 248, in refresh_grant
response_data = _token_endpoint_request(request, token_uri, body)
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/_client.py", line 124, in _token_endpoint_request
_handle_error_response(response_body)
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/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: Bad Request', '{\n "error": "invalid_grant",\n "error_description": "Bad Request"\n}')
Process finished with exit code 1
我可以通过从我的目录中删除 'youtube_data_token_brand.pickle' 文件并重新 运行 程序(然后要求我使用我的 google 帐户登录和通过 OAuth2 步骤手动重新授权访问)。
这让我相信我的刷新令牌即将过期(我发誓我在文档中的某个地方读到它不应该过期直到访问被撤销 - 我没有这样做 - 但我找不到那个注释反复搜索后没有了)。
有趣的是,我能够 运行 同一程序用于我控制的不同 YouTube 帐户,并且该帐户没有遇到相同的刷新令牌错误问题。我还能够使用相同的 token.pickle 方法将刷新令牌存储到其他 Google APIs(Google Analytics、YouTube Analytics 等),但我没有体验过他们中的任何一个都有这个问题。
在此先感谢您提供的任何帮助!
根据 official documentation,刷新令牌失效的一种情况是与相应刷新令牌关联的应用程序的操作权限被撤销。
刷新令牌失效的另一种情况是相应帐户已超过授予的最大刷新令牌数:
There is currently a limit of 50 refresh tokens per Google Account per OAuth 2.0 client ID. If the limit is reached, creating a new refresh token automatically invalidates the oldest refresh token without warning.
你可以检查一下 account's permission page.
上确实发生了什么
刷新令牌似乎已过期的原因有多种。最主要的是,如果身份验证服务器正在 returning 一个新的刷新令牌并且您没有存储它,那么每次运行您的代码时,运行五十次后,您存储的刷新令牌将过期。
注意:授权服务器不会return每次都刷新一个新的token,access token是刷新的。这似乎是基于某种语言,C# 是如何做的,php 不是,我认为节点也不是。我还没有找到为什么会发生这种情况我怀疑它在库中的东西,我不确定 python 库是否这样做,但无论如何最好让它处理事情。
看看这段代码,它允许库处理刷新令牌的所有存储。您似乎手动做了很多这样的事情。这可能会或可能不会导致您的刷新令牌的忏悔。
"""Hello YouTube API ."""
import argparse
from apiclient.discovery import build
import httplib2
from oauth2client import client
from oauth2client import file
from oauth2client import tools
SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']
CLIENT_SECRETS_PATH = 'client_secrets.json' # Path to client_secrets.json file.
def initialize_youtube():
"""Initializes the youtube service object.
Returns:
youtube an authorized youtube service object.
"""
# Parse command-line arguments.
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[tools.argparser])
flags = parser.parse_args([])
# Set up a Flow object to be used if we need to authenticate.
flow = client.flow_from_clientsecrets(
CLIENT_SECRETS_PATH, scope=SCOPES,
message=tools.message_if_missing(CLIENT_SECRETS_PATH))
# Prepare credentials, and authorize HTTP object with them.
# If the credentials don't exist or are invalid run through the native client
# flow. The Storage object will ensure that if successful the good
# credentials will get written back to a file.
storage = file.Storage('youtube.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = tools.run_flow(flow, storage, flags)
http = credentials.authorize(http=httplib2.Http())
# Build the service object.
youtube= build('youtube', 'v3', http=http)
return youtube
您可能还想确保用户没有通过他们的 google 帐户撤销您的访问权限,但我假设您已经检查过了。
在阅读了@stvar 在其中一个答案中发布的 official documentation 后,问题似乎是这个特定的刷新令牌总是有一周的生命周期。这只是因为我的情况是一场“完美风暴”:
- 问题程序的 OAuth2 客户端 ID 凭据是使用 Google Cloud Console 创建的(而另一个是使用 Developers' Console 创建的)。
- 问题程序的 OAuth Consent Screen App API 凭据设置为“外部用户”类型(两个程序的 OAuth2 客户端 ID 凭据实际上都是)。
- OAuth 同意屏幕应用程序的发布状态为“测试”(同样,OAuth2 客户端 ID 凭据也是如此 - 一个与有问题的程序相关,另一个 运行 很好使用相同的代码但通过开发人员控制台创建的不同刷新令牌)。
唯一的解决方案似乎是发布 OAuth 同意屏幕应用程序。
我想做什么:
我正在尝试构建一个 Python 3.9 程序来使用 OAuth2 凭据(设置为“测试”发布状态,作为“网络”每天调用 YouTube 数据 API v3应用程序”类型和“外部”用户类型),通过存储刷新令牌以在我每次进行唯一调用时获取新的访问令牌。
我一直在使用 YouTube Data API v3 official documentation, the Python code examples from the Google API repository on GitHub, along with this OAuth token solution I found from Corey Schafer on YouTube。
到目前为止我尝试了什么:
这是我的 Python 代码(为了匿名起见,我打乱了播放列表 ID,但是如果您为自己创建的频道输入自己的播放列表 ID,代码 运行 就可以了有凭据):
# YouTube Data API v3
# Pulling data for the brand account
import os
import pickle
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
credentials = None
# youtube_data_token_brand.pickle stores the user's credentials from previously successful logins
if os.path.exists('youtube_data_token_brand.pickle'):
print('Loading Credentials From File...')
with open('youtube_data_token_brand.pickle', 'rb') as token:
credentials = pickle.load(token)
# If there are no valid credentials available, then either refresh the token or log in.
if not credentials or not credentials.valid:
if credentials and credentials.expired and credentials.refresh_token:
print('Refreshing Access Token...')
credentials.refresh(Request())
else:
print('Fetching New Tokens...')
flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets_youtube_data_brand.json',
scopes=[
'https://www.googleapis.com/auth/youtube.readonly'
]
)
flow.run_local_server(port=8080, prompt='consent',
authorization_prompt_message='')
credentials = flow.credentials
# Save the credentials for the next run
with open('youtube_data_token_brand.pickle', 'wb') as f:
print('Saving Credentials for Future Use...')
pickle.dump(credentials, f)
youtube = build('youtube', 'v3', credentials=credentials)
request = youtube.playlistItems().list(
part="status, contentDetails", playlistId='UUlG34RnfYmCsNFgxmTmYjPA', maxResults=28
)
response = request.execute()
for item in response["items"]:
vid_id = (item["contentDetails"]["videoId"])
yt_link = f"https://youtu.be/{vid_id}"
print(yt_link)
我得到的结果:
我的程序 运行s 大约一个星期,然后我得到以下错误(同样,为了匿名我已经编辑了部分文件路径):
/Users/…/PycharmProjects/GoogleAPIs/HelloYouTubeDataAPIOAuth.py
Loading Credentials From File...
Refreshing Access Token...
Traceback (most recent call last):
File "/Users/.../PycharmProjects/GoogleAPIs/HelloYouTubeDataAPIOAuth.py", line 23, in <module>
credentials.refresh(Request())
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/credentials.py", line 200, in refresh
access_token, refresh_token, expiry, grant_response = _client.refresh_grant(
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/_client.py", line 248, in refresh_grant
response_data = _token_endpoint_request(request, token_uri, body)
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/_client.py", line 124, in _token_endpoint_request
_handle_error_response(response_body)
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/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: Bad Request', '{\n "error": "invalid_grant",\n "error_description": "Bad Request"\n}')
Process finished with exit code 1
我可以通过从我的目录中删除 'youtube_data_token_brand.pickle' 文件并重新 运行 程序(然后要求我使用我的 google 帐户登录和通过 OAuth2 步骤手动重新授权访问)。
这让我相信我的刷新令牌即将过期(我发誓我在文档中的某个地方读到它不应该过期直到访问被撤销 - 我没有这样做 - 但我找不到那个注释反复搜索后没有了)。
有趣的是,我能够 运行 同一程序用于我控制的不同 YouTube 帐户,并且该帐户没有遇到相同的刷新令牌错误问题。我还能够使用相同的 token.pickle 方法将刷新令牌存储到其他 Google APIs(Google Analytics、YouTube Analytics 等),但我没有体验过他们中的任何一个都有这个问题。
在此先感谢您提供的任何帮助!
根据 official documentation,刷新令牌失效的一种情况是与相应刷新令牌关联的应用程序的操作权限被撤销。
刷新令牌失效的另一种情况是相应帐户已超过授予的最大刷新令牌数:
There is currently a limit of 50 refresh tokens per Google Account per OAuth 2.0 client ID. If the limit is reached, creating a new refresh token automatically invalidates the oldest refresh token without warning.
你可以检查一下 account's permission page.
上确实发生了什么刷新令牌似乎已过期的原因有多种。最主要的是,如果身份验证服务器正在 returning 一个新的刷新令牌并且您没有存储它,那么每次运行您的代码时,运行五十次后,您存储的刷新令牌将过期。
注意:授权服务器不会return每次都刷新一个新的token,access token是刷新的。这似乎是基于某种语言,C# 是如何做的,php 不是,我认为节点也不是。我还没有找到为什么会发生这种情况我怀疑它在库中的东西,我不确定 python 库是否这样做,但无论如何最好让它处理事情。
看看这段代码,它允许库处理刷新令牌的所有存储。您似乎手动做了很多这样的事情。这可能会或可能不会导致您的刷新令牌的忏悔。
"""Hello YouTube API ."""
import argparse
from apiclient.discovery import build
import httplib2
from oauth2client import client
from oauth2client import file
from oauth2client import tools
SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']
CLIENT_SECRETS_PATH = 'client_secrets.json' # Path to client_secrets.json file.
def initialize_youtube():
"""Initializes the youtube service object.
Returns:
youtube an authorized youtube service object.
"""
# Parse command-line arguments.
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[tools.argparser])
flags = parser.parse_args([])
# Set up a Flow object to be used if we need to authenticate.
flow = client.flow_from_clientsecrets(
CLIENT_SECRETS_PATH, scope=SCOPES,
message=tools.message_if_missing(CLIENT_SECRETS_PATH))
# Prepare credentials, and authorize HTTP object with them.
# If the credentials don't exist or are invalid run through the native client
# flow. The Storage object will ensure that if successful the good
# credentials will get written back to a file.
storage = file.Storage('youtube.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = tools.run_flow(flow, storage, flags)
http = credentials.authorize(http=httplib2.Http())
# Build the service object.
youtube= build('youtube', 'v3', http=http)
return youtube
您可能还想确保用户没有通过他们的 google 帐户撤销您的访问权限,但我假设您已经检查过了。
在阅读了@stvar 在其中一个答案中发布的 official documentation 后,问题似乎是这个特定的刷新令牌总是有一周的生命周期。这只是因为我的情况是一场“完美风暴”:
- 问题程序的 OAuth2 客户端 ID 凭据是使用 Google Cloud Console 创建的(而另一个是使用 Developers' Console 创建的)。
- 问题程序的 OAuth Consent Screen App API 凭据设置为“外部用户”类型(两个程序的 OAuth2 客户端 ID 凭据实际上都是)。
- OAuth 同意屏幕应用程序的发布状态为“测试”(同样,OAuth2 客户端 ID 凭据也是如此 - 一个与有问题的程序相关,另一个 运行 很好使用相同的代码但通过开发人员控制台创建的不同刷新令牌)。
唯一的解决方案似乎是发布 OAuth 同意屏幕应用程序。