import { compare, hash } from "bcryptjs" import NextAuth from "next-auth" import Credentials from "next-auth/providers/credentials" const normalizeRole = (value: unknown) => { const role = String(value ?? "").trim().toLowerCase() if (role === "admin" || role === "student" || role === "teacher" || role === "parent") return role return "student" } const normalizeBcryptHash = (value: string) => { if (value.startsWith("$2")) return value if (value.startsWith("$")) return `$2b${value}` return `$2b$${value}` } export const { handlers, auth, signIn, signOut } = NextAuth({ trustHost: true, secret: process.env.NEXTAUTH_SECRET, session: { strategy: "jwt" }, pages: { signIn: "/login" }, providers: [ Credentials({ credentials: { email: { label: "Email", type: "email" }, password: { label: "Password", type: "password" }, }, authorize: async (credentials) => { const email = String(credentials?.email ?? "").trim().toLowerCase() const password = String(credentials?.password ?? "") if (!email || !password) return null const [{ eq }, { db }, { users }] = await Promise.all([ import("drizzle-orm"), import("@/shared/db"), import("@/shared/db/schema"), ]) const user = await db.query.users.findFirst({ where: eq(users.email, email), }) if (!user) return null const storedPassword = user.password ?? null if (!storedPassword) return null const normalizedPassword = normalizeBcryptHash(storedPassword) if (!normalizedPassword.startsWith("$2")) return null const ok = await compare(password, normalizedPassword) if (!ok) return null return { id: user.id, name: user.name ?? undefined, email: user.email, role: normalizeRole(user.role), } }, }), ], callbacks: { jwt: async ({ token, user }) => { if (user) { token.id = (user as { id: string }).id token.role = normalizeRole((user as { role?: string }).role) token.name = (user as { name?: string }).name } const userId = String(token.id ?? "").trim() if (userId) { const [{ eq }, { db }, { users }] = await Promise.all([ import("drizzle-orm"), import("@/shared/db"), import("@/shared/db/schema"), ]) const fresh = await db.query.users.findFirst({ where: eq(users.id, userId), columns: { role: true, name: true }, }) if (fresh) { token.role = normalizeRole(fresh.role ?? token.role) token.name = fresh.name ?? token.name } } return token }, session: async ({ session, token }) => { if (session.user) { session.user.id = String(token.id ?? "") session.user.role = normalizeRole(token.role) if (typeof token.name === "string") { session.user.name = token.name } } return session }, }, })