import NextAuth from 'next-auth'; import { PrismaAdapter } from '@auth/prisma-adapter'; import { authConfig } from './auth.config'; import prisma from '@/lib/prisma'; import { buildAuthProviders } from '@/lib/auth-providers'; import { logAuditEvent } from '@/lib/audit-log'; export const { auth, signIn, signOut, handlers } = NextAuth({ ...authConfig, adapter: PrismaAdapter(prisma), providers: buildAuthProviders(), events: { async createUser({ user }) { const adminEmail = process.env.ADMIN_EMAIL?.toLowerCase(); if (!adminEmail || !user.id || user.email?.toLowerCase() !== adminEmail) { logAuditEvent({ userId: user.id, action: 'USER_CREATED', metadata: { email: user.email } }); return; } await prisma.user.update({ where: { id: user.id }, data: { role: 'ADMIN', emailVerified: new Date() }, }); logAuditEvent({ userId: user.id, action: 'USER_CREATED', metadata: { email: user.email, role: 'ADMIN' } }); }, async signOut(message) { const userId = 'token' in message && message.token?.sub ? message.token.sub : 'session' in message && message.session?.userId ? message.session.userId : null; if (!userId) return; logAuditEvent({ userId, action: 'LOGOUT' }); await prisma.$transaction([ prisma.user.update({ where: { id: userId }, data: { sessionVersion: { increment: 1 } }, }), prisma.session.deleteMany({ where: { userId } }), ]); }, }, callbacks: { ...authConfig.callbacks, async signIn({ user, account }) { if (account?.provider === 'google' && user.email) { const email = user.email.toLowerCase(); const adminEmail = process.env.ADMIN_EMAIL?.toLowerCase(); const existing = await prisma.user.findUnique({ where: { email } }); if (existing && adminEmail && email === adminEmail && existing.role !== 'ADMIN') { await prisma.user.update({ where: { id: existing.id }, data: { role: 'ADMIN', emailVerified: new Date() }, }); } } logAuditEvent({ userId: user.id, action: 'LOGIN', metadata: { provider: account?.provider ?? 'credentials', email: user.email }, }); return true; }, async jwt({ token, user, trigger, session }) { if (trigger === 'update' && session) { if ('aiSessionConsent' in session) token.aiSessionConsent = session.aiSessionConsent === true; if ('onboardingCompleted' in session) token.onboardingCompleted = session.onboardingCompleted === true; return token; } if (user?.id) { token.id = user.id; token.aiSessionConsent = false; const dbUser = await prisma.user.findUnique({ where: { id: user.id }, select: { role: true, sessionVersion: true, onboardingCompleted: true }, }); if (!dbUser) return null; token.role = dbUser.role; token.sessionVersion = dbUser.sessionVersion; token.onboardingCompleted = dbUser.onboardingCompleted; } else if (token.sub) { const dbUser = await prisma.user.findUnique({ where: { id: token.sub }, select: { role: true, sessionVersion: true, onboardingCompleted: true }, }); if (!dbUser) return null; if ( typeof token.sessionVersion === 'number' && token.sessionVersion !== dbUser.sessionVersion ) { return null; } token.id = token.sub; token.role = dbUser.role; token.sessionVersion = dbUser.sessionVersion; token.onboardingCompleted = dbUser.onboardingCompleted; } return token; }, }, });