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.
In this guide
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".
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.
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.
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 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?
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 callCompetitive Intelligence
Efficiency Modeling
© 2026 NexWorldTech — Built for Global Dominance.