import NextAuth, { type DefaultSession } from "next-auth" import Credentials from "next-auth/providers/credentials" import { ZodError } from "zod" import type { UserRole } from "@/generated/prisma/client" import { SIGN_IN_URL, TOKEN_EXPIRATION_SECONDS } from "@/lib/constants" import { verifyPassword } from "@/lib/security" import { signInSchema } from "@/schemas/auth.schema" import { getUserByUsername } from "@/services/user.service" declare module "next-auth" { interface Session { user: { id: string role: UserRole } & DefaultSession["user"] } interface User { role: UserRole } } export const { handlers, signIn, signOut, auth } = NextAuth({ session: { strategy: "jwt", maxAge: TOKEN_EXPIRATION_SECONDS, }, providers: [ Credentials({ credentials: { username: {}, password: {}, }, authorize: async (credentials) => { try { const { data, success } = signInSchema.safeParse(credentials) if (!success) throw new Error("Invalid username or password") const user = await getUserByUsername(data.username) if (!user) { throw new Error("Invalid username or password") } if (!user.isActive) { throw new Error("Invalid username or password") } if (!(await verifyPassword(data.password, user.password))) throw new Error("Invalid username or password") return { ...user, role: user.role, } } catch (error) { if (error instanceof ZodError) { return null } throw new Error("Invalid username or password") } }, }), ], pages: { signIn: SIGN_IN_URL, }, callbacks: { async session({ session, token }) { if (!token) return session session.user.id = token.sub as string session.user.name = token.name as string session.user.email = token.email as string session.user.role = token.role as UserRole return session }, async jwt({ token, user }) { if (user) { token.sub = user.id token.name = user.name token.email = user.email token.role = user.role } return token }, authorized: async ({ auth }) => { return !!auth }, }, })