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

Next.js 14服务端组件中获取Firebase Auth ID Token的最佳方案

在Next.js 14(App Router)服务端组件中获取Firebase Auth ID Token的最佳方案

以下是几种适配不同场景的可行方案,按生产环境推荐优先级排序:

方案一:使用Firebase Auth会话Cookie(推荐)

Firebase Auth支持将短期ID Token转换为长期会话Cookie,具备HttpOnly、Secure等安全属性,适合服务端读取验证,是生产环境的最优选择。

步骤1:客户端触发会话Cookie生成

在客户端组件中,用户登录后获取ID Token,调用Next.js API路由生成会话Cookie:

// 客户端组件(如LoginClient.jsx)
import { auth } from '@/lib/firebase';
import { signInWithEmailAndPassword } from 'firebase/auth';

const handleLogin = async (email, password) => {
  const userCredential = await signInWithEmailAndPassword(auth, email, password);
  const idToken = await userCredential.user.getIdToken();

  // 调用API路由生成会话Cookie
  await fetch('/api/set-session', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ idToken }),
  });
};

步骤2:API路由生成并设置会话Cookie

在app/api/set-session/route.js中,用Firebase Admin SDK验证ID Token并生成会话Cookie:

import { adminAuth } from '@/lib/firebase-admin';
import { cookies } from 'next/headers';

export async function POST(request) {
  const { idToken } = await request.json();
  const expiresIn = 60 * 60 * 24 * 5 * 1000; // 5天有效期

  // 验证ID Token并生成会话Cookie
  const sessionCookie = await adminAuth.createSessionCookie(idToken, { expiresIn });

  // 设置安全Cookie属性
  cookies().set('firebase-session', sessionCookie, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'strict',
    maxAge: expiresIn / 1000,
    path: '/',
  });

  return new Response(JSON.stringify({ status: 'success' }), { status: 200 });
}

步骤3:服务端组件读取并验证会话Cookie

在服务端组件中读取Cookie,验证后获取用户UID:

// 服务端组件(如DashboardServer.jsx)
import { adminAuth } from '@/lib/firebase-admin';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';

export default async function Dashboard() {
  const sessionCookie = cookies().get('firebase-session')?.value;

  if (!sessionCookie) return redirect('/login');

  try {
    // 验证会话Cookie并解析用户信息
    const decodedClaims = await adminAuth.verifySessionCookie(sessionCookie, true);
    const uid = decodedClaims.uid;

    // 基于UID获取用户数据或执行其他逻辑
    return <div>当前用户UID:{uid}</div>;
  } catch (error) {
    // Cookie无效,清除后跳转登录页
    cookies().delete('firebase-session');
    return redirect('/login');
  }
}

方案二:客户端主动存储ID Token到Cookie(适合简单场景)

若不需要长期登录状态,可直接将ID Token存入Cookie(注意:ID Token有效期仅1小时,需定时刷新)。

步骤1:客户端监听Auth状态,存储/刷新Token

// 客户端组件(如AuthProvider.jsx)
import { auth } from '@/lib/firebase';
import { onAuthStateChanged } from 'firebase/auth';
import { cookies } from 'next/headers';
import { useEffect } from 'react';

export function AuthProvider({ children }) {
  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, async (user) => {
      if (user) {
        // 初始获取Token并存入Cookie
        let idToken = await user.getIdToken();
        cookies().set('firebase-token', idToken, {
          secure: process.env.NODE_ENV === 'production',
          sameSite: 'lax',
          maxAge: 3600,
          path: '/',
        });

        // 每55分钟强制刷新Token(避免过期)
        const refreshInterval = setInterval(async () => {
          idToken = await user.getIdToken(true);
          cookies().set('firebase-token', idToken, {
            secure: process.env.NODE_ENV === 'production',
            sameSite: 'lax',
            maxAge: 3600,
            path: '/',
          });
        }, 55 * 60 * 1000);

        return () => clearInterval(refreshInterval);
      } else {
        // 用户登出时清除Cookie
        cookies().delete('firebase-token');
      }
    });

    return () => unsubscribe();
  }, []);

  return children;
}

步骤2:服务端组件读取并验证Token

// 服务端组件
import { adminAuth } from '@/lib/firebase-admin';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';

export default async function Profile() {
  const idToken = cookies().get('firebase-token')?.value;

  if (!idToken) return redirect('/login');

  try {
    const decodedToken = await adminAuth.verifyIdToken(idToken);
    const uid = decodedToken.uid;

    return <div>用户UID:{uid}</div>;
  } catch (error) {
    cookies().delete('firebase-token');
    return redirect('/login');
  }
}

方案三:Next.js Middleware统一处理验证

通过Middleware提前验证会话Cookie/Token,将UID传递给服务端组件,避免重复验证逻辑。

步骤1:Middleware中验证并传递UID

// middleware.js
import { adminAuth } from '@/lib/firebase-admin';
import { NextResponse } from 'next/server';

export async function middleware(request) {
  const sessionCookie = request.cookies.get('firebase-session')?.value;

  if (!sessionCookie) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  try {
    const decodedClaims = await adminAuth.verifySessionCookie(sessionCookie, true);
    // 将UID添加到请求头,供服务端组件读取
    const headers = new Headers(request.headers);
    headers.set('x-firebase-uid', decodedClaims.uid);

    return NextResponse.next({ headers });
  } catch (error) {
    const response = NextResponse.redirect(new URL('/login', request.url));
    response.cookies.delete('firebase-session');
    return response;
  }
}

// 指定需要验证的路由
export const config = {
  matcher: ['/dashboard/:path*', '/profile'],
};

步骤2:服务端组件读取请求头中的UID

// 服务端组件
import { headers } from 'next/headers';

export default async function Dashboard() {
  const uid = headers().get('x-firebase-uid');

  return <div>当前用户UID:{uid}</div>;
}

方案对比

  • 方案一:安全级别最高,支持长期登录状态,适合生产环境。
  • 方案二:实现简单,但需处理Token刷新逻辑,适合小型项目或测试场景。
  • 方案三:统一验证逻辑,减少组件重复代码,适合路由较多的项目。

内容的提问来源于stack exchange,提问作者Rajendra A Verma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 22:10:33