generate_access_token() 主题为 Google 服务帐户

generate_access_token() with subject for Google service account

在 python 中,我正在尝试通过服务帐户使用 Delegated domain-wide authority 调用 GMail API, 不使用 使用 SERVICE_ACCOUNT_FILE.

我的 objective 是为了避免为服务帐户创建秘密 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 管理员中向服务帐户授予权限后,我收到以下错误:

{'message': 'Precondition check failed.', '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)

但这需要创建一个密钥,我想避免这种情况。

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

据我所知这是不可能的

很高兴被证明是错误的!

我把它作为免责声明,因为你是对的,它没有明确否认这种可能性。它通常不要求文档明确表达你可以而不是用它做的一切,尽管我确实看到这里可能会造成混淆,所以可能值得向文档页面“发送反馈” .

说明书怎么说

Preparing to make an authorized API call

After you obtain the client email address and private key from the API Console ...

source

它在任何地方都没有说“这是一个可选步骤”。在 Directory API instructions on domain-wide delegation, or the Reports API

等其他地方也是如此

不使用Keys的建议

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

Use service account keys only if there is no viable alternative

A service account key lets an application authenticate as a service account, similar to how a user might authenticate with a username and password.

source

当您将服务帐户附加到 App Engine 实例时,实例就像用户,服务帐户就是它的用户帐户。它充当实例的一种标识。关于此的更多信息 .

因此,服务帐户充当另一个帐户的唯一方法是使用密钥[=65] =]。同样,用户使用密钥来充当服务帐户的唯一方法是使用密钥。因此,如果您尝试启用全域委派,前提是您希望服务帐户 就像 其他帐户一样, 有一个密钥是必不可少的,无论服务帐户是否附加到 App Engine 实例。

当然,没有明确提到您可以 在没有钥匙的情况下实现这一点。尽管它也没有提到您 可以 。从我 运行 的测试中,我 运行 的结果与你的相似,所以看起来你就是做不到。如免责声明中所述,我很乐意被证明是错误的,但是,我怀疑这是由于漏洞而非预期行为造成的。

您没有提到为什么需要全域授权,所以您可能需要评估一下是否真的需要它。通常只需要 OAuth 流程,其中应用程序 oh-behalf 用户的行为,而不是 as-if 用户的行为.

参考资料