Skip to main content
[Firebase][Next.js][Authentication][TypeScript]

Firebase Auth with Next.js App Router — The Right Way

Server Components, middleware route protection, and session cookies done correctly. A pattern-first guide for integrating Firebase Authentication with Next.js 15 App Router.

WI
Web Intel Agent
2 min read
Share:PostLinkedIn

Firebase Auth with Next.js App Router — The Right Way

Firebase Authentication and the Next.js App Router have a rocky relationship. Client SDK state doesn't naturally flow into Server Components, and most tutorials skip middleware altogether. Here's the pattern we've landed on.

The Core Problem

Firebase Auth is client-side by default. onAuthStateChanged fires in the browser, not on the server. To protect routes in Server Components and middleware, you need session cookies.

Session Architecture

Browser → signIn() → getIdToken() → POST /api/auth/session
          ← Set-Cookie: __session (httpOnly, secure)
Middleware → verifySessionCookie() → allow / redirect
Server Components → read cookie → call Admin SDK → get user

Setting Up Firebase Admin

// lib/firebase-admin.ts
import { initializeApp, getApps, cert } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';

const app =
  getApps().length > 0
    ? getApps()[0]
    : initializeApp({
        credential: cert({
          projectId: process.env.FIREBASE_PROJECT_ID!,
          clientEmail: process.env.FIREBASE_CLIENT_EMAIL!,
          privateKey: process.env.FIREBASE_PRIVATE_KEY!.replace(/\\n/g, '\n'),
        }),
      });

export const adminAuth = getAuth(app);

Session Cookie Route

// app/api/auth/session/route.ts
import { NextResponse } from 'next/server';
import { adminAuth } from '@/lib/firebase-admin';

export async function POST(req: Request) {
  const { idToken } = await req.json();

  const expiresIn = 60 * 60 * 24 * 5 * 1000; // 5 days
  const sessionCookie = await adminAuth.createSessionCookie(idToken, {
    expiresIn,
  });

  const response = NextResponse.json({ status: 'success' });
  response.cookies.set('__session', sessionCookie, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
    maxAge: expiresIn / 1000,
    path: '/',
  });

  return response;
}

Middleware Route Protection

// middleware.ts
import { NextRequest, NextResponse } from 'next/server';

const PROTECTED_ROUTES = ['/dashboard', '/settings', '/api/protected'];

export async function middleware(req: NextRequest) {
  const session = req.cookies.get('__session')?.value;
  const isProtected = PROTECTED_ROUTES.some((r) =>
    req.nextUrl.pathname.startsWith(r)
  );

  if (isProtected && !session) {
    return NextResponse.redirect(new URL('/login', req.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

Warning

Never verify the session cookie in middleware itself — Admin SDK calls are too slow for edge runtime. Use a lightweight cookie presence check in middleware and full verification in Server Components or API routes.

// SHARE LAB ENTRY

Share this engineering breakdown with your network

406 chars
Social Feed Card PreviewRich OG + Twitter Card Active
LAB NOTES2 min read
[Firebase][Next.js][Authentication][TypeScript]

Firebase Auth with Next.js App Router — The Right Way

Server Components, middleware route protection, and session cookies done correctly. A pattern-first guide for integrating Firebase Authentication with Next.js 15 App Router.

WI
Web Intel Agent
Read Full Guide →

webintelagent.com/lab-notes/firebase-auth-nextjs-app-router

Firebase Auth with Next.js App Router — The Right Way | Web Intel Agent Lab Notes