Authentication patterns in Next.js
Authentication in Next.js
Next.js offers multiple patterns for authentication, leveraging Server Components, Middleware, and Route Handlers. The key is deciding where to check authentication and how to protect routes.
Middleware Authentication
Check auth on every request before it reaches your pages:
tsx
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const publicPaths = ["/", "/login", "/register", "/api/auth"];
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Allow public paths
if (publicPaths.some(path => pathname.startsWith(path))) {
return NextResponse.next();
}
// Check for auth token
const token = request.cookies.get("auth-token")?.value;
if (!token) {
const loginUrl = new URL("/login", request.url);
loginUrl.searchParams.set("callbackUrl", pathname);
return NextResponse.redirect(loginUrl);
}
return NextResponse.next();
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};Server Component Authentication
tsx
// lib/auth.ts
import { cookies } from "next/headers";
export async function getSession() {
const cookieStore = await cookies();
const token = cookieStore.get("auth-token")?.value;
if (!token) return null;
try {
const user = await verifyToken(token);
return user;
} catch {
return null;
}
}tsx
// app/dashboard/page.tsx (Server Component)
import { redirect } from "next/navigation";
import { getSession } from "@/lib/auth";
export default async function DashboardPage() {
const session = await getSession();
if (!session) {
redirect("/login");
}
return (
<div>
<h1>Welcome, {session.user.name}</h1>
<DashboardContent />
</div>
);
}Route Handler Authentication
tsx
// app/api/profile/route.ts
import { getSession } from "@/lib/auth";
export async function GET() {
const session = await getSession();
if (!session) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
return Response.json({ user: session.user });
}Server Action Authentication
tsx
"use server";
import { getSession } from "@/lib/auth";
export async function updateProfile(formData: FormData) {
const session = await getSession();
if (!session) {
throw new Error("Unauthorized");
}
const name = formData.get("name") as string;
await db.update(users)
.set({ name })
.where(eq(users.id, session.user.id));
revalidatePath("/profile");
}Auth with NextAuth.js (Auth.js)
tsx
// auth.ts
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
import Credentials from "next-auth/providers/credentials";
export const { auth, handlers, signIn, signOut } = NextAuth({
providers: [
GitHub,
Credentials({
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
authorize: async (credentials) => {
const user = await getUserByEmail(credentials.email);
if (!user || !await verifyPassword(credentials.password, user.password)) {
return null;
}
return user;
},
}),
],
});
// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";
export const { GET, POST } = handlers;Where to Check Auth
| Layer | When to use | Pros |
|---|---|---|
| Middleware | Protect entire sections | Runs before rendering, fast |
| Server Component | Page-level auth | Can fetch user data |
| Route Handler | API protection | Standard HTTP auth |
| Server Action | Mutation protection | Validates before DB writes |
| Layout | Section-wide auth | Wraps all child pages |
Best Practices
- Always verify on server — never trust client-side auth alone
- Use Middleware for broad route protection
- Use Server Components for page-level auth with data fetching
- Always validate in Server Actions — they're callable endpoints
- HttpOnly cookies for tokens — not localStorage
- Combine layers — Middleware + Server Component for defense in depth
Important:
Next.js authentication should be checked at multiple layers: Middleware for route protection, Server Components for page-level access, and Server Actions for mutation security. Use HttpOnly cookies for token storage and always validate on the server. Libraries like NextAuth.js (Auth.js) simplify OAuth and credential-based authentication.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.