通过 Google Apps 脚本安全地调用 Google Cloud 函数 [英] Securely calling a Google Cloud Function via a Google Apps Script

查看:30
本文介绍了通过 Google Apps 脚本安全地调用 Google Cloud 函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何通过 Google Apps 脚本安全地调用 Google Cloud 函数?

How can I securely call a Google Cloud Function via a Google Apps Script?

✅ 我有一个谷歌云函数,我可以在 https://MY_REGION-MY_PROJECT.cloudfunctions.net/MY_FUNCTION 访问它,我想允许某些用户通过应用脚本.

✅ I have a Google Cloud Function, which I can access at https://MY_REGION-MY_PROJECT.cloudfunctions.net/MY_FUNCTION, and which I would like to allow certain users to invoke via an Apps Script.

✅ 为了保护 Cloud Functions,我将 Cloud Functions Invoker 设置为仅包含已知电子邮件(例如 USER@COMPANY.com,这是有效的 Google 电子邮件).

✅ To secure the Cloud Function, I have set Cloud Function Invoker to only include known email (e.g. USER@COMPANY.com, where this is a valid Google email).

✅ 我能够通过 curl 成功调用 Cloud Function,同时使用此电子邮件登录 gcloud,运行:curl https://MY_REGION-MY_PROJECT.cloudfunctions.net/MY_FUNCTION -H "Authorization: Bearer$(gcloud auth print-identity-token)".

✅ I am able to successfully invoke the Cloud Function via curl, while logged into gcloud with this email, by running: curl https://MY_REGION-MY_PROJECT.cloudfunctions.net/MY_FUNCTION -H "Authorization: Bearer $(gcloud auth print-identity-token)".

✅ 我已在我的 Apps 脚本清单中授予以下 oauthScopes:

✅ I have granted the following oauthScopes in my Apps Script's manifest:

  • https://www.googleapis.com/auth/script.external_request"
  • https://www.googleapis.com/auth/userinfo.email"
  • https://www.googleapis.com/auth/cloud-platform"

⛔️ 但是,当我尝试通过 Google Apps 脚本调用云函数时,在使用电子邮件 USER@COMPANY.com 登录时,我无法调用它,而是返回了 401. 以下是我尝试调用 Cloud Function 的方式:

⛔️ However, when I attempt to invoke the Cloud Function via a Google Apps Script, while logged in with the email USER@COMPANY.com, I am unable to invoke it and instead returned a 401. Here is how I have attempted to invoke the Cloud Function:

const token = ScriptApp.getIdentityToken();
const options = {
  headers: {'Authorization': 'Bearer ' + token}
}
UrlFetchApp.fetch("https://MY_REGION-MY_PROJECT.cloudfunctions.net/MY_FUNCTION", options);

ℹ️我也试过以下:

  • 使用ScriptApp.getOAuthToken()
  • 添加额外的 oauthScopes,例如openid.
  • 创建 OAuth 客户端 ID,并将 https://script.google.com 设置为授权的 Javascript 来源.
  • 部署 Apps 脚本.
  • 极度绝望地向天空呼喊
  • Using ScriptApp.getOAuthToken()
  • Adding additional oauthScopes, e.g. openid.
  • Creating an OAuth Client ID with https://script.google.com set as an Authorized Javascript origin.
  • Deploying the Apps Script.
  • Crying out to the sky in utter, abject despair

推荐答案

我在通过 Apps Script 进行身份验证以调用 Cloud Run 应用程序时遇到了很多困难,并且刚刚想通了,我相信调用任何 Google Cloud 应用程序(包括 Cloud)都类似职能.本质上,目标是使用您已经运行的应用程序的身份验证信息调用受 Google Cloud IAM 保护的 HTTP 方法以用户身份编写脚本.

I struggled very much authenticating from Apps Script to invoke a Cloud Run application and just figured it out, and I believe it's similar for calling any Google Cloud application including Cloud Functions. Essentially the goal is to invoke an HTTP method protected by Google Cloud IAM using the authentication information you already have running Apps Script as the user.

我认为缺少的一步是,您使用的技术只有在 Apps 脚本脚本和 Google Cloud Function(在我的例子中是运行容器)在同一个 GCP 项目时才有效.(了解如何将脚本与 GCP 项目相关联.)

The missing step I believe is that the technique you're using will only work if the Apps Script script and Google Cloud Function (or Run container in my case) are in the same GCP project. (See how to associate the script with the GCP project.)

以这种方式进行设置比其他方式简单得多:当您将脚本与 GCP 项目相关联时,这会自动创建一个 OAuth 客户端 ID 配置到项目,Apps 脚本的 getIdentityToken 函数返回一个仅对该客户端 ID 有效的身份令牌(它被编码到 aud 字段 令牌的字段).如果您想要一个适用于另一个项目的身份令牌,则需要另辟蹊径.

Setting it up this way is much simpler than otherwise: when you associate the script with a GCP project, this automatically creates an OAuth Client ID configuration to the project, and Apps Script's getIdentityToken function returns an identity token that is only valid for that client ID (it's coded into the aud field field of the token). If you wanted an identity token that works for another project, you'd need to get one another way.

如果您能够将脚本和 GCP 函数或应用程序放在同一个 GCP 项目中,您还必须做这些事情,其中​​许多事情您已经做过:

If you are able to put the script and GCP function or app in the same GCP project, you'll also have to do these things, many of which you already did:

  • 通过 curl https://MY_REGION-MY_PROJECT.cloudfunctions.net/MY_FUNCTION -H "Authorization: Bearer $(gcloud auth print-identity-token)" 成功测试您的云功能的身份验证(按照此处的说明).如果此操作失败,那么您遇到的问题与此 Stack Overflow 问题中所问的问题不同,因此我将省略对此的故障排除步骤.

  • Successfully test authentication of your cloud function via curl https://MY_REGION-MY_PROJECT.cloudfunctions.net/MY_FUNCTION -H "Authorization: Bearer $(gcloud auth print-identity-token)" (as instructed here). If this fails then you have a different problem than is asked in this Stack Overflow question, so I'm omitting troubleshooting steps for this.

确保您实际上是运行脚本的人.您无法从电子表格中的自定义函数中获取身份令牌,因为它们是匿名运行的.在其他情况下,Apps 脚本代码可能会以其他人的身份运行,例如某些触发器.

Ensure you are actually who the script is running as. You cannot get an identity token from custom function in a spreadsheet as they run anonymously. In other cases, the Apps Script code may be running as someone else, such as certain triggers.

按照此处所述重新部署云功能(或类似地重新部署此处) 中提到的 Cloud Run 容器,因此应用程序将选择任何新的客户端 ID 配置.创建任何新客户端 ID 后都需要这样做,包括通过向 GCP 项目添加或重新添加脚本自动创建的客户端 ID.(如果您将脚本移至另一个 GCP 项目,然后再将其移回,它似乎会创建另一个客户端 ID,而不是重用旧的,而旧的将停止工作.)

Redeploy the Cloud Function as mentioned here (or similarly redeploy the Cloud Run container as mentioned here) so the app will pick up any new Client ID configuration. This is required after any new Client ID is created, including the one created automatically by adding or re-adding the script to the GCP project. (If you move the script to another GCP project and then move it back again, it seems to create another Client ID rather than reuse the old one and the old one will stop working.)

添加 "openid" 范围(以及所有其他需要的范围,例如 https://www.googleapis.com/auth/script.external_request)在 清单.getIdentityToken() 将返回 null 没有 openid 范围,这可能会导致此错误.读者注意事项:仔细阅读此要点 - 范围名称实际上只是openid" - 它不像其他范围那样是一个 URL.

Add the "openid" scope (and all other needed scopes, such as https://www.googleapis.com/auth/script.external_request) explicitly in the manifest. getIdentityToken() will return null without the openid scope which can cause this error. Note to readers: read this bullet point carefully - the scope name is literally just "openid" - it's not a URL like the other scopes.

"oauthScopes": ["openid", "https://...", ...]

  • 使用getIdentityToken() 并且不要使用 getOAuthToken().根据我读过的内容, getOAuthToken() 返回访问令牌而不是身份令牌.访问令牌不能证明您的身份;相反,他们只是提供访问某些资源的证明授权.

  • Use getIdentityToken() and do NOT use getOAuthToken(). According to what I've read, getOAuthToken() returns an access token rather than an identity token. Access tokens do not prove your identity; rather they just give prove authorization to access some resources.

    如果您无法将脚本添加到与 GCP 应用程序相同的项目,我不知道该怎么办,因为我从未成功尝试过.通常,您的任务是获取与您的 GCP 客户端 ID 之一相关联的 OAuth 身份令牌.我不认为一个应用程序(或 GCP 项目)应该能够为不同的 OAuth 应用程序(不同的 GCP 项目)获取身份令牌.不管怎样,还是有可能的.Google 在其 OpenID Connect 文档 中从高层次讨论了 OAuth 身份验证.也许一个 HTML 服务来做一个常规的Google 登录流程,带有 Web 客户端,如果您让用户单击重定向链接,因为 Apps 脚本不允许浏览器重定向,则适用于用户存在的操作.如果您只需要保护您的服务不受公众影响,也许您可​​以尝试其他涉及服务帐户的身份验证选项.(我也没有尝试过.)如果服务只需要知道用户是谁,也许您可​​以解析身份令牌并将用户的标识符作为请求的一部分发送.如果该服务需要访问他们的 Google 资源,那么也许您可以让用户单独登录该应用,并通常使用 OAuth 来长期访问他们的资源,在 Apps 脚本调用时根据需要使用它.

    If you are not able to add the script to the same project as the GCP application, I don't know what to do as I've never successfully tried it. Generally you're tasked with obtaining an OAuth identity token tied to one of your GCP client ids. I don't think one app (or GCP project) is supposed to be able to obtain an identity token for a different OAuth app (different GCP project). Anyway, it may still be possible. Google discusses OAuth authentication at a high level in their OpenID Connect docs. Perhaps an HTML service to do a regular Google sign-in flow with a web client, would work for user-present operations if you get the user to click the redirect link as Apps Script doesn't allow browser redirects. If you just need to protect your service from the public, perhaps you could try other authentication options that involve service accounts. (I haven't tried this either.) If the service just needs to know who the user is, perhaps you could parse the identity token and send the identifier of the user as part of the request. If the service needs to access their Google resources, then maybe you could have the user sign in to that app separately and use OAuth generally for long term access to their resources, using it as needed when called by Apps Script.

    这篇关于通过 Google Apps 脚本安全地调用 Google Cloud 函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆