我希望在服务器端尽可能清除凭证。因此,我使用Firebase Auth Rest Api向Firebase Auth进行身份验证。从请求中获得Firebase ID令牌,并使用此令牌按照Authenticate Rest Request (Authenticate with an ID token)的说明向Firebase Realtime Database发出请求。
问题是,当我尝试对Firebase Storage (Google Cloud Storage Request Endpoints)执行相同操作时,找不到任何避免将凭据存储在服务器端的解决方案(例如,使用Admin SDK,我可以写入或读取任何文件) ,但这意味着服务器的安全性将来可能出现问题,因为暴露了我的凭据)尽管在Authenticate Rest Request (Authenticate with an ID token)中明确指出:“当用户或设备使用Firebase身份验证登录时,Firebase会创建一个相应的ID令牌可以唯一地标识它们,并授予他们访问多种资源的权限,例如实时数据库和云存储。”
问题是:如何像使用Firebase Runtime Database一样使用Firebase ID令牌授权Firebase Storage Api Rest调用?
谢谢。
答案 0 :(得分:2)
最后,我找到了答案的解决方案。解决方案是使用云功能。
Cloud Functions允许我们在nodejs环境中创建端点并使用AdminSdk,这是我们的Firebase项目的一部分。通过这种方法,我们可以向该端点发送一个http请求,这将检查接收到的带有请求的Token是否有效,如果是,它将保存文件。
这是功能代码:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const path = require("path");
const os = require("os");
const fs = require("fs");
const Busboy = require("busboy");
// Follow instructions to set up admin credentials:
// https://firebase.google.com/docs/functions/local-emulator#set_up_admin_credentials_optional
admin.initializeApp({
credential: admin.credential.cert(
__dirname + "/path/to/cert.json"
),
storageBucket: "bucket-name",
});
const express = require("express");
const app = express();
// Express middleware that validates Firebase ID Tokens passed in the Authorization HTTP header.
// The Firebase ID token needs to be passed as a Bearer token in the Authorization HTTP header like this:
// `Authorization: Bearer <Firebase ID Token>`.
// when decoded successfully, the ID Token content will be added as `req.user`.
const authenticate = async (req, res, next) => {
if (
!req.headers.authorization ||
!req.headers.authorization.startsWith("Bearer ")
) {
res.status(403).send("Unauthorized");
return;
}
const idToken = req.headers.authorization.split("Bearer ")[1];
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
req.user = decodedIdToken;
next();
return;
} catch (e) {
res.status(403).send("Unauthorized");
return;
}
};
app.use(authenticate);
// POST /api/messages
// Create a new message, get its sentiment using Google Cloud NLP,
// and categorize the sentiment before saving.
app.post("/test", async (req, res) => {
const busboy = new Busboy({ headers: req.headers });
const tmpdir = os.tmpdir();
// This object will accumulate all the fields, keyed by their name
const fields = {};
// This object will accumulate all the uploaded files, keyed by their name.
const uploads = {};
// This code will process each non-file field in the form.
busboy.on("field", (fieldname, val) => {
// TODO(developer): Process submitted field values here
console.log(`Processed field ${fieldname}: ${val}.`);
fields[fieldname] = val;
});
const fileWrites = [];
// This code will process each file uploaded.
busboy.on("file", (fieldname, file, filename) => {
// Note: os.tmpdir() points to an in-memory file system on GCF
// Thus, any files in it must fit in the instance's memory.
console.log(`Processed file ${filename}`);
const filepath = path.join(tmpdir, filename);
uploads[fieldname] = filepath;
const writeStream = fs.createWriteStream(filepath);
file.pipe(writeStream);
// File was processed by Busboy; wait for it to be written.
// Note: GCF may not persist saved files across invocations.
// Persistent files must be kept in other locations
// (such as Cloud Storage buckets).
const promise = new Promise((resolve, reject) => {
file.on("end", () => {
writeStream.end();
});
writeStream.on("finish", resolve);
writeStream.on("error", reject);
});
fileWrites.push(promise);
});
// Triggered once all uploaded files are processed by Busboy.
// We still need to wait for the disk writes (saves) to complete.
busboy.on("finish", async () => {
await Promise.all(fileWrites);
// Process saved files here
for (const file in uploads) {
admin.storage().bucket().upload(uploads[file], function(err, file) {
if (err) {
res.status(403).send("Error saving the file.");
}
res.status(201).send("Saved");
});
}
});
busboy.end(req.rawBody);
});
exports.api = functions.https.onRequest(app);
答案 1 :(得分:0)
如果您需要访问服务器端的Firebase存储,则将无法避免将凭据存储在某处。您唯一可以做的就是通过API请求将在客户端上获得的用户凭据传递给服务器。尽管这样做不会给您带来任何安全好处,因为如果攻击者可以访问您的服务器,那么他仍然可以访问您的存储。
通常,将存储凭据存储在服务器上是安全的。无论如何,您都需要使服务器尽可能安全。
答案 2 :(得分:0)
由于Firebase Storage实际上只是对Cloud Storage的重新包装,因此您可以使用Cloud Storage JSON API处理存储桶中的内容。从user account credentials的部分开始。您需要为Firebase Auth用户提供OAuth令牌,以与请求一起发送。
答案 3 :(得分:0)
由于目前尚无解决方案,我决定将二进制格式的文件存储在实时数据库中。 这样,我不需要在服务器中公开我的凭据,因为Firebase ID令牌可以进行身份验证。