Learn how to implement secure and scalable authentication in a Next.js application using Prisma ORM.

Authentication is the backbone of any modern web application. Whether you’re building a SaaS platform, a multi-tenant system, or a simple dashboard, securing user access is non-negotiable.
In this guide, we’ll build a robust authentication system using Next.js and Prisma, covering:
npx create-next-app@latest auth-app
cd auth-app
npm install prisma @prisma/client bcrypt jsonwebtoken
npx prisma init
Edit prisma/schema.prisma:
model User {
id String @id @default(uuid())
email String @unique
password String
createdAt DateTime @default(now())
}
Run migration:
npx prisma migrate dev --name init
Create: app/api/auth/signup/route.js
import { PrismaClient } from "@prisma/client";
import bcrypt from "bcrypt";
const prisma = new PrismaClient();
export async function POST(req) {
const { email, password } = await req.json();
const hashedPassword = await bcrypt.hash(password, 10);
const user = await prisma.user.create({
data: { email, password: hashedPassword },
});
return Response.json({ user });
}
import jwt from "jsonwebtoken";
import bcrypt from "bcrypt";
export async function POST(req) {
const { email, password } = await req.json();
const user = await prisma.user.findUnique({ where: { email } });
if (!user) {
return Response.json({ error: "User not found" },
{ status: 404 });
}
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return Response.json({ error: "Invalid password" },
{ status: 401 });
}
const token = jwt.sign(
{ userId: user.id },
process.env.JWT_SECRET,
{ expiresIn: "1d" }
);
return Response.json({ token });
}
Create middleware.js:
import jwt from "jsonwebtoken";
import { NextResponse } from "next/server";
export function middleware(req) {
const token = req.headers.get("authorization")?.split(" ")[1];
if (!token) {
return NextResponse.json({ error: "Unauthorized" },
{ status: 401 });
}
try {
jwt.verify(token, process.env.JWT_SECRET);
return NextResponse.next();
} catch (err) {
return NextResponse.json({ error: "Invalid token" },
{ status: 401 });
}
}
// Example protected API
export async function GET(req) {
return Response.json({ message: "Protected data" });
}
Instead of sending JWT in headers, you can:
❌ Storing plain passwords ❌ Not using environment variables ❌ Missing token expiration ❌ No input validation
Since you’re working on microservices + multi-tenant systems, consider:
Using Next.js with Prisma gives you:
This setup is perfect for real-world SaaS applications and can scale with your system.
