NextAuth的Firebase安全规则

8wtpewkr  于 2023-11-21  发布在  其他
关注(0)|答案(1)|浏览(142)

我使用Firebase + NextJS,在那里我设置了NextAuth和FirestoreAdapter的身份验证。我使用以下allow-all规则进行调试,所有我想要的功能都完美地工作。

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

字符串
然而,我知道这是一个巨大的安全问题,当我把代码推到生产环境,并希望添加更多的具体规则,只有文档所有者可以读取和写入数据。我已经尝试了这个解决方案从this github issue没有成功。

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));
}


我认为主要的问题来自于nextAuth和FirestoreAdapter与我的Firestore数据库交互的方式。当我使用下面的代码创建一个新文档时,它会按照下面的截图在“users → www.example.com → chats → document”中创建文档session.user.id
是否有适当的方法来设置安全规则,以便只有在session.user.id == chatDoc.userId时才允许DB读/写?
x1c 0d1x的数据
创建新草图

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);

gcuhipw9

gcuhipw91#

我也面临着同样的问题,因为出于明显的安全原因,没有办法将用户会话传递给Firebase安全规则。
经过一番研究,我找到了两种可能的解决方案。
(1)使用Firebase Admin SDK从服务器端结构化所有数据库CRUD访问,因为Firebase管理员绕过所有安全规则,然后您可以将数据库读写设置为false以保护所有其他访问。这种结构现在可以使用NextJS app router,其中默认服务器组件,并使用server actions在客户端组件上执行服务器端操作。
(2)使用Firebase Auth的signInWithCustomToken()方法通过从NextAuth传递用户会话来登录Firebase Auth。这需要从现有的NextAuth设置中额外增加2个步骤。
使用Firebase管理员的createCustomToken()从API路由创建自定义令牌(应用程序路由器示例)。

// api/createCustomToken/route.js

import { NextResponse } from "next/server";
import { adminAuth } from "@/firebase/firebaseAdmin";

export async function POST(req, res) {
  if (req.method !== "POST") {
    return NextResponse.json(
      { message: "Method not allowed" },
      { status: 405 }
    );
  }

  const { user } = await req.json();

  try {
    const customToken = await adminAuth.createCustomToken(user.id);
    return NextResponse.json({ token: customToken }, { status: 200 });
  } catch (error) {
    return NextResponse.json(
      { message: "Error creating custom token" },
      { status: 500 }
    );
  }
}

字符串
获得自定义令牌后,在客户端使用signInWithCustomToken()登录Firebase Auth。

const { data: session, status } = useSession();

const signInWithFirebase = async (user) => {
  const response = await fetch("/api/createCustomToken", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ user }),
  });

  const customToken = await response.json();

  signInWithCustomToken(auth, customToken.token)
    .then((userCredential) => {
      const user = userCredential.user;
      console.log(user);
    })
    .catch((error) => {
      console.log(error);
    });
};

useEffect(() => {
  if (!session) return;

  signInWithFirebase(session.user);
}, [session]);


一旦用户会话注册到Firebase,您的数据库安全规则就应该按预期工作。
您可以参考此post以进行进一步的阅读。

相关问题