I am currently working on an app where I need to restrict access for reading, writing (etc..) a document in a collection. However, I am still able to fetch the data.
Brief explanation of the structure and code:
(I have simplified the code and images for ease of understanding)
This is a representation of the current Firestore structure:
I have a collection of "groups", whereas each group will have members and owners. Every user is defined in the collection of "users" and is assigned a user-ID (the user-IDs are used for assigning users to a group).
Firstly, Let's say you want to fetch all groups (GET /api/groups
), all groups given from the API should be all the groups you are a part of. If you are not a part of the group, it should not be returned.
If I'm part of a group I would expect to GET a list of groups; e.g.
[
{groupName: 1, members: ["..."], owners: ["myId"]},
{groupName: 2, members: ["myID"], owners: ["..."]}
]
If I'm not a part of any groups, I would expect an empty list []
or some error..
Next.Js:
In the file pages/api/groups/index.tsx
I have created an API handler that fetches all the available groups in the collection "groups":
import { NextApiRequest, NextApiResponse } from 'next';
import db from 'config/db';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const groupsRef = await db.collection('groups');
if (req.method === 'GET') {
const groups = await groupsRef.get();
const groupsData = groups.docs.map((doc) => doc.data());
res.status(200).json(groupsData);
}
if (req.method === 'POST') {
...
}
} catch (err) {
res.status(400).end();
}
}
Firestore Rules
For ease of testing I have created a simple rule that should restrict any READING or writing from the collection "groups":
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /groups/{id} {
allow read, write: if false;
}
}
}
After Publishing, I expected an error or empty list. I however still got the data from groups when fetching the data in e. g. pages/index.tsx
:
import React from 'react';
import axios from 'axios';
import useSWR from 'swr';
const fetcher = (url: string) => axios.get(url).then((res) => res.data);
export default function index() {
const { data, error } useSWR('/api/groups', fetcher);
console.log(data);
console.log(error);
if (!data) { ... }
if (error) { ... }
return (<div> ... </div>)
}
Have I misunderstood the purpose of Firestore Rules? Or is there something wrong with the security rules? Why do I still get the data?
[EDIT] (answering Dharmaraj's question):
Can you share import db from 'config/db'; file? Are you using the Admin SDK (that bypasses all the security rules)?
inside /config/db/index.js
import admin from 'firebase-admin';
import serviceAccount from './serviceAccountKey.json';
if (!admin.apps.length) {
try {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
} catch (error) {
console.log('Firebase admin initialization error', error.stack);
}
}
export default admin.firestore();
You are using the Firebase Admin SDK that has privileged access to your Firebase services and bypasses any security rules. Also, Security rules are not filters. You need to specify that in your.
If you are fetching your groups then use .where("members", "array-contains", "USER_ID")
. Otherwise you are fetching the whole collection.