0

I am using Firebase + NextJS where I set up the authentication with NextAuth and FirestoreAdapter. I am using the following allow-all rules for debugging and all of my intended features are working perfectly.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true
    }
  }
}

However, I know this is a huge security issue when I push the code to production and wish to add more specific rules so only document owners can read and write the data. I have tried this solution from this github issue with no success.

match /store/{userId}/{document=**} {
        allow read, write: if request.auth.token.id == userId && exists(/databases/$(database)/documents/tokens/$(request.auth.uid)/sessions/$(request.auth.token.sessionToken));
}

I believe the main issue comes from the way nextAuth and FirestoreAdapter is interacting with my Firestore database. When I create a new document using the following code, it creates the document in “users → session.user.id → chats → document” as per the screenshot below, but the User UID and session.user.id is not the same which is why I think the code above is not working.

Is there a proper way to set up security rules so DB read/write is only allowed when session.user.id == chatDoc.userId?

enter image description here

Create New Draft

const createNewDraft = async () => {
      const doc = await addDoc(
        collection(db, "users", session?.user?.id!, "drafts"),
        {
          userId: session?.user?.id!,
          createdAt: serverTimestamp(),
        }
      );
 };

[…nextAuth].ts

import { FirestoreAdapter } from "@next-auth/firebase-adapter";
import { GoogleAuthProvider, signInWithCredential } from "firebase/auth";
import { cert } from "firebase-admin/app";
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import "firebase/firestore";

import { fbAuth } from "../../../../firebase";

const sa = JSON.parse(process.env.NEXT_PUBLIC_FIREBASE_SERVICE_KEY);

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!,
      clientSecret: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    async signIn({ user, account, profile, email, credentials }) {
      try {
        const googleCredential = GoogleAuthProvider.credential(
          account?.id_token
        );
        const userCredential = await signInWithCredential(
          fbAuth,
          googleCredential
        ).catch((e) => {
          console.log(e);
          return false;
        });
        return !!userCredential;
      } catch (e) {
        console.log(e);
        return false;
      }
    },
    session: async ({ session, token }) => {
      if (session?.user) {
        session.user.id = token.sub;
      }
      return session;
    },
  },
  session: {
    strategy: "jwt",
  },
  adapter: FirestoreAdapter({
    credential: cert({
      projectId: sa.project_id,
      clientEmail: sa.client_email,
      privateKey: sa.private_key,
    }),
  }),
};
export default NextAuth(authOptions);
Tyler Kim
  • 181
  • 1
  • 11
  • What is `sessionToken`? You're not showing where that gets set in the user's custom claims. Without that, the rule will always reject. – Doug Stevenson Jul 03 '23 at 13:14
  • Is it not possible to implement firestore security rules with Next Auth Firebase adapter as [@auth/firebase-adapter](https://authjs.dev/reference/adapter/firebase#installation) uses firebase admin sdk to initialize the firestore DB and firebase admin sdk bypass all cloud firestore security rules.check this [document](https://firebase.google.com/docs/firestore/security/get-started) & stackoverflow [thread](https://stackoverflow.com/a/57761870/18265638) – Sathi Aiswarya Jul 03 '23 at 14:18
  • @SathiAiswarya That means it should work technically with just rejecting everything by using the following rule, right? Both documents don't seem to work in my case. Are there any other recommendations you suggest? ``` match /{document=**} { allow read, write: if false; } ``` – Tyler Kim Aug 11 '23 at 05:25

0 Answers0