【发布时间】:2020-08-01 13:41:43
【问题描述】:
我为我的 firebase 数据库设置了以下规则:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if request.auth.uid != null;
}
}
}
我想创建一个写入规则,该规则仅允许用户在拥有有效访问令牌的情况下进行写入。我不认为上述规则是正确的,我对 Firebase 如何跟踪用户授权感到非常困惑。
到目前为止,这就是我设置应用程序的方式:
在前端(React)上,我有一个简单的电子邮件/密码登录组件:
axios.get('/auth', {
headers: {
username: this.usernameRef.current.value,
password: this.passwordRef.current.value
}
}).then(...).catch(...);
我的后端(Node.js/express)收到请求:
const fb = require('firebase');
const initFirebase = () => {
const config = {
apiKey: "***",
authDomain: "***",
databaseURL: "***",
projectId: "***",
storageBucket: "***",
messagingSenderId: "***",
appId: "***",
measurementId: "***"
};
const app = fb.initializeApp(config);
return app.firestore();
}
router.get('/', (req, res, next) => {
const email = req.headers.username;
const password = req.headers.password;
let fbApp;
if (!fb.apps.length) {
try {
initFirebase();
} catch (err) {
res.status(500).send('Error initializing Firebase.');
return;
}
}
fbApp = fb.apps[0];
fbApp.auth().signInWithEmailAndPassword(email, password).then(user => {
res.status(200).send(JSON.stringify(user));
}).catch(err => {
res.status(401).send();
});
});
如果发送到 Firebase 以验证用户身份的请求成功,它会返回一个用户对象,其中包含访问令牌。我将用户对象返回到前端。前端将访问令牌存储在 localStorage 和 State 中。然后对于任何后续对 firebase 的请求,它会将令牌注入到请求的标头中。
例如,如果我想在我的博客中添加新帖子,我会这样做:
前端:
axios.post('/blogs', {
title: this.state.newPost.title,
body: this.state.newPost.body
}, {
headers: {Authorization: this.state.accessToken}
}).then(response => {...}, err => {...});
后端接收它并这样做:
const fb = require('firebase');
const initFirebase = () => {
const config = {
apiKey: "***",
authDomain: "***",
databaseURL: "***",
projectId: "***",
storageBucket: "***",
messagingSenderId: "***",
appId: "***",
measurementId: "***"
};
const app = fb.initializeApp(config);
return app.firestore();
}
router.post('/', (req, res, next) => {
if (!req.headers.authorization) {
res.status(401).status('Unauthorized');
return;
}
const chunks = [];
req.on('data', chunk => chunks.push(chunk));
req.on('end', () => {
const data = JSON.parse(chunks);
const post = {};
post.title = data.title;
post.body = data.body;
post.createdAt = Date.now();
post.updatedAt = post.createdAt;
let firestore;
if (fb.apps.length) firestore = fb.apps[0].firestore();
else {
try {
firestore = initFirebase();
} catch (err) {
res.status(500).send('Error initializing Firebase.');
return;
}
}
firestore.collection('blogposts').add(post).then(docRef => {
res.status(200).send(JSON.stringify({id: docRef.id, createdAt: post.createdAt}));
}).catch (err => {
res.status(500).send('Error posting blog post.');
});
});
});
在简单的用例场景中,这是可行的。
我不明白安全规则如何在每个用户的基础上工作。只要用户(任何用户)经过身份验证,似乎 request.auth.uid(在上面的写入规则中)就已设置。
我尝试通过从 localStorage 和 State 中删除访问令牌来删除前端的访问令牌,从而模仿未登录的用户。然后我尝试创建一个新帖子。有效。所以 Firebase 显然不需要我发送有效的访问令牌来通过上面的写入规则。
我尝试直接从完全不同的应用程序(用 Node.js 编写)创建对 firebase 的请求:
const fb = require('firebase');
const config = {
apiKey: "***",
authDomain: "***",
databaseURL: "***",
projectId: "***",
storageBucket: "***",
messagingSenderId: "***",
appId: "***",
measurementId: "***"
};
const store = fb.initializeApp(config).firestore();
const post = {
title: 'blog #14',
body: 'This is blog #14.',
createdAt: Date.now(),
updatedAt: Date.now()
}
store.collection('blogposts').add(post).then(docRef => {
console.log('docRef.id = ', docRef.id);
}).catch (err => {
console.log('err=', err);
});
这失败了,来自 Firebase 的 PERMISSION_DENIED 错误。这告诉我在 initializeApp(...) 中创建的 Firebase 应用程序必须与它有关。我的后端为所有请求重新使用相同的应用程序,包括身份验证请求。此端应用程序创建了一个全新的 Firebase 应用程序。是跟踪用户是否经过身份验证的应用程序吗?它是否保留访问令牌的副本并将其与所有请求一起隐式发送到 Firebase?
奇怪的是,这个测试似乎取消了我的主要用户的授权。回到原来的应用程序,我开始收到相同的 PERMISSION_DENIED 错误。我不得不从 localStorage 中删除访问令牌,刷新页面,再次登录,这似乎重置了它。
如何编写检查访问令牌的 Firebase 规则?以及如何将访问令牌与请求一起发送到 Firebase?如果这一切都在用于发出请求的 Firebase 应用中,我如何确保每个人使用不同的应用?
真正有帮助的是描述前端应该做什么来启动进程(必要时发送访问令牌的地方),后端应该如何处理这个请求,后端应该如何调用 Firebase,以及 Firebase 规则的外观。
非常感谢您即将提供的任何帮助。
【问题讨论】:
标签: javascript node.js firebase firebase-authentication firebase-security