Cloud Run + Cloud Endpoints + Service Account Authentication – 在 curl 中有效,但在 JS 中使用 fetch API 时无效

Posted

技术标签:

【中文标题】Cloud Run + Cloud Endpoints + Service Account Authentication – 在 curl 中有效,但在 JS 中使用 fetch API 时无效【英文标题】:Cloud Run + Cloud Endpoints + Service Account Authentication – works in curl but doesn't when using fetch API in JS 【发布时间】:2021-01-24 01:01:18 【问题描述】:

我已经配置了一个执行 GCF 的云端点。当云运行服务允许 allUsers 调用 API 时,一切正常。

删除 allUsers 并使用服务帐户进行身份验证后,云运行控制台中会出现 403 错误:

请求未通过身份验证。允许未经身份验证的调用或设置正确的 Authorization 标头。阅读更多https://cloud.google.com/run/docs/securing/authenticating

Chrome JS 控制台显示以下错误消息:

访问 'https://.run.app/do-this&key=' 从源获取 “http://0.0.0.0:8080”已被 CORS 策略阻止:对预检请求的响应没有 通过访问控制检查:请求中不存在“Access-Control-Allow-Origin”标头 资源。如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取 禁用 CORS 的资源。

这是我在浏览器中运行的 JS 代码:

        let options: RequestInit = 
            headers: 
                'Authorization': `Bearer $token`,
            ,
        
        
        const result = await fetch(fetchURL, options);

当使用相同的令牌运行 curl 时,我得到了预期的响应

curl -H "Authorization: Bearer $token" 'https://<my-api>.run.app/do-this&key=<key>'

为了完整性,这里也是端点 yaml

swagger: '2.0'
info:
  title: My first widget
  description: This is a great widget
  version: 1.0.0
host: <my-api>.run.app
schemes:
  - https
produces: 
  - application/json
paths:
  /do-this:
    get:
      summary: Do-this
      operationId: doit
      x-google-backend:
        address: https://<project-id>.cloudfunctions.net/do-that
      responses:
        '200':
          description: A successful response.
          schema:
            type: string
        '403':
          description: An error occurred
          schema:
            type: string
      security:
        - api_key: []

securityDefinitions:
  # This section configures basic authentication with an API key.
  api_key:
    type: "apiKey"
    name: "key"
    in: "query"

更新 esp 的命令:

gcloud run services update <my-api> --set-env-vars="^|^ENDPOINTS_SERVICE_NAME=<my-api>.run.app|ESP_ARGS=--rollout_strategy=managed,--cors_preset=basic" --project=<project-id> --platform=managed --region=europe-west1

更新

启用 cors 浏览器端没有帮助。

谷歌文档提到应该可以call from outside GCP

如果您从无权访问计算元数据的计算实例(例如您自己的服务器)调用服务,则必须手动生成正确的令牌: 自签名服务帐户 JWT,并将 target_audience 声明设置为接收服务的 URL。 将自签名的 JWT 交换为 Google 签名的 ID 令牌,该令牌应将 aud 声明设置为上述 URL。 在对服务的请求中将 ID 令牌包含在 Authorization: Bearer ID_TOKEN 标头中。 虽然 Cloud Run(全托管)尚不支持 Identity-Aware Proxy,但您可以查看 Identity-Aware Proxy 示例代码以获取上述步骤的代码示例。

The end-users section:虽然提到了 CORS

当您构建 Web 应用程序时,您必须考虑跨域资源共享 (CORS) 问题。例如,CORS 预检请求在没有授权标头的情况下发送,因此它们在非公共服务上被拒绝。因为预检请求失败,所以主请求也会失败。 要解决此问题,您可以在同一域中托管您的 Web 应用程序和服务,以避免 CORS 预检请求。您可以通过使用 Firebase 托管来实现。

我尝试在 Firebase 托管上托管 JS 脚本和 html,但问题仍然存在。

想到的另一个问题是:我需要在开放 API 规范中设置 OAuth 和 API 密钥身份验证吗?

更新 2

This discussion 建议无法将 Cloud Run 与支持 CORS 的身份验证一起使用。我还想知道为什么卷曲有可能。我正在使用服务帐户令牌进行身份验证,而不是最终用户。

【问题讨论】:

您是在浏览器中还是在后端应用程序中使用 Fetch API? @AhmetB:在浏览器中获取 API。 这似乎与您有关***.com/a/45640164/54929 我不认为您可以发送任意标头,但显然 CORS 可以。 谢谢@AhmetB。我检查了线程并尝试设置凭据 = true。进一步挖掘我发现了与这个主题相关的另外两个线程。也尝试了这些方法但没有成功(还)。 ***.com/a/55873861/967327 ***.com/a/63471693/967327 【参考方案1】:

没有为 Cloud Endpoint 激活 cors。像这样更新您的 openAPI 规范

swagger: '2.0'
info:
  title: My first widget
  description: This is a great widget
  version: 1.0.0
host: <my-api>.run.app
x-google-endpoints:
- name: <my-api>.run.app
  allowCors: True
...
...
...

或者按照错误消息的描述设置no-cors签入你的电话。

【讨论】:

我之前已经在环境“cors=basic”中尝试过。我实际上关注了你的中篇文章!不过,我会再次完成这些步骤 - 确保我没有错过任何事情。 我的文章没有提到 CORS。 确实如此。我将 cors 添加到 openAPI 并运行以下命令,但仍然出现相同的错误:gcloud endpoints services deploy ./esp/openapi.yaml --project &lt;project-id&gt;gcloud run services update &lt;api-name&gt; --set-env-vars="ENDPOINTS_SERVICE_NAME=&lt;my-api&gt;.run.app,--rollout_strategy=managed" --project=&lt;project-id&gt; --platform=managed --region=europe-west1【参考方案2】:

我已通过以下方式使其工作:

    oauth 作为安全定义添加到 OpenAPI 规范,并与每个 API 路径的 api 密钥一起使用 使用--set-env-vars="^|^ENDPOINTS_SERVICE_NAME=&lt;my-api&gt;.run.app|ESP_ARGS=--cors_preset=basic,--rollout_strategy=managed" 部署端点 在云函数中,将Access-Control-Allow-Origin设置为空字符串''res.setHeader("Access-Control-Allow-Origin", '') 允许allUsers 访问 Cloud Run 容器

虽然每个人都可以访问 Cloud Run 容器,但端点负责身份验证。

让我感到惊讶的是,CF 会自动在 Access-Control-Allow-Origin 标头中添加调用主机(例如 mydomain.com)和 *。此标题中不允许有多个项目,因此我将删除 mydomain.com 并保留 *

我将尝试使用不同的选项,完成后将提供包含所有相关步骤的操作方法。非常感谢任何 cmets/建议!

更新

深入挖掘后,我现在明白Access-Control-Allow-Originconst cors = require('cors')(origin: true);自动添加的

对于我的用例,我不需要 CF 内的 cors,因为它们只能从 Cloud Run ESP 访问。

因此重要的步骤是:

将 oauth 添加到 OpenAPI 规范 通过 ESP_ARGS 启用 cors 允许allUsers 访问云运行容器

【讨论】:

以上是关于Cloud Run + Cloud Endpoints + Service Account Authentication – 在 curl 中有效,但在 JS 中使用 fetch API 时无效的主要内容,如果未能解决你的问题,请参考以下文章

Cloud Run 完全托管连接到 Cloud SQL:这是不是支持 SQL Server?

将 Cloud Armor 与 Cloud Run 结合使用并避免绕过

Cloud Scheduler 调用 Cloud Run 服务的身份验证

通过启用 IAM 登录的 cloud-sql-proxy 从 Cloud Run 连接到 Cloud SQL

尝试运行 Cloud Run 作业时,Cloud Scheduler 的权限被拒绝

Cloud Run 完成,但 Cloud Scheduler 认为该作业已失败