0

I would like to fetch data from Firestore during server side rendering. I know I could use REST API (and attach the token to the request's headers) but I don't want to write REST requests on server side and then duplicate the same requests on client side using standard Firestore queries. On client I prefer standard queries (no REST) because of the realtime updates. And I would like to reuse the queries from client also on the server (even without the benefit of realtime updates).

I validate the token manually on the server:

import admin from 'firebase-admin';
import { initializeApp, getApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';

const firebaseApp = initializeApp(config);
const db = getFirestore(firebaseApp);

const decodedIdToken = await admin.auth().verifySessionCookie(sessionCookie);
// => token verified: decodedIdToken.userId = "xxx"

But when I want to perform a query:

import { collection, getDocs } from 'firebase/firestore';

const querySnapshot = await getDocs(collection(db, 'myCollection'));

I get error:

{
  "code": "permission-denied",
  "name": "FirebaseError"
}

Firestore rules:

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth != null;
    }
  }
}

On client side the error could be solved using onAuthStateChanged but I can't use this listener on server.

Is there any way to run Firestore queries with manually verified token?

2
  • 1
    Can you provide the Firestore rules for your project based on this documentation? Commented Oct 21, 2022 at 5:34
  • @RobertG Question updated with the rules. Commented Oct 21, 2022 at 11:10

1 Answer 1

0

I've managed to replicate your error. You're getting this error because you're trying to use client SDK instead of firebase-admin.

Here's the sample code for your reference:

import admin from 'firebase-admin';
import { initializeApp } from 'firebase-admin/app';
import { getFirestore } from "firebase-admin/firestore";

const firebaseApp = initializeApp(config);
const db = getFirestore(firebaseApp);

// const decodedIdToken = await admin.auth().verifySessionCookie(sessionCookie);
// => token verified: decodedIdToken.userId = "xxx"

// const querySnapshot = await getDocs(collection(db, 'myCollection'));
db.collection("myCollection").get().then((querySnapshot) => {
    querySnapshot.forEach((doc) => {
        // doc.data() is never undefined for query doc snapshots
        console.log(doc.id, " => ", doc.data());
    });
});

I've used version 8 (commonJS) instead of version 9 (modular) as firebase-admin still uses the dot notation syntax.

[sampleQuery]  =>  { test: 'testing' }

Here's the link on how to get all documents in a collection.

Here's another reference on upgrading to Node.js SDK Admin SDK v10 (modular SDK).


Update:

If you wanted to use the Firestore Security Rules, you need to use custom signed tokens, you need to pass it to signInWithCustomToken so that the client auth can sign in.

Below is a sample code for your reference:

import { getAuth, signInWithCustomToken } from "firebase/auth";

const auth = getAuth();
signInWithCustomToken(auth, token)
  .then((userCredential) => {
    // Signed in
    const user = userCredential.user;
    // ...
  })
  .catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
    // ...
  });

A new user will be created and linked all their credentials and the new account will be stored as part of your project, and will be used to identify a user across every app in your project.

You can also allow a user to sign out by calling signOut:

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

You can check this documentation on authenticating with Firebase for additional information.

Sign up to request clarification or add additional context in comments.

2 Comments

I want to use client SDK also on the server. The reason is I want to run the queries on behalf of the logged user in order to apply Firestore rules. So I imagine I need to somehow "sign" the requests from my server to Firestore with client's token.
@PetrK., please check as I've updated my answer. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.