访问 Firebase Cloud Storage 服务器端并应用安全规则

时间:2021-01-04 10:09:32

标签: node.js firebase google-cloud-firestore firebase-authentication

在我的 Firebase 应用中,我想知道代表登录用户(通过 ID 令牌进行身份验证)向 Cloud Storage for Firebase 发出服务器端请求的推荐方式,并将安全规则应用于请求(即不使用 Admin SDK)。

目前的整体应用流程如下所示:

  • 用户登录客户端并向 Google Cloud API 网关发出请求,在请求标头中发送他们的 ID token
  • API 网关 authenticates user 并将请求转发到后端 API 中的不同端点:
    • 如果是对 Firebase 身份验证的请求,API 会使用 Auth Admin SDK 直接处理它
    • 如果是针对 Firestore,则 API 使用 API 网关转发的 x-forwarded-authorization 请求标头(其中包含最初由客户端提供的 ID 令牌),然后向 {{ 3}},以便可以通过安全规则评估请求。

对于 Cloud Storage,我想做一些类似于上面 Firestore 案例的事情,但文档中没有特定于 Firebase 的 REST API。可以让客户端直接向 Firebase 发出请求(如本 Firestore's REST API 中建议的那样),但更希望将逻辑保留在后端。

对于存储,是否有其他方法可以执行此操作?请随时指出是否有更好的方法来处理上述 Firebase Auth 和 Firestore 案例。谢谢!

编辑:添加更多可能的解决方案

  • Doug 的回答 answer 和 Frank 的回答 here 建议使用 Cloud Storage REST API,其中应用程序为用户生成 OAuth 令牌并here
  • 此答案 makes the request 提到可以将 auth=IDTOKEN 查询参数传递给 Firebase REST API:
<块引用>

获得 ID 令牌后,您可以通过以下方式将其传递给 REST API auth 查询参数来验证请求。请求尊重 Firebase 安全规则,就好像登录客户端的最终用户是 提出请求。

3 个答案:

答案 0 :(得分:1)

在使用 Admin SDK 时,无法为 Cloud Storage 强制执行 Firebase 的安全规则。 Firebase 没有为 Cloud Storage 提供记录在案的 REST API,而且我怀疑 Google Cloud REST API for Storage 是否接受 Firebase ID 令牌。

所以我真的不认为您有很好的选择来代表您的用户从服务器执行此操作。我能想到的两个选项:

  1. 将必要的信息传回给客户端,并让其通过 Firebase SDK 访问数据 - 在这种情况下会强制执行安全规则。
  2. 在您的 Cloud Functions 代码中复制必要的安全逻辑。

答案 1 :(得分:1)

编辑 2020 年 1 月 6 日:此方法已在重载下进行了几天的测试。有关一些次要的新要求,请参见底部。

原始回复:这不是答案,而是一种生态系统内的解决方法(对我有用!)

基本上,我有一个类似的用例,涉及离线优先/可能不安全的物联网设备,该设备需要具有安全规则的基于身份验证的存储访问。今天早上,我查看了 Firebase js sdk repo 并注意到原生 XHR API 是工作节点实现中缺少的主要部分。

我详细介绍了我在 this github issue 上的解决方法 ... 为方便起见,这里包括:

<块引用>

.. 使用 XHR polyfill 修补存储库实际上似乎有效(Node 15 / 迄今为止的最少测试)。具体来说,我已经安装了 xmlhttprequest-ssl 并将其导入到存储模块的顶部(在我的例子中是 @firebase/storage/dist/index.cjs.js 中的第 5 行):

var XMLHttpRequest = require("xmlhttprequest-ssl").XMLHttpRequest;
<块引用>

回到应用中,我照常导入 firebase/storage,然后从本地文件系统加载文件并按照标准 Firebase API 上传:

import { firebase } from "@firebase/app";
import "@firebase/storage";

/* include Firebase setup, etc. */

const filename = 'offline-user-generated-image.jpg';
const file = fs.readFileSync(filename); // for brevity only
const ref = firebase.storage().ref().child(`test/${filename}`);

// use the buffer's underlying arraybuffer 
ref.put(file.buffer, {
  contentType: 'image/jpg', // defaults to 'application/octet-stream'
  customMetadata: {
    uid: 'abc123', // for enhanced security rules
  },
})
.then(() => console.log('done'))
.catch(e => console.log(e));
<块引用>

存储规则适用于上传(哇!),安全令牌和自定义元数据按预期应用。也用 `putString' 测试过。这为我的用例解决了一些相当激烈的解决方法,因为这意味着我可以保留我的逻辑/身份验证/等。完全在 Firebase 生态系统中。渴望听到别人的想法。

注意:我使用 patch-package 来确保补丁在安装/升级过程中保持完整。

2020 年 1 月 6 日更新:一些额外的细节

事实证明,存储包中存在未清除的超时,导致节点进程陷入困境(并且可能是所有环境中的资源浪费)..我有 submitted a PR(几乎是 {{1 }}) 解决了问题,但与此同时,这也需要手动修补,除非您可以安全地clearTimeout 在您的代码中。

答案 2 :(得分:0)

在考虑了上述各种选项后(感谢 Frank 和 som!),在我的情况下,最好的选择是进行更改并直接从客户端发出存储请求。两个主要原因是:

  • 这是最简单的实现,因为已经有一个 SDK 允许您执行此操作
  • 与 Firebase Storage 交互可能涉及大型对象,因此如果网络行程过多,用户体验可能会受到影响

从客户端发出请求时,如果出现 XMLHttpRequest is not defined 错误,上面的 som 和 this answer 的回答可能会很有用