I have chatrooms stored in Cloud Firestore that need to be secured and I'm having a difficult time doing so. My state in React looks as such:
export class Messages extends React.Component {
constructor(props) {
super(props);
this.boardID = this.props.settings.id;
this.mainChatRef = database
.collection("boards")
.doc(boardID)
.collection("chats")
.doc("MAIN")
.collection("messages");
this.chatRoomsRef = database
.collection("boards")
.doc(boardID)
.collection("chats");
}
My query in react looks like:
latestMessages = (messageSnapshot) => {
const message = [];
messageSnapshot.forEach((doc) => {
const { text, createdAt, uid } = doc.data();
message.push({
key: doc.id,
text,
createdAt,
uid,
});
});
this.setState({
dataSource: message,
});
}
queryRooms = async () => {
const recentQuery = await this.chatRoomsRef
.where("uidConcat", "in", [
this.props.user.uid + this.state.value.objectID,
this.state.value.objectID + this.props.user.uid,
])
.get();
for (const qSnap of recentQuery.docs) {
const messagesRef = this.chatRoomsRef
.doc(qSnap.id)
.collection("messages")
.orderBy("createdAt")
.limitToLast(30);
messagesRef.onSnapshot(this.latestMessages);
}
}
My database structure:
boards(collection)
{boardId}
chats (collection)
MAIN
{chatId_1}
{chatId_2}
uidArray (has only two UIDs since it's for private chat)
uidConcat: user1_uid+user2_uid
messages(collection)
{message1}
createdAt
text
uid_creator
uidArray (has UID of user1 and user2)
I tried securing my boards as such:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /boards/{boardId} {
allow read, write: if request.time < timestamp.data(2050, 5, 1);
match /chats/MAIN/messages/{messagesId=**} {
allow read, create: if request.auth.uid !=null;
}
match /chats/{chatId} {
allow read, write: if request.auth.uid !=null;
match /messages/{messagesId=**} {
allow read: if (request.auth.uid in get(/databases/{database}/documents/boards/
{boardId}/chats/{chatId}/messages/{messageId}).data.uidArray)
&& request.query.limit <= 30 && request.query.orderBy.createdAt == 'ASC';
allow write: if request.auth.uid != null;
}
}
}
}
}
I wish to have anyone that's authenticated to always have access to the boards MAIN chat and the way I have it written it works. For private rooms I keep getting an error. I've read the documents and I know that Security rules are not filters which is why I added a bunch of AND statements in an attempt to closely resemble my securities to my written code query in React yet I still keep getting Uncaught Errors in snapshot listener: FirebaseError: Missing or insufficient permissions. I am currently writing my rules to secure at the document level within the messages collection but Ideally I'd like to secure at the document level in the chat colection and check if my request.auth.uid is in the document field uidArray as so :
match /chats/{chatId=**} {
allow read, write if request.auth.uid in resource.data.uidArray
}
I imagine securing the documents in the chat collection would be more secure but any method that secures messages is more than appreciated.