firebase / firebase-js-sdk

Firebase Javascript SDK
https://firebase.google.com/docs/web/setup
Other
4.82k stars 886 forks source link

Firestore returns permission-denied when node instance restarts even if session cookie is verified #2669

Open Krucial92 opened 4 years ago

Krucial92 commented 4 years ago

Environment:

Issue:

I believe I have found a bug with firestore, and have finally found what causes the issue to occur. Essentially firestore incorrectly returns a "permission-denied" response despite security rules being correct and the session cookie being verified.

This happens only in development when the node instance restarts!

Whether or not the NodeJS server restarts, if verifySessionCookie() passes and the user is logged in there should be no issue fetching documents which require auth from firestore.

How to reproduce:

I have created a minimal repo which demonstrates this exact issue: https://github.com/Krucial92/firestore-bug-example

Please follow these steps to reproduce:

1) Clone the repo and install dependencies for both the client & server 2) Inside /server/src/private/firebase-cert.json add your private key 3) Update /server/.firebaserc with your project, also go into /server/src/firebase.js and fill out the TODOs 4) Add this basic security rule into firestore:

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {

    // Users
    match /users/{userId} {
        allow read, write: if request.auth.uid == userId;
    }
  }
}

5) Go into ./client and run npm start, also go into ./server and run npm start 6) Now in the browser click "Sign Up", this will take a second. 7) Click "Fetch current user", you will see in the network requests that this is successful and you can fetch the users name "Johnny" from firestore. 8) Now restart the NodeJS server (you can also write something and let it hot-reload) 9) Now in the browser again click "Fetch current user" and you will see the network request fails with permission-denied

Other info

I really hope that this can be fixed soon, as it causes a very confusing developer experience. This lead me to believe that there was something wrong with my application or security rules, but further investigation into the issue seems like it is a problem with firestore.

What is happening on the backend and why would this occur? Appreciate any help or pointers in regards to this issue.

thebrianchen commented 4 years ago

@Krucial92 Thanks for reporting the issue! I was able to reproduce the error with your repo, and I'll update this issue once I investigate.

Krucial92 commented 4 years ago

@thebrianchen thanks for looking into this, it will be great if we can get a fix in.

thebrianchen commented 4 years ago

@Krucial92

You're using Firebase Admin's session cookies, which are incompatible with Firestore, RTDB, etc. Whenever the Firestore instance is restarted, states are lost, and no user is logged in, which means that you'll need to re-auth again. According to the manage cookies documentation:

For security reasons, Firebase session cookies cannot be used with other Firebase services due to their custom validity period, which can be set to the maximum duration of 2 weeks. All applications using server side cookies are expected to enforce permissions checks after verifying these cookies server side.

verifySessionCookie will return true until the underlying JWT expires or is revoked. In your use case, it makes more sense to use Firestore in the Admin SDK instead of trying to use the Firestore SDK directly.

Krucial92 commented 4 years ago

@thebrianchen thank-you for the clarification, appreciate your time. This did indeed turn out to be the issue.

One thing I would like to clarify, is it valid to use the Client SDK on the backend with the Admin SDK (doing most of the work), for these specific methods which are not provided by the Admin SDK such as signInWithEmailAndPassword, verifyPasswordResetCode & confirmPasswordReset?

NixBiks commented 10 months ago

I just want to make sure I understand this correctly. Does this mean that you can't use realtime updates client side if you are using Firebase Admin's session cookies? No way to hack around it?

tom-andersen commented 10 months ago

@NixBiks Someone with auth knowledge will have to give a more complete answer.

What I can tell you about real time updates is that the snapshot listener will stop when the auth token expires. At that point, the SDK will automatically reconnect with a fresh auth token and resume where it left off. The Firestore component relies on auth to provide the token. So long as auth can provide valid tokens, the real time updates will work.

At this point, someone with knowledge of auth will need to describe under what conditions auth can provide valid tokens, and for how long. If a hack is used, then the hack needs to be applied periodically so that there is always a valid token. Otherwise, realtime time update through the snapshot listener will cease to function at some point.

NixBiks commented 10 months ago

@NixBiks Someone with auth knowledge will have to give a more complete answer.

What I can tell you about real time updates is that the snapshot listener will stop when the auth token expires. At that point, the SDK will automatically reconnect with a fresh auth token and resume where it left off. The Firestore component relies on auth to provide the token. So long as auth can provide valid tokens, the real time updates will work.

At this point, someone with knowledge of auth will need to describe under what conditions auth can provide valid tokens, and for how long. If a hack is used, then the hack needs to be applied periodically so that there is always a valid token. Otherwise, realtime time update through the snapshot listener will cease to function at some point.

I went ahead and implemented server auth with cookie while also keeping auth state client side (ie. persisting when logging in / fetching the token). Then I implemented some additional logic to keep server and client auth state in sync.