Guides/Web Development
Web Development10 min read

How to Add Authentication to a Next.js App Router App

Add secure authentication to your Next.js 14 App Router application using NextAuth.js v5 — configure providers, protect routes with middleware, and handle sessions server and client side.

Install and Configure NextAuth.js v5

Install: npm install next-auth@beta. Create auth.ts at the project root: import NextAuth from "next-auth"; import GitHub from "next-auth/providers/github"; export const { handlers, signIn, signOut, auth } = NextAuth({ providers: [GitHub({ clientId: process.env.AUTH_GITHUB_ID, clientSecret: process.env.AUTH_GITHUB_SECRET })] }). Create the route handler at src/app/api/auth/[...nextauth]/route.ts: export { handlers as GET, handlers as POST } from "@/auth".

Protect Routes with Middleware

Create middleware.ts at the project root: export { auth as middleware } from "@/auth". Add a matcher to the config: export const config = { matcher: ["/dashboard/:path*", "/admin/:path*"] }. This runs the auth middleware only on matched routes. Unauthenticated users are automatically redirected to the sign-in page. The middleware runs on the edge — zero cold start, no database call needed for basic route protection.

Access the Session in Server Components

In any Server Component: import { auth } from "@/auth"; export default async function Page() { const session = await auth(); if (!session) redirect("/login"); return <div>Welcome {session.user?.name}</div>; }. The auth() function in Server Components reads the session from the cookie server-side — no round trip to the client, no flicker. This is the recommended pattern for Next.js App Router.

Access the Session in Client Components

Wrap your layout with SessionProvider: import { SessionProvider } from "next-auth/react"; — in your root layout. Then in client components: import { useSession } from "next-auth/react"; const { data: session, status } = useSession();. Check status === "loading" before rendering authenticated content to prevent layout shift. Use status === "authenticated" to conditionally render user-specific UI.

Add Credentials Provider for Email/Password

Add to your providers array: Credentials({ credentials: { email: {}, password: {} }, async authorize(credentials) { const user = await db.user.findUnique({ where: { email: credentials.email } }); if (!user || !await bcrypt.compare(credentials.password, user.passwordHash)) return null; return user; } }). Always hash passwords with bcrypt — never store plaintext. Use zod to validate credentials before the database query.

Need Help?

Want this done for you?

Our engineering team handles implementations like this every week. Get a free scoping call — we will tell you exactly what it takes and what it costs.

Book a free call

© 2026 NexWorldTech — Built for Global Dominance.