Wednesday, November 13, 2024
How to add Server Side Firebase Auth Checking to your Next.js App
Introduction
This guide will explain how to use Firebase Authentication to secure pages and API routes in a Next.js application by implementing Server-Side Authentication with Firebase, using middleware for access control.
Save cookies
Saving the authentication token in a cookie lets your Next.js app handle authentication on both client and server sides. This ensures pages and API routes are securely accessible only to authenticated users.
Add the cookie-saving logic in the AuthProvider's useEffect hook, where onIdTokenChanged updates the cookie whenever the token changes.
import { onAuthStateChanged, onIdTokenChanged, User } from "firebase/auth";
export const AuthProvider = ({ children, defaultUser }: AuthProviderProps) => {
//...
useEffect(() => {
return onIdTokenChanged(auth, async (user) => {
if (!user) {
setUser(null);
setCookie(undefined, "token", "", { path: "/" });
} else {
const token = await user.getIdToken();
setUser(user);
setCookie(undefined, "token", token, { path: "/" });
}
});
}, []);
//...
}
Server Side Firebase Auth Checking
Create serverApp.ts to your project.
import { getAuth } from "firebase/auth";
import { firebaseConfig } from "./config";
import { initializeServerApp } from "firebase/app";
import { cookies, headers } from "next/headers";
export async function getAuthenticatedAppForUser() {
const cookieStore = cookies();
const token = cookieStore.get("token")?.value;
if (!token) {
return null;
}
const firebaseServerApp = initializeServerApp(
firebaseConfig,
{ authIdToken: token }
);
const auth = getAuth(firebaseServerApp);
await auth.authStateReady();
return { firebaseServerApp, currentUser: auth.currentUser || null };
}
Protect API Routes with Middleware
Create middleware.ts to your root project or inside the src directory if you are using the src directory.
import { getAuthenticatedAppForUser } from '@/lib/firebase/serverApp';
import { NextResponse, type NextRequest } from 'next/server';
export async function middleware(request: NextRequest) {
const user = await getAuthenticatedAppForUser();
if (!user) {
return NextResponse.redirect(new URL('/auth/login', request.nextUrl).toString() + `?next=${encodeURIComponent(request.url)}`);
}
}
export const config = {
matcher: ['/account/:path*'], // Add routes that you want to protect
};