You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js(TypeScript)中Firebase Auth与中间件结合实现权限控制的问题

Next.js(TypeScript)中Firebase Auth与中间件结合实现权限控制的问题

Hey Sam,我完全懂你遇到的这个坑——Firebase Auth的currentUser在Next.js中间件里一直返回null,客户端的AuthContext又没法在服务器端的中间件里用,这确实是个常见的环境差异问题。让我一步步帮你解决:

为什么auth.currentUser在中间件里是null?

先搞清楚本质:currentUser是Firebase Auth客户端SDK维护的客户端状态,而Next.js中间件运行在服务器端,两者不在同一个环境,中间件根本没法直接访问客户端的Auth状态。我们得换个思路,把用户的认证信息从客户端传递到服务器端。

解决方案:用ID Token + HttpOnly Cookie做服务器端验证

核心逻辑是:客户端登录后,把Firebase的ID Token存入HttpOnly Cookie,中间件读取这个Cookie并验证Token有效性,以此判断用户是否有权访问受保护路由。

步骤1:客户端登录后将ID Token存入Cookie

在你的AuthContext或登录逻辑里,监听用户状态变化,获取ID Token后通过API路由存入HttpOnly Cookie(前端JS没法直接设置HttpOnly Cookie,必须通过服务器端)。

比如修改你的useAuth hook:

import { onAuthStateChanged, getAuth, getIdToken } from "firebase/auth";
import { useEffect } from "react";

export const useAuth = () => {
  const auth = getAuth();

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, async (user) => {
      if (user) {
        // 获取用户的ID Token
        const idToken = await getIdToken(user);
        // 调用API路由设置Cookie
        await fetch("/api/set-firebase-token", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ token: idToken }),
        });
      } else {
        // 用户登出时清除Cookie
        await fetch("/api/clear-firebase-token", { method: "POST" });
      }
    });

    return unsubscribe;
  }, [auth]);
};

然后创建两个API路由处理Cookie的设置和清除:

/app/api/set-firebase-token/route.ts

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export async function POST(request: NextRequest) {
  const { token } = await request.json();
  const response = NextResponse.json({ success: true });
  
  // 设置安全的HttpOnly Cookie,防止XSS攻击
  response.cookies.set("firebase-token", token, {
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    sameSite: "strict",
    maxAge: 60 * 60 * 24, // 有效期1天
    path: "/",
  });
  
  return response;
}

/app/api/clear-firebase-token/route.ts

import { NextResponse } from "next/server";

export async function POST() {
  const response = NextResponse.json({ success: true });
  response.cookies.delete("firebase-token");
  return response;
}

步骤2:在中间件里验证Token并控制访问

现在修改你的中间件,读取Cookie里的Token,用Firebase Admin SDK验证其有效性(比客户端SDK更安全,无需暴露客户端API密钥):

首先安装Firebase Admin:

npm install firebase-admin

然后修改middleware.ts:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import admin from 'firebase-admin';

// 确保Firebase Admin只初始化一次
if (!admin.apps.length) {
  admin.initializeApp({
    credential: admin.credential.cert(
      JSON.parse(process.env.FIREBASE_ADMIN_SERVICE_ACCOUNT || '')
    ),
  });
}

const protectedRoutes = ["/article/add"];

export async function middleware(request: NextRequest) {
  // 从Cookie中获取Token
  const token = request.cookies.get("firebase-token")?.value;
  let isAuthenticated = false;

  if (token) {
    try {
      // 验证ID Token的有效性
      const decodedToken = await admin.auth().verifyIdToken(token);
      // decodedToken包含用户UID、邮箱、自定义声明等,可用于更细粒度的权限控制
      console.log("当前认证用户UID:", decodedToken.uid);
      isAuthenticated = true;
    } catch (error) {
      // Token无效或过期,清除Cookie并跳转到登录页
      console.error("Token验证失败:", error);
      const response = NextResponse.redirect(new URL("/login", request.nextUrl.origin));
      response.cookies.delete("firebase-token");
      return response;
    }
  }

  // 检查是否访问受保护路由且未认证
  if (!isAuthenticated && protectedRoutes.includes(request.nextUrl.pathname)) {
    return NextResponse.redirect(new URL("/login", request.nextUrl.origin));
  }

  // 允许正常访问
  return NextResponse.next();
}

额外优化建议

  • 细粒度权限控制:如果需要基于用户角色(如管理员/普通用户)控制路由,可以在Firebase控制台给用户添加自定义声明(比如role: "admin"),然后在中间件里通过decodedToken.role判断权限。
  • Token自动刷新:Firebase ID Token有效期为1小时,你可以在客户端监听Token过期事件,自动刷新Token并更新Cookie,避免用户频繁登录。

备注:内容来源于stack exchange,提问作者Sam B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 02:44:57