import { betterAuth } from 'better-auth'; import { bearer } from 'better-auth/plugins/bearer'; import { Pool } from 'pg'; import { Kysely, PostgresDialect } from 'kysely'; import type { FastifyBaseLogger } from 'fastify'; export interface BetterAuthConfig { databaseUrl: string; pool?: Pool; secret: string; baseUrl: string; trustedOrigins: string[]; logger: FastifyBaseLogger; } /** * Create Better Auth instance with PostgreSQL adapter and passkey support */ export async function createBetterAuth(config: BetterAuthConfig) { try { config.logger.debug({ databaseUrl: config.databaseUrl.replace(/:[^:@]+@/, ':***@'), baseUrl: config.baseUrl, }, 'Creating Better Auth instance'); // Use existing pool if provided, otherwise create new one const pool = config.pool || new Pool({ connectionString: config.databaseUrl, }); config.logger.debug('PostgreSQL pool created'); // Test database connection first try { config.logger.debug('Testing database connection...'); const testClient = await pool.connect(); await testClient.query('SELECT 1'); testClient.release(); config.logger.debug('Database connection test successful'); } catch (dbError: any) { config.logger.error({ error: dbError, message: dbError.message, stack: dbError.stack, }, 'Database connection test failed'); throw new Error(`Database connection failed: ${dbError.message}`); } // Create Kysely instance for Better Auth config.logger.debug('Creating Kysely database instance...'); const db = new Kysely({ dialect: new PostgresDialect({ pool }), }); config.logger.debug('Kysely instance created'); // Better Auth v1.5.3 postgres configuration const auth = betterAuth({ database: { db, type: 'postgres', }, // Secret for JWT signing secret: config.secret, // Base URL for callbacks and redirects baseURL: config.baseUrl, // Trusted origins for CORS trustedOrigins: config.trustedOrigins, // Email/password authentication emailAndPassword: { enabled: true, requireEmailVerification: false, // Set to true in production sendResetPassword: async ({ user, url }) => { // TODO: Implement email sending config.logger.info({ userId: user.id, resetUrl: url }, 'Password reset requested'); }, }, // Session configuration session: { expiresIn: 60 * 60 * 24 * 7, // 7 days updateAge: 60 * 60 * 24, // Update session every 24 hours cookieCache: { enabled: true, maxAge: 5 * 60, // 5 minutes }, }, // Plugins plugins: [ bearer(), // Enable Bearer token authentication for API/WebSocket ], }); config.logger.debug('Better Auth instance created'); return auth; } catch (error: any) { config.logger.error({ error, message: error.message, stack: error.stack, cause: error.cause, }, 'Error creating Better Auth instance'); throw error; } } export type BetterAuthInstance = Awaited>;