--- Build: pass | Tests: FAIL — Tests 9 failed | 2081 passed | 4 skipped (2094)
127 lines
3.1 KiB
TypeScript
127 lines
3.1 KiB
TypeScript
import { betterAuth } from 'better-auth';
|
|
import type { Pool } from 'pg';
|
|
|
|
import {
|
|
CONTROLPLANE_AUTH_MODE,
|
|
CONTROLPLANE_API_PORT,
|
|
CONTROLPLANE_HOST_IP,
|
|
BETTER_AUTH_SECRET,
|
|
BETTER_AUTH_URL,
|
|
CONTROLPLANE_EXPOSURE,
|
|
PLATFORM_INTERNAL_BASE,
|
|
PLATFORM_INTERNAL_DOMAIN,
|
|
PLATFORM_PUBLIC_BASE,
|
|
} from './config.js';
|
|
import { logger } from './logger.js';
|
|
|
|
export type AuthMode = 'local_trusted' | 'authenticated';
|
|
|
|
function getBetterAuthBaseOrigin(): string | null {
|
|
try {
|
|
return new URL(BETTER_AUTH_URL).origin;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function deriveReverseProxyOrigins(): string[] {
|
|
const origins: string[] = [];
|
|
try {
|
|
const url = new URL(BETTER_AUTH_URL);
|
|
if (
|
|
url.protocol === 'http:' &&
|
|
url.hostname !== 'localhost' &&
|
|
url.hostname !== '127.0.0.1'
|
|
) {
|
|
origins.push(`https://${url.hostname}`);
|
|
}
|
|
} catch {
|
|
// ignore
|
|
}
|
|
return origins;
|
|
}
|
|
|
|
function assertControlplaneAuthConfiguration(): void {
|
|
if (
|
|
CONTROLPLANE_EXPOSURE === 'public' &&
|
|
CONTROLPLANE_AUTH_MODE === 'local_trusted'
|
|
) {
|
|
throw new Error(
|
|
'Public controlplane exposure requires CONTROLPLANE_AUTH_MODE=authenticated; local_trusted is unsafe for ai.<public_base>.',
|
|
);
|
|
}
|
|
|
|
if (
|
|
CONTROLPLANE_AUTH_MODE === 'authenticated' &&
|
|
!BETTER_AUTH_SECRET.trim()
|
|
) {
|
|
throw new Error(
|
|
'Authenticated controlplane mode requires BETTER_AUTH_SECRET.',
|
|
);
|
|
}
|
|
}
|
|
|
|
export function createAuth(pool: Pool) {
|
|
assertControlplaneAuthConfiguration();
|
|
const betterAuthBaseOrigin = getBetterAuthBaseOrigin();
|
|
const reverseProxyOrigins = deriveReverseProxyOrigins();
|
|
|
|
const trustedOrigins = Array.from(
|
|
new Set([
|
|
`http://localhost:${CONTROLPLANE_API_PORT}`,
|
|
`http://${PLATFORM_INTERNAL_DOMAIN}:${CONTROLPLANE_API_PORT}`,
|
|
`http://${CONTROLPLANE_HOST_IP}:${CONTROLPLANE_API_PORT}`,
|
|
`http://ai.${PLATFORM_INTERNAL_BASE}:${CONTROLPLANE_API_PORT}`,
|
|
...(betterAuthBaseOrigin ? [betterAuthBaseOrigin] : []),
|
|
...reverseProxyOrigins,
|
|
...(PLATFORM_PUBLIC_BASE && CONTROLPLANE_EXPOSURE === 'public'
|
|
? [`https://ai.${PLATFORM_PUBLIC_BASE}`]
|
|
: []),
|
|
]),
|
|
);
|
|
|
|
const auth = betterAuth({
|
|
baseURL: BETTER_AUTH_URL,
|
|
database: pool,
|
|
secret: BETTER_AUTH_SECRET || undefined,
|
|
emailAndPassword: {
|
|
enabled: CONTROLPLANE_AUTH_MODE === 'authenticated',
|
|
},
|
|
session: {
|
|
modelName: 'cp_sessions',
|
|
cookieCache: {
|
|
enabled: true,
|
|
maxAge: 60 * 5,
|
|
},
|
|
},
|
|
user: {
|
|
modelName: 'cp_users',
|
|
additionalFields: {
|
|
role: {
|
|
type: ['operator', 'viewer'],
|
|
required: false,
|
|
defaultValue: 'operator',
|
|
input: false,
|
|
},
|
|
},
|
|
},
|
|
account: {
|
|
modelName: 'cp_accounts',
|
|
},
|
|
verification: {
|
|
modelName: 'cp_verifications',
|
|
},
|
|
trustedOrigins,
|
|
logger: {
|
|
level: 'warn',
|
|
},
|
|
});
|
|
|
|
logger.info({ mode: CONTROLPLANE_AUTH_MODE }, 'Better Auth initialized');
|
|
|
|
return auth;
|
|
}
|
|
|
|
export function isLocalTrusted(): boolean {
|
|
return CONTROLPLANE_AUTH_MODE === 'local_trusted';
|
|
}
|