NextAuth + Prisma Integration Security

Share

To secure NextAuth + Prisma integration, you need to: (1) use the PrismaAdapter for database session storage, (2) include user.id in the session callback for database queries, (3) check auth() in all API routes and Server Components, (4) set a strong NEXTAUTH_SECRET (32+ characters), and (5) properly configure OAuth callback URLs. This blueprint ensures sessions are securely stored and user identity is verified server-side.

Setup Time2-3 hours

TL;DR

NextAuth with Prisma adapter stores sessions in your database. Use the session callback to include user ID in the session, protect API routes with getServerSession, and configure NEXTAUTH_SECRET properly. Database sessions are more secure than JWTs for sensitive apps.

Auth Configuration NextAuth Prisma

lib/auth.ts
import NextAuth from 'next-auth'
import { PrismaAdapter } from '@auth/prisma-adapter'
import { prisma } from '@/lib/prisma'
import GitHub from 'next-auth/providers/github'

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    GitHub({
      clientId: process.env.GITHUB_ID!,
      clientSecret: process.env.GITHUB_SECRET!,
    }),
  ],
  callbacks: {
    session: ({ session, user }) => ({
      ...session,
      user: {
        ...session.user,
        id: user.id,  // Include user ID in session
      },
    }),
  },
})

Protected API Route

app/api/posts/route.ts
import { auth } from '@/lib/auth'
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'

export async function POST(req: Request) {
  const session = await auth()

  if (!session?.user?.id) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  const body = await req.json()

  const post = await prisma.post.create({
    data: {
      title: body.title,
      authorId: session.user.id,  // Verified user ID from session
    },
  })

  return NextResponse.json(post)
}

export async function GET() {
  const session = await auth()

  if (!session?.user?.id) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  const posts = await prisma.post.findMany({
    where: { authorId: session.user.id },
  })

  return NextResponse.json(posts)
}

Protected Server Component

app/dashboard/page.tsx
import { auth } from '@/lib/auth'
import { redirect } from 'next/navigation'
import { prisma } from '@/lib/prisma'

export default async function Dashboard() {
  const session = await auth()

  if (!session?.user?.id) {
    redirect('/api/auth/signin')
  }

  const posts = await prisma.post.findMany({
    where: { authorId: session.user.id },
  })

  return (
    <div>
      <h1>Welcome, {session.user.name}</h1>
      {posts.map(post => <div key={post.id}>{post.title}</div>)}
    </div>
  )
}

Always include user ID in session callback. By default, NextAuth sessions don't include the database user ID. Add it in the callback to use for database queries.

Security Checklist

Pre-Launch Checklist

NEXTAUTH_SECRET set (32+ chars)

Session callback includes user ID

API routes check auth()

OAuth callback URLs configured

Database connection secured

Clerk + Next.js Managed Auth Auth0 + Next.js Alternative Redis Session Management

Check Your NextAuth Integration

Scan for authentication security issues.

Start Free Scan
Security Blueprints

NextAuth + Prisma Integration Security