clawdie-ai/src/auth.ts
Operator & Codex bab4f76439 Reorder shared service IPs and switch docs to English root
---
Build: pass | Tests: FAIL — Tests  9 failed | 2081 passed | 4 skipped (2094)
2026-05-02 20:21:19 +02:00

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';
}