import { betterAuth } from "better-auth" import { drizzleAdapter } from "better-auth/adapters/drizzle" import { nextCookies } from "better-auth/next-js" import { admin } from "better-auth/plugins" import { db } from "@/lib/db" import { user, session, account, verification, profiles } from "@/lib/db/schema" import { sendEmail } from "@/lib/email/send" // Bootstrap superadmins from env — no API path lets a user self-promote. const ADMIN_USER_IDS = (process.env.ADMIN_USER_IDS ?? "") .split(",") .map((s) => s.trim()) .filter(Boolean) export const auth = betterAuth({ baseURL: process.env.BETTER_AUTH_URL, secret: process.env.BETTER_AUTH_SECRET, database: drizzleAdapter(db, { provider: "pg", schema: { user, session, account, verification }, }), emailAndPassword: { enabled: true, // Login works immediately; flip to true once verification email is desired. // Recommended for production: set requireEmailVerification to true. requireEmailVerification: false, minPasswordLength: 8, sendResetPassword: async ({ user: u, url }) => { await sendEmail({ to: u.email, subject: "Reset your Property Management Network password", html: resetPasswordHtml(url), }) }, }, socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID ?? "", clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "", }, }, // Throttle auth endpoints (per IP) to slow brute-force / credential stuffing. rateLimit: { enabled: true, window: 60, // seconds max: 20, // requests per window per IP for auth endpoints }, // Auto-create the app `profiles` row whenever Better Auth creates a user // (replaces the old `handle_new_user` Postgres trigger). databaseHooks: { user: { create: { after: async (u) => { try { await db .insert(profiles) .values({ id: u.id, email: u.email, full_name: u.name ?? null }) .onConflictDoNothing() } catch { // Never block sign-up on profile creation. } }, }, }, }, // `admin` enables role/ban/impersonation; `nextCookies` MUST stay last. plugins: [ admin({ adminUserIds: ADMIN_USER_IDS }), nextCookies(), ], }) function resetPasswordHtml(url: string) { return `
Click the button below to choose a new password. If you didn't request this, you can ignore this email.
Reset PasswordProperty Management Network