NetcaneTechnologies

Next.js

Auth Patterns in Next.js: Sessions, JWT, and Protected Routes

How to implement login, sessions or JWT, and protect routes in the App Router without overcomplicating it.

Yasir Haleem3 min read

Auth in Next.js means: where you store the credential (session cookie vs JWT), how you check it on the server, and how you protect routes. Here’s a pattern that works with the App Router.

Sessions vs JWT

Session-based auth: the server stores session data (e.g. in a DB or Redis); the client holds an opaque cookie. Each request you look up the session and get the user. JWT: the client holds a signed token; the server verifies the signature and reads the payload. No server-side session store, but revocation is harder. For most apps, sessions are simpler to reason about (logout = delete session; revoke = invalidate one place). Use JWT when you need stateless auth across services or when your auth provider only issues tokens.

Storing and sending the credential

For sessions, set an HTTP-only, secure cookie with the session id (or a signed session payload). For JWT, you can put it in an HTTP-only cookie (recommended so JS can’t read it) or send it in a header from the client; cookie is simpler and avoids XSS stealing the token. In Next.js, set cookies in Server Actions or route handlers after login; read them in middleware or server components to decide redirects and to load the user.

// After login (e.g. in a Server Action)
import { cookies } from "next/headers";
 
export async function login(formData: FormData) {
  const res = await yourAuthAPI(formData);
  if (!res.ok) return { error: "Invalid credentials" };
  const { sessionToken } = await res.json();
  (await cookies()).set("session", sessionToken, {
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    sameSite: "lax",
    path: "/",
    maxAge: 60 * 60 * 24 * 7,
  });
  redirect("/dashboard");
}

Protecting routes

In middleware: read the session or JWT cookie; if missing or invalid, redirect to login for protected paths. In server components and Server Actions: look up the session or verify the JWT and get the user; if missing, redirect or return unauthorized. Don’t rely only on client-side checks; always enforce on the server so API and SSR can’t be bypassed.

// middleware.ts
export function middleware(request: NextRequest) {
  const session = request.cookies.get("session")?.value;
  if (!session && request.nextUrl.pathname.startsWith("/dashboard")) {
    return NextResponse.redirect(new URL("/login", request.url));
  }
  return NextResponse.next();
}

Use a small helper (e.g. getSession()) that reads the cookie and returns the user (or null) so layouts and pages can call it and show the right UI or redirect.

Summary

Choose sessions for simpler revocation and JWTs when you need stateless auth. Store the credential in an HTTP-only cookie; set it at login and read it in middleware and server code. Protect routes in middleware (redirect) and in server components/actions (enforce). That gives you a clear auth pattern for the App Router.

About the author

Yasir Haleem is founder and lead engineer at Netcane Technologies. He builds production Next.js sites with headless CMS platforms — Strapi, Contentful, Sanity, and WordPress — with a focus on performance, SEO, and maintainable architecture.

Let's work together

Tell us about your project. We respond within one business day with a clear scope, timeline, and estimate.