import { db } from "@/lib/db/index"; import { DrizzleAdapter } from "@auth/drizzle-adapter"; import { DefaultSession, getServerSession, NextAuthOptions } from "next-auth"; import { Adapter } from "next-auth/adapters"; import { redirect } from "next/navigation"; import { z } from "zod"; import AzureADProvider from "next-auth/providers/azure-ad"; export type AuthSession = { session: { user: { id: string; name?: string; email?: string; tenantId?: string; }; } | null; }; const envSchema = z.object({ AZURE_AD_CLIENT_ID: z.string().min(1), AZURE_AD_CLIENT_SECRET: z.string().min(1), }); export const env = envSchema.parse(process.env); export const authOptions: NextAuthOptions = { adapter: DrizzleAdapter(db) as Adapter, session: { strategy: "jwt", // CRITICAL: Use JWT strategy to access token in session callback }, callbacks: { jwt: async ({ token, account, profile }) => { // Store access token if (account) { token.accessToken = account.access_token; } // Extract tenantId from Azure AD tid claim if (profile && 'tid' in profile) { token.tenantId = profile.tid as string; } return token; }, session: ({ session, token }) => { // Copy user id from token if (token?.sub) { session.user.id = token.sub; } // Copy access token if (token?.accessToken) { session.accessToken = token.accessToken as string; } // Copy tenantId from token to session if (token?.tenantId) { session.user.tenantId = token.tenantId as string; } return session; }, }, providers: [ AzureADProvider({ clientId: env.AZURE_AD_CLIENT_ID, clientSecret: env.AZURE_AD_CLIENT_SECRET, tenantId: "common", // Multi-Tenancy Support authorization: { params: { scope: "openid profile email offline_access User.Read", }, }, }), ], }; export const getUserAuth = async () => { const session = await getServerSession(authOptions); return { session } as AuthSession; }; export const checkAuth = async () => { const { session } = await getUserAuth(); if (!session) redirect("/api/auth/signin"); };