0
votes

There is a little problem here with the Firestore security rules. I have a React/Redux SPA with a custom API using Firebase and Firestore.

I currently use the session cookies as described by the Firebase official documentation (https://firebase.google.com/docs/auth/admin/manage-cookies). So when I log in, the cookie is set and sent with the subsequent requests to the API, no problem with that.

But now when I'm logged in with the session cookie system, the Firestore rules don't let me read or write data with a "permission denied" error since the request.auth object of the Firestore request is no longer set with the session cookie system...

How can I detect if a user is authenticated through a session cookie in my Firestore security rules?

login route in my Firebase external API using session cookie:

login(req, res) {
auth.setPersistence(firebase.auth.Auth.Persistence.NONE);
auth.signInWithEmailAndPassword(req.query.email, req.query.password)
  .then((response) => response.user.getIdToken().then((idToken) => {
    const expiresIn = 60 * 15 * 1000;
    admin.auth().createSessionCookie(idToken, { expiresIn })
      .then((sessionCookie) => {
        const options = { maxAge: expiresIn, httpOnly: true, secure: !!process.env.NODE_ENV === 'production' };
        res.cookie('session', sessionCookie, options);
        auth.signOut();
        res.json(response);
      }, () => {
        res.status(401).write('Error while verifying token.');
      });
  })).catch((err) => {
    console.error(err);
  });
},

basic user get in the same API using Firestore; called right after login with cookie set in request header:

get(req, res) {
  const { uid } = req;
  db.collection('users').doc(uid).get().then((doc) => {
    res.json(doc.data());
  })
  .catch((err) => {
    console.log(req.path);
    console.error(err);
    res.json(err);
  });
},

permission denied error from Firestore security rules:

Error [FirebaseError]: Missing or insufficient permissions.

the old Firestore security rules that i need to work:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    function isSignedIn() {
      return request.auth != null;
    }
    match /users/{userId} {
      allow create, update, get: if isSignedIn();
    }
    match /medic/{medicId} {
      allow create, read: if isSignedIn();
    }
  }
}

Thanks a lot!

1

1 Answers

0
votes

Ok so I found the solution here. I am not using the Firebase Admin SDK in my server-side API, this is why Firestore security rules are checked since the system thinks this is a client-side request.

I have to restrict all operations in Firestore security rules and then use the Firebase Admin SDK to do my database operations in the API (obviously I need to check the data and the authentication myself since Firestore security rules are not used anymore; but that's already the case thanks to Firebase session cookie)

As you explore security rules in depth, you will eventually discover that requests from the Firebase Admin SDK are not gated by rules. The Admin SDK is initialized with a service account, which gives the SDK full access to your data. In Firebase Realtime Database, you can scope the Admin SDK's privileges to a user ID, and enforce rules as usual. But other products, most notably Google Cloud Firestore, don't support this feature yet. You should be mindful about that when implementing server-side data access operations.

see: https://firebase.googleblog.com/2019/03/firebase-security-rules-admin-sdk-tips.html