Implementing Authentication in Next.js 14 with NextAuth.js v5 and Prisma
Complete guide to implementing secure authentication in Next.js 14 App Router using NextAuth.js v5 (Auth.js) with Prisma adapter and multiple OAuth providers.
Next.js 14 with the App Router requires a different approach to authentication. NextAuth.js v5 (now called Auth.js) has been redesigned for this architecture. Here's how to implement it properly.
Project Setup
npx create-next-app@latest my-app --typescript --tailwind --app
cd my-app
npm install next-auth@beta @prisma/client @auth/prisma-adapter
npm install -D prismaPrisma Schema Configuration
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}Auth.js Configuration (auth.ts)
import NextAuth from "next-auth"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { prisma } from "@/lib/prisma"
import Google from "next-auth/providers/google"
import GitHub from "next-auth/providers/github"
export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
GitHub({
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
}),
],
callbacks: {
session: ({ session, user }) => ({
...session,
user: { ...session.user, id: user.id },
}),
},
})API Route Handler (app/api/auth/[...nextauth]/route.ts)
import { handlers } from "@/auth"
export const { GET, POST } = handlersMiddleware for Protected Routes
import { auth } from "@/auth"
export default auth((req) => {
if (!req.auth && req.nextUrl.pathname !== "/login") {
const newUrl = new URL("/login", req.nextUrl.origin)
return Response.redirect(newUrl)
}
})
export const config = {
matcher: ["/dashboard/:path*", "/settings/:path*"],
}Server Component Authentication
import { auth } from "@/auth"
export default async function DashboardPage() {
const session = await auth()
if (!session?.user) redirect("/login")
return <div>Welcome, {session.user.name}</div>
}Client Component Authentication
"use client"
import { useSession, signIn, signOut } from "next-auth/react"
export function AuthButton() {
const { data: session, status } = useSession()
if (status === "loading") return <div>Loading...</div>
if (session) return <button onClick={() => signOut()}>Sign Out</button>
return <button onClick={() => signIn()}>Sign In</button>
}Security Best Practices
Always use HTTPS, implement CSRF protection (built into NextAuth), set secure cookie options, validate OAuth redirect URIs, and implement rate limiting on auth endpoints.
NyxaLabs Next.js Development
We build secure, scalable Next.js applications for startups and enterprises. Our expertise includes authentication systems, API development, and performance optimization. Get in touch to discuss your project.