带有 Google 服务帐户主题的 generate_access_token()

Posted

技术标签:

【中文标题】带有 Google 服务帐户主题的 generate_access_token()【英文标题】:generate_access_token() with subject for Google service account 【发布时间】:2021-10-07 16:36:07 【问题描述】:

在 python 中,我试图通过Delegated domain-wide authority 的服务帐户调用 GMail API,没有 使用SERVICE_ACCOUNT_FILE

我的目标是避免为服务帐户创建秘密 Key。相反,我将Service Account Token Creator 角色分配给了流程所有者(我在本地开发中,App Engine 服务帐户在生产中)。

在下面的代码中,我成功获取并使用了服务帐户的访问令牌,没有任何SERVICE_ACCOUNT_FILE

from google.cloud.iam_credentials_v1 import IAMCredentialsClient
from google.oauth2.credentials import Credentials
import googleapiclient.discovery

tk = IAMCredentialsClient().generate_access_token(
    name=f'projects/-/serviceAccounts/service_id',
    scope=['https://www.googleapis.com/auth/gmail.insert'],
    # subject='admin@my.domain' doesn't work here :'(
)
service = googleapiclient.discovery.build('gmail', 'v1', credentials=Credentials(tk.access_token))
response = service.users().messages().insert(userId='user@my.domain', body=body).execute()

问题是,在my.domain 的 Google Admin 中授予服务帐户权限后,我收到以下错误:

'message': '前置条件检查失败。', 'domain': 'global', 'reason': 'failedPrecondition'

我怀疑我缺少的是subject,即my.domain 管理员的电子邮件。

我知道我可以通过以不同方式构造 credentials 来提供 subject

from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file(key_file, scopes=scopes)
delegated_credentials = credentials.with_subject('admin@my.domain'). # <- I need this
service = googleapiclient.discovery.build('gmail', 'v1', credentials=delegated_credentials)

但这需要创建一个密钥,我想避免这样做。

上面第一个代码示例中有没有办法传递主题?

【问题讨论】:

您能解释一下为什么要避免创建密钥吗? Google 似乎不鼓励它,例如。 here: "Use service account keys only if there is no viable alternative"。就我而言,似乎有另一种选择,因为我设法在没有任何用户管理密钥的情况下获得服务的访问令牌。仅缺少该服务对另一个用户的模拟(如果使用密钥进行身份验证,它可以这样做)。 所以你想运行一个“附加”服务帐户?你确定你是从一个受支持的平台上运行它并且有attached它吗? 我遇到了将服务帐户附加到资源的概念。不幸的是,在我们当前的工作流程中,我们为每个客户创建一个服务帐户(我们是多租户)。 IIUC 只能有一个服务帐户附加到我们的 App Engine 实例。 我认为在这种情况下这可能是您问题的答案 - ***.com/questions/55354070/… - 即服务帐户标识应用引擎实例,因此您不能将多个附加到它。跨度> 【参考方案1】:

AFAIK 这是不可能的

很高兴被证明是错误的!

我将此作为免责声明,因为您是对的,它并没有明确否认这种可能性。它通常不需要文档明确表达您可以用它做的所有事情,尽管我确实看到这里可能会造成混淆,因此可能值得向文档页面“发送反馈”。 p>

说明书是怎么说的

准备进行授权的 API 调用

在您从 API 控制台获取客户端电子邮件地址和私钥后...

source

它在任何地方都没有说“这是一个可选步骤”。在Directory API instructions on domain-wide delegation 或Reports API 等其他地方也是如此

不使用密钥的建议

以下内容来自您链接的文章:

只有在没有可行的替代方案时才使用服务帐号密钥

服务帐户密钥允许应用程序作为服务进行身份验证 帐户,类似于用户使用用户名进行身份验证的方式和 密码。

source

当您将服务帐户附加到 App Engine 实例时,该实例就像用户,而服务帐户就是它的用户帐户。它充当实例的一种身份。更多信息请参见另一个*** thread。

因此,服务帐户就像另一个帐户的唯一方法是,使用密钥。同样,用户将其视为服务帐户的唯一方法是使用密钥。因此,如果您尝试启用域范围的委派,前提是您希望服务帐户就像其他帐户一样,拥有密钥是必不可少的,服务帐号是否附加到 App Engine 实例。

当然,没有明确提到您可以在没有密钥的情况下实现此目的。虽然它也没有提到你可以。从我运行的测试中,我遇到了与你相似的结果,所以看起来你不能。如免责声明中所述,我很高兴被证明是错误的,但是,我怀疑这可能是由于漏洞,而不是预期的行为。

您还没有提到为什么需要全域范围的授权,因此您可能需要评估您是否真的需要它。通常所需要的只是 OAuth 流程,其中应用代表用户,而不是好像它是用户。

参考文献

Access to Google APIs with Service Accounts How to Authenticate Service Accounts Directory API instructions on domain-wide delegation Reports API instructions on domain-wide delegation "How to assign multiple service accounts to cloud functions" (you can't)。

【讨论】:

感谢您的研究,我会接受这个答案,因为没有其他挑战者。至于为什么需要全域委派,我在一家网络钓鱼模拟公司工作,作为我们服务的一部分,我们在不同公司(不同的 Google Workspaces)的邮箱中写电子邮件。这带来了its own set of questions?

以上是关于带有 Google 服务帐户主题的 generate_access_token()的主要内容,如果未能解决你的问题,请参考以下文章

无法使用服务帐户订阅 google pub 子主题

带有服务帐户的 Google Calendar Python API 未返回任何结果

如何在 C# 中使用带有服务帐户的 gmail api 或 google Oauth 来发送邮件?

如何使用 Google 服务帐户通过 Activity API 检索 Google Drive 活动?

为具有 pubsub 目标的 google-sink 预定义服务帐户

使用 JS 的服务帐户调用 Google bigquery