Skip to main content
Practice Problems

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

LayerWhen to usePros
MiddlewareProtect entire sectionsRuns before rendering, fast
Server ComponentPage-level authCan fetch user data
Route HandlerAPI protectionStandard HTTP auth
Server ActionMutation protectionValidates before DB writes
LayoutSection-wide authWraps all child pages

Best Practices

  1. Always verify on server — never trust client-side auth alone
  2. Use Middleware for broad route protection
  3. Use Server Components for page-level auth with data fetching
  4. Always validate in Server Actions — they're callable endpoints
  5. HttpOnly cookies for tokens — not localStorage
  6. 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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems