Next.js Middleware for Route Protection
Middleware runs at the edge before a request reaches your page. It is the ideal place to check authentication and redirect unauthenticated users before any component renders.
middleware.ts
// middleware.ts (at the root of your project, same level as app/)
import { auth } from '@/auth';
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export default auth((req) => {
const { nextUrl, auth: session } = req;
const isAuthenticated = !!session?.user;
const isAuthPage = nextUrl.pathname.startsWith('/login') ||
nextUrl.pathname.startsWith('/register');
const isProtectedRoute = nextUrl.pathname.startsWith('/dashboard') ||
nextUrl.pathname.startsWith('/settings') ||
nextUrl.pathname.startsWith('/profile');
const isAdminRoute = nextUrl.pathname.startsWith('/admin');
// Redirect authenticated users away from auth pages
if (isAuthenticated && isAuthPage) {
return NextResponse.redirect(new URL('/dashboard', nextUrl));
}
// Redirect unauthenticated users to login
if (!isAuthenticated && isProtectedRoute) {
const loginUrl = new URL('/login', nextUrl);
loginUrl.searchParams.set('callbackUrl', nextUrl.pathname);
return NextResponse.redirect(loginUrl);
}
// Role-based protection
if (isAdminRoute && session?.user?.role !== 'admin') {
return NextResponse.redirect(new URL('/unauthorized', nextUrl));
}
return NextResponse.next();
});
// Apply middleware only to these paths (not _next, api/auth, static files)
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|api/auth).*)',
],
};
Reading the Session in Server Components
// app/dashboard/page.tsx
import { auth } from '@/auth';
import { redirect } from 'next/navigation';
export default async function DashboardPage() {
const session = await auth();
if (!session) {
redirect('/login');
}
return (
<div>
<h1>Welcome, {session.user.name}</h1>
<p>Email: {session.user.email}</p>
</div>
);
}
Session in Client Components
// components/user-menu.tsx
'use client';
import { useSession } from 'next-auth/react';
export function UserMenu() {
const { data: session, status } = useSession();
if (status === 'loading') return <div className="animate-pulse h-8 w-8 rounded-full bg-gray-200" />;
if (!session) return null;
return (
<div className="flex items-center gap-3">
{session.user.image && (
<img src={session.user.image} className="h-8 w-8 rounded-full" alt="" />
)}
<span>{session.user.name}</span>
</div>
);
}
Middleware-based protection is the most reliable approach — it is enforced at the edge before any server code runs.