import NextAuth from "next-auth"; import Credentials from "next-auth/providers/credentials"; import bcrypt from "bcryptjs"; import { prisma } from "@/lib/db"; import { LoginSchema } from "@/lib/validation/auth"; // Credentials-only, JWT sessions, no database adapter: with a single // Credentials provider and no OAuth, there's nothing for a DB-backed // Session/Account table to do, so we skip the Prisma adapter entirely. export const { handlers, auth, signIn, signOut } = NextAuth({ session: { strategy: "jwt" }, // Self-hosted behind whatever host/port the operator points at it (not a // platform Auth.js auto-detects, like Vercel) -- without this, Auth.js // rejects every request with an UntrustedHost error. trustHost: true, pages: { signIn: "/login", }, providers: [ Credentials({ credentials: { email: {}, password: {}, }, async authorize(rawCredentials) { const parsed = LoginSchema.safeParse(rawCredentials); if (!parsed.success) return null; const { email, password } = parsed.data; const user = await prisma.user.findUnique({ where: { email } }); if (!user) return null; const passwordsMatch = await bcrypt.compare(password, user.passwordHash); if (!passwordsMatch) return null; return { id: user.id, email: user.email, name: user.name, role: user.role }; }, }), ], callbacks: { async jwt({ token, user }) { if (user?.id) { token.sub = user.id; token.role = user.role; } return token; }, async session({ session, token }) { if (session.user && token.sub) session.user.id = token.sub; // Cached in the JWT at login time -- a role change made through the // Admin page won't take effect for that user until their next login. // No session-invalidation mechanism yet; fine for now, revisit if // role changes need to apply immediately. if (session.user && token.role) session.user.role = token.role; return session; }, }, });