我已经创建了一个Google Cloud函数,我想从我正在开发的Python应用程序中访问它。我可以在不需要身份验证的情况下访问该功能,但在启用身份验证时无法访问该功能。
这是我正在使用的服务帐户密钥,其中包含已删除的信息。它配置的唯一角色是调用云函数。
{
"type": "service_account",
"project_id": "XYZ",
"private_key_id": "XYZ",
"private_key": "XYZ",
"client_email": "XYZ",
"client_id": "XYZ",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "XYZ"
}
经过身份验证的请求似乎需要包含在请求Authorization
标头中的令牌,但我不知道从哪里获取该令牌。
我尝试过使用这里概述的方法,包括环境变量和默认的auth方法,但这不起作用。我认为这是因为服务帐户密钥与OAuth令牌不同。(我已经创建了一个具有云功能调用程序权限的新服务帐户,并且正在使用该服务帐户密钥(。我收到以下错误:
google.auth.exceptions.RefreshError: ('invalid_scope: Invalid OAuth scope or ID token audience provided.', '{"error":"invalid_scope","error_description":"Invalid OAuth scope or ID token audience provided."}')
如何生成此令牌来验证来自Python脚本的请求?还是建议使用服务帐户的方法,但出现了其他问题?
我能够做到这一点,尽管在撰写本文时,文档中有一个错误让我很失望。
使用google-auth
库中的IDTokenCredentials类可以访问受保护的云功能:
credentials = service_account.IDTokenCredentials.from_service_account_file(
SERVICE_ACCOUNT_JSON_FILE,
target_audience="https://function/url/here",
)
authed_session = AuthorizedSession(credentials)
response = authed_session.post(url)
我的服务帐户配置有";云函数调用程序";角色
您的问题几乎可以肯定是您为该服务帐户提供的角色。根据我的经验,服务帐户很挑剔,角色/权限并不总是按照你认为的方式行事。首先创建一个具有完全权限的服务帐户(项目所有者(。在脚本中使用该服务帐户,然后从那里开始限制权限。听起来你至少需要一个云功能";管理员";。如果有效的话,试着降低另一个级别。云功能"显影剂";等
如果你正在使用应用程序引擎甚至其他云功能连接到你的云功能,你可以使用这个:功能到功能,步骤基本上是:
-
授予云函数调用程序。
-
在调用功能中,您需要:
-
创建一个谷歌签名的OAuth ID令牌,其中受众(aud(设置为接收函数的URL
-
在对函数的请求中的Authorization:Bearer ID_token标头中包含ID令牌。
导入请求
//# TODO<developer>: set these values REGION = 'us-central1' PROJECT_ID = 'my-project' RECEIVING_FUNCTION = 'my-function' //# Constants for setting up metadata server request //# See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature function_url = f'https://{REGION}-{PROJECT_ID}.cloudfunctions.net/{RECEIVING_FUNCTION}' metadata_server_url = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=' token_full_url = metadata_server_url + function_url token_headers = {'Metadata-Flavor': 'Google'} def calling_function(request): //# Fetch the token token_response = requests.get(token_full_url, headers=token_headers) jwt = token_response.text //# Provide the token in the request to the receiving function function_headers = {'Authorization': f'bearer {jwt}'} function_response = requests.get(function_url, headers=function_headers) return function_response.text
我已经测试了这个解决方案,并按预期工作。
如果您从无法访问计算元数据的计算实例(例如您自己的服务器(调用函数,则必须手动生成正确的令牌:
-
在target_audience声明设置为接收功能的URL的情况下,对服务帐户JWT进行自签名。
-
将自签名JWT交换为谷歌签名的ID令牌,该令牌应将aud声明设置为上述URL。
-
在对函数的请求中的Authorization:Bearer ID_token标头中包含ID令牌。
CloudIAP文档中有演示此功能的示例代码。你可能感兴趣的部分应该是从服务帐户进行身份验证