import NextAuth from "next-auth"; import MicrosoftEntraID from "next-auth/providers/microsoft-entra-id"; import { PrismaAdapter } from "@auth/prisma-adapter"; import { prisma } from "@/lib/prisma"; import type { Role } from "@/generated/prisma/client"; export const { handlers, auth, signIn, signOut } = NextAuth({ adapter: PrismaAdapter(prisma), providers: [ MicrosoftEntraID({ clientId: process.env.AUTH_MICROSOFT_ENTRA_ID_ID, clientSecret: process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET, issuer: `https://login.microsoftonline.com/${process.env.AUTH_MICROSOFT_ENTRA_ID_TENANT_ID}/v2.0`, // Safe for Entra ID — Microsoft verifies organizational emails. // Required to link SSO accounts to pre-seeded User records by email match. allowDangerousEmailAccountLinking: true, }), ], session: { strategy: "database", }, events: { async signIn({ user }) { if (!user.id || !user.email) return; // Auto-assign organization by email domain match const dbUser = await prisma.user.findUnique({ where: { id: user.id }, select: { organizationId: true, email: true }, }); if (dbUser?.organizationId || !dbUser?.email) return; const domain = dbUser.email.split("@")[1]; if (!domain) return; const org = await prisma.organization.findFirst({ where: { domain }, }); if (org) { await prisma.user.update({ where: { id: user.id }, data: { organizationId: org.id }, }); } }, }, callbacks: { async session({ session, user }) { // Fetch user with role and org from database const dbUser = await prisma.user.findUnique({ where: { id: user.id }, select: { role: true, organizationId: true }, }); if (dbUser) { session.user.id = user.id; session.user.role = dbUser.role; session.user.organizationId = dbUser.organizationId; } return session; }, }, pages: { signIn: "/login", }, });