import NextAuth, { type DefaultSession } from 'next-auth' import { UserRole } from '@prisma/client' import { PrismaAdapter } from '@auth/prisma-adapter' import db from '@/lib/db' import authConfig from '@/auth.config' import { getUserById } from '@/data/user' import { AUTH_ERROR_URL, AUTH_LOGIN_URL } from '@/config/routes' import { getCurrentLocale } from '@/locales/server' import { type loc } from '@/config/locales' import { getTwoFactorConfirmationByUserId } from '@/data/two-factor-confirmation' export type ExtendedUser = DefaultSession['user'] & { role: UserRole, locale: loc, isTwoFactorEnabled?: boolean, image?: string } declare module 'next-auth' { interface Session { user: ExtendedUser } } export const VERIFICATION_TOKEN_EXPIRATION_DURATION = 3_600_000 export const { handlers: { GET, POST }, auth, signIn, signOut, } = NextAuth({ pages: { signIn: AUTH_LOGIN_URL, error: AUTH_ERROR_URL, }, events: { async linkAccount ({ user }) { await db.user.update({ where: { id: user.id as string }, data: { emailVerified: new Date() }, }) }, }, callbacks: { async signIn ({ user, account }) { // Bypass email verification when Oauth are being used if (account?.provider !== 'credentials') return true const existingUser = await getUserById(user.id as string) // Prevent sign in without email verification if (!existingUser?.emailVerified) return false if (existingUser.isTwoFactorEnabled) { const twoFactorConfirmation = await getTwoFactorConfirmationByUserId(existingUser.id) if (!twoFactorConfirmation) return false // Delete 2FA for the next sign in await db.twoFactorComfirmation.delete({ where: { id: twoFactorConfirmation.id }, }) } return true }, async session ({ token, session }) { if (token.sub && session.user) { session.user.id = token.sub } if (token.role && session.user) { session.user.role = token.role as UserRole } if (session.user) { session.user.isTwoFactorEnabled = token.isTwoFactorEnabled as boolean } session.user.locale = getCurrentLocale() return session }, async jwt ({ token }) { if (!token.sub) return token const existingUser = await getUserById(token.sub) if (!existingUser) return token token.role = existingUser.role token.isTwoFactorEnabled = existingUser.isTwoFactorEnabled return token }, }, adapter: PrismaAdapter(db), session: { strategy: 'jwt' }, ...authConfig, })