52 lines
1.6 KiB
TypeScript
52 lines
1.6 KiB
TypeScript
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 };
|
|
},
|
|
}),
|
|
],
|
|
callbacks: {
|
|
async jwt({ token, user }) {
|
|
if (user?.id) token.sub = user.id;
|
|
return token;
|
|
},
|
|
async session({ session, token }) {
|
|
if (session.user && token.sub) session.user.id = token.sub;
|
|
return session;
|
|
},
|
|
},
|
|
});
|