Next.js authentication verifies user identity on the server before returning protected pages or mutating records. Keeping session tokens inside HTTP-only cookies stops browser JavaScript from reading sensitive credentials.
In the App Router, you enforce access checks at three layers: fast redirects in middleware, page protection in Server Components, and strict authorization inside every Server Action and Route Handler.
Read the current user session on the server and call redirect('/login') when the visitor is signed out. No unauthenticated data ever leaves the server.
// app/dashboard/page.tsx
import { redirect } from 'next/navigation';
import { getCurrentSession } from '@/lib/auth';
export default async function DashboardPage() {
const session = await getCurrentSession();
if (!session) {
redirect('/login');
}
return (
<section className="p-6">
<h2 className="text-2xl font-bold">Welcome, {session.user.name}</h2>
<p className="mt-2 text-gray-600">Account role: {session.user.role}</p>
</section>
);
}
Hiding a Delete button in the UI does not protect your backend. Re-check both authentication and role permissions inside every Server Action.
// app/actions/postActions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { getCurrentSession } from '@/lib/auth';
import { db } from '@/lib/db';
export async function deleteArticle(articleId: string) {
const session = await getCurrentSession();
if (!session || session.user.role !== 'admin') {
throw new Error('Forbidden: administrator access required.');
}
await db.article.delete({ where: { id: articleId } });
revalidatePath('/dashboard/articles');
}
HttpOnly, Secure, SameSite=Lax cookie so middleware can read them quickly without database calls.HttpOnly cookie and look up active sessions in PostgreSQL or Redis when instant logout revocation is required.localStorage: localStorage is readable by any XSS script on the page and cannot be read by Server Components.app/layout.tsx: Layouts do not re-execute on every child route change, so verify sessions inside pages or a dedicated Data Access Layer.Popular choices include Auth.js (NextAuth), Better Auth, Clerk, and Supabase Auth, or custom encrypted session cookies using jose.
redirect() not wrapped in a try/catch block?Next.js implements redirect() by throwing a special internal error, so catching it inside try/catch blocks the navigation.
getCurrentSession() use React cache()?Yes, wrapping your session lookup helper in React's cache() avoids duplicate database or cookie decoding calls during a single render pass.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With