import { betterAuth } from 'better-auth'; import { getStringLiteralChecker } from './utils'; const betterAuthUrl = process.env.NEXT_PUBLIC_BETTER_AUTH_URL!; const permissions = ['GeneralSettings.read', 'GeneralSettings.write'] as const; export type Permission = (typeof permissions)[number]; export const isPermission = getStringLiteralChecker(permissions); const roleToPerm = new Map([ ['TestRole1', []], ['TestRole2', ['GeneralSettings.read']], ['User', ['GeneralSettings.read']], ['PowerUser', ['GeneralSettings.read', 'GeneralSettings.write']], ]); export const auth = betterAuth({ secret: process.env.BETTER_AUTH_SECRET!, baseURL: betterAuthUrl, socialProviders: { microsoft: { clientId: process.env.MICROSOFT_CLIENT_ID!, clientSecret: process.env.MICROSOFT_CLIENT_SECRET!, tenantId: process.env.MICROSOFT_TENANT_ID!, enabled: true, // Entra test users comes without email field populated in profile (this breaks better-auth) // https://www.better-auth.com/docs/errors/email_not_found // So we map the profile to ensure email is always set // This is likely needed only for test purposes // Problem: any extra attributes returned from this function are not available in custom session // https://github.com/better-auth/better-auth/discussions/3290 "Map additional fields by using mapProfileToUser in OAuth" mapProfileToUser: (profile) => { const userRoles = profile.roles || []; const userPerms = [ ...new Set(userRoles.flatMap((role) => roleToPerm.get(role) || [])), ]; return { id: profile.id || profile.sub, email: profile.preferred_username || profile.userPrincipalName || profile.mail || profile.email || profile.oid || `${profile.id}@placeholder.local`, name: profile.displayName || profile.name, image: profile.picture, emailVerified: true, roles: userRoles, permissions: userPerms, }; }, }, }, user: { additionalFields: { roles: { type: 'string[]', }, permissions: { type: 'string[]', }, }, }, session: { expiresIn: 60 * 60 * 24, // 24 hours updateAge: 60 * 60 * 1, // update every 1 hour cookieCache: { enabled: true, maxAge: 60 * 5, // 5 minutes }, cookieOptions: { sameSite: 'lax', // Important for OAuth redirects }, }, });