未授权客户端:客户端未授权使用此方法检索访问令牌

Posted

技术标签:

【中文标题】未授权客户端:客户端未授权使用此方法检索访问令牌【英文标题】:unauthorized_client: Client is unauthorized to retrieve access tokens using this method 【发布时间】:2018-12-01 21:51:26 【问题描述】:

我是一名拥有近 300 名用户的 GSuite 管理员,我正在从 GSuite 迁移出去。我需要下载他们创建/上传的所有用户文件。

我从编写 Python 脚本开始,该脚本将在列表中显示用户文件,似乎我遇到了压倒性的授权问题。

    我在 Google 控制台中创建了一个项目,并创建了一个服务帐户,其中包含私钥(基于 json)并勾选了 GSuite 域范围的委派复选框 在我的 GSuite 管理面板中,我在管理 API 访问这些范围中添加了新创建的客户端 ID 和权限范围:https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/admin.datatransfer,https://www.googleapis.com/auth/admin.directory.user, https://www.googleapis.com/auth/admin.directory.group

根据文档(https://developers.google.com/admin-sdk/directory/v1/guides/delegation)非常好

    我正在从 ServiceAccountCredentials 创建一个资源对象,并分别基于 API 名称/版本“drive”和“v3”构建一个对象,并尝试根据 Google 的快速入门 (https://developers.google.com/drive/api/v3/quickstart/python) 获取文件列表:

     from googleapiclient.discovery import build
     from oauth2client.service_account import ServiceAccountCredentials
    
     SERVICE_ACCOUNT_EMAIL = "drive-getter@mygoogledomain.iam.gserviceaccount.com"
     SERVICE_ACCOUNT_PKCS12 = "./service_key.json"
    
     def create_directory_service(user_email):
       credentials = ServiceAccountCredentials.from_json_keyfile_name(SERVICE_ACCOUNT_PKCS12, scopes=['https://www.googleapis.com/auth/drive'])
       credentials = credentials.create_delegated(user_email)
    
       return build('drive', 'v3', credentials=credentials)
    
    
     resource = create_directory_service('username@mydomain.com')
    
     results = resource.files().list(
       pageSize=10, fields="nextPageToken, files(id, name)"
     ).execute()
    
     items = results.get('files', [])
    
     print(items)
    

看起来完全正确,但我收到以下错误消息:

连接到 pydev 调试器(内部版本 181.5087.37)

Traceback(最近一次调用最后一次):文件 “/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py”,行 1664 年,在 main() 文件“/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py”,行 1658,主要 globals = debugger.run(setup['file'], None, None, is_module) File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", 第 1068 行,运行中 pydev_imports.execfile(file, globals, locals) # 执行脚本文件 "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", 第 18 行,在 execfile 中 exec(compile(contents+"\n", file, 'exec'), glob, loc) File "/Users/probe/Projects/drive_getter/src/dconnect.py",第 16 行,在 pageSize=10, fields="nextPageToken, files(id, name)" 文件 "/Users/probe/Projects/drive_getter/drive_getter/lib/python3.6/site-packages/googleapiclient/_helpers.py", 第 130 行,在 positional_wrapper 中 返回包装的(*args,**kwargs)文件“/Users/probe/Projects/drive_getter/drive_getter/lib/python3.6/site-packages/googleapiclient/http.py”, 第 835 行,执行中 方法=str(self.method), body=self.body, headers=self.headers) 文件 "/Users/probe/Projects/drive_getter/drive_getter/lib/python3.6/site-packages/googleapiclient/http.py", 第 162 行,在 _retry_request 中 resp, content = http.request(uri, method, *args, **kwargs) 文件 "/Users/probe/Projects/drive_getter/drive_getter/lib/python3.6/site-packages/oauth2client/transport.py", 第 159 行,在 new_request 中 credentials._refresh(orig_request_method)文件“/Users/probe/Projects/drive_getter/drive_getter/lib/python3.6/site-packages/oauth2client/client.py”, 第 749 行,在 _refresh 中 self._do_refresh_request(http) 文件“/Users/probe/Projects/drive_getter/drive_getter/lib/python3.6/site-packages/oauth2client/client.py”, 第 819 行,在 _do_refresh_request 中 引发 HttpAccessTokenRefreshError(error_msg, status=resp.status) oauth2client.client.HttpAccessTokenRefreshError:未授权客户端: 客户端未经授权无法使用此方法检索访问令牌。

知道在这个过程中做错了什么吗?再说一遍 - 我的目标是列出所有 GSuite 用户的所有用户文件并稍后下载,因此我正在考虑循环我的用户电子邮件并对所有用户应用相同的逻辑,直到我下载所有文件。

感谢您的合作!

【问题讨论】:

你能用fields="files(id, name)"试试这个吗? 【参考方案1】:

客户端未经授权无法使用此方法检索访问令牌。

Google Develoepr 控制台中有多种不同类型的客户端。

浏览器 - 用于 Web 应用程序。 本机 - 用于已安装的应用程序 服务帐户 - 用于服务器到服务器的通信 手机

用于对这些不同类型进行身份验证的代码是可以理解的不同。

您使用的代码似乎是具有服务帐户的 python 的正确代码。虽然from_json_keyfile_name 接受 p12 文件而不是 .json 文件。

那里的客户必须是正确的。返回 Google 开发者控制台创建一个服务帐户客户端并下载正确的 .json 文件。

Service account python

from google.oauth2 import service_account

SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
SERVICE_ACCOUNT_FILE = '/path/to/service.json'

credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)

我建议在 p12 文件上使用 .json 文件,我怀疑谷歌将来会停止使用这些文件。

【讨论】:

有问题。如果我导入from oauth2client.service_account import ServiceAccountCredentials 它有credentials.create_delegated 方法,它允许我下载特定用户的文件,但是这个对象不支持JSON 中的服务帐户凭据,但是当我使用您的导入时,我可以加载JSON 中的服务帐户凭据,但没有 create_delegated 方法。 :) @deb0rian 那么解决方案是什么?【参考方案2】:

解决方案很简单。

首先,据我所知,只有当您使用 P12 密钥并基于这些凭据构造对象时,google.oauth2 库才支持域范围的委托功能。

其次,请注意您正在下载的 p12 密钥的文件名,我将密钥文件名重命名为更短的文件名,并没有注意 Google 在文件名中包含公钥指纹。

所以解决方案是,我的问题帖子中描述的所有步骤,使用 p12 密钥创建服务帐户,勾选域范围委托,工作代码如下所示:

from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials

SERVICE_ACCOUNT_EMAIL = "client-name@client-name.iam.gserviceaccount.com"
PK12_FILE = "./private-key-original-filename.p12"
PK12_PASSWORD = "notasecret"
SCOPES = ['https://www.googleapis.com/auth/drive']

def create_directory_service(user_email):
    credentials = ServiceAccountCredentials.from_p12_keyfile(
        SERVICE_ACCOUNT_EMAIL,
        PK12_FILE,
        PK12_PASSWORD,
        scopes=SCOPES
    )

    credentials = credentials.create_delegated(user_email)

    return build('drive', 'v3', credentials=credentials)


bryan = create_directory_service('domain-user@your-domain.com')

results = bryan.files().list(
    pageSize=10, fields="nextPageToken, files(id, name, mimeType, parents)"
).execute()

items = results.get('files', [])

print(items)

奖励:如果您想从 GDrive 下载文件并保留文件夹结构(Google 不会告诉您文件哈希是否为文件夹),请使用提取的 mimeType 和父 ID 来构建父堆栈(文件夹),在循环列表时自动创建它们,然后将文件放在它们各自的父 ID 中。

【讨论】:

google.oauth2 库仅在您使用 P12 密钥时才支持域范围的委托功能,如果这是真的,它是一个错误,您应该在问题论坛上记录它,这不是我拥有的听说过 如果我尝试这个,我得到“这个库仅通过 pyOpenSSL 库实现 PKCS#12 支持。安装 pyOpenSSL,或者请将 .p12 文件转换为 .pem 格式”,即使我有 pyOpenSSL安装...?

以上是关于未授权客户端:客户端未授权使用此方法检索访问令牌的主要内容,如果未能解决你的问题,请参考以下文章

刷新谷歌访问令牌时未授权_客户端

客户端未经授权使用此方法检索访问令牌 Gmail API C#

客户端未经授权使用此方法检索访问令牌服务帐户错误

由于未授权客户端错误,无法获取令牌

Blazor 客户端中的 Identityserver4 未授权 api

JWT 令牌未经过验证,因此每次都返回 401 未授权