import NextAuth, { DefaultSession } from "next-auth" import { JWT } from "@auth/core/jwt" import { PrismaAdapter } from "@auth/prisma-adapter" import { db } from "@/lib/db" import authConfig from "@/auth.config" import { getUserById } from "./data/user" import { User, UserRole } from "@prisma/client" import { getImpersonationById } from "./data/impersonation" declare module "@auth/core/types" { /** * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context */ interface Session { user: { role: UserRole | null impersonation: User | null // By default, TypeScript merges new interface properties and overwrite existing ones. In this case, the default session user properties will be overwritten, with the new one defined above. To keep the default session user properties, you need to add them back into the newly declared interface } & DefaultSession["user"] // To keep the default types } } declare module "@auth/core/jwt" { /** Returned by the `jwt` callback and `auth`, when using JWT sessions */ interface JWT { role: UserRole | null impersonation: User | null } } export const { handlers: { GET, POST }, auth, signIn, signOut, } = NextAuth({ events: { async linkAccount({ user }) { await db.user.update({ where: {id: user.id }, data: { emailVerified: new Date() } }) } }, callbacks: { async session({ session, user, token }) { if (token.sub && session.user) { session.user.id = token.sub } if (token.role && session.user) { session.user.role = token.role } else { session.user.role = null } if (token.impersonation && session.user) { session.user.impersonation = token.impersonation } else { token.impersonation = null } return session }, async jwt({ token, user, account, profile }) { if (!token.sub) return token const existingUser = await getUserById(token.sub) if (!existingUser) return token // Update Role token.role = existingUser.role // Update Impersonation const impersonation = await getImpersonationById(existingUser.id) if (token.role == "ADMIN" && impersonation && impersonation.targetId != existingUser.id) { const impersonationUser = await getUserById(impersonation.targetId) if (impersonation) { token.impersonation = impersonationUser } else { token.impersonation = null } } else if (impersonation && impersonation.targetId == existingUser.id) { await db.impersonation.delete({ where: { sourceId: existingUser.id } }) token.impersonation = null } else { token.impersonation = null } return token } }, adapter: PrismaAdapter(db), session: { strategy: "jwt" }, ...authConfig, })