Files
ai/gateway/src/auth/better-auth-config.ts
2026-03-24 21:37:49 -04:00

113 lines
3.2 KiB
TypeScript

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<ReturnType<typeof createBetterAuth>>;