Merge remote-tracking branch 'public-gh/master'
* public-gh/master: fix(auth): apply effective trusted origins and honor allowed hostnames in public mode fix(onboard): preserve env-derived secrets defaults and report ignored exposure env in local_trusted mode fix: parseBooleanFromEnv silently treats common truthy values as false set `PAPERCLIP_PUBLIC_URL` in compose files centralize URLs into single canonical URL var
This commit is contained in:
@@ -28,6 +28,12 @@ function resolveDbUrl(configPath?: string) {
|
|||||||
|
|
||||||
function resolveBaseUrl(configPath?: string, explicitBaseUrl?: string) {
|
function resolveBaseUrl(configPath?: string, explicitBaseUrl?: string) {
|
||||||
if (explicitBaseUrl) return explicitBaseUrl.replace(/\/+$/, "");
|
if (explicitBaseUrl) return explicitBaseUrl.replace(/\/+$/, "");
|
||||||
|
const fromEnv =
|
||||||
|
process.env.PAPERCLIP_PUBLIC_URL ??
|
||||||
|
process.env.PAPERCLIP_AUTH_PUBLIC_BASE_URL ??
|
||||||
|
process.env.BETTER_AUTH_URL ??
|
||||||
|
process.env.BETTER_AUTH_BASE_URL;
|
||||||
|
if (fromEnv?.trim()) return fromEnv.trim().replace(/\/+$/, "");
|
||||||
const config = readConfig(configPath);
|
const config = readConfig(configPath);
|
||||||
if (config?.auth.baseUrlMode === "explicit" && config.auth.publicBaseUrl) {
|
if (config?.auth.baseUrlMode === "explicit" && config.auth.publicBaseUrl) {
|
||||||
return config.auth.publicBaseUrl.replace(/\/+$/, "");
|
return config.auth.publicBaseUrl.replace(/\/+$/, "");
|
||||||
|
|||||||
@@ -118,6 +118,29 @@ function collectDeploymentEnvRows(config: PaperclipConfig | null, configPath: st
|
|||||||
const dbUrl = process.env.DATABASE_URL ?? config?.database?.connectionString ?? "";
|
const dbUrl = process.env.DATABASE_URL ?? config?.database?.connectionString ?? "";
|
||||||
const databaseMode = config?.database?.mode ?? "embedded-postgres";
|
const databaseMode = config?.database?.mode ?? "embedded-postgres";
|
||||||
const dbUrlSource: EnvSource = process.env.DATABASE_URL ? "env" : config?.database?.connectionString ? "config" : "missing";
|
const dbUrlSource: EnvSource = process.env.DATABASE_URL ? "env" : config?.database?.connectionString ? "config" : "missing";
|
||||||
|
const publicUrl =
|
||||||
|
process.env.PAPERCLIP_PUBLIC_URL ??
|
||||||
|
process.env.PAPERCLIP_AUTH_PUBLIC_BASE_URL ??
|
||||||
|
process.env.BETTER_AUTH_URL ??
|
||||||
|
process.env.BETTER_AUTH_BASE_URL ??
|
||||||
|
config?.auth?.publicBaseUrl ??
|
||||||
|
"";
|
||||||
|
const publicUrlSource: EnvSource =
|
||||||
|
process.env.PAPERCLIP_PUBLIC_URL
|
||||||
|
? "env"
|
||||||
|
: process.env.PAPERCLIP_AUTH_PUBLIC_BASE_URL || process.env.BETTER_AUTH_URL || process.env.BETTER_AUTH_BASE_URL
|
||||||
|
? "env"
|
||||||
|
: config?.auth?.publicBaseUrl
|
||||||
|
? "config"
|
||||||
|
: "missing";
|
||||||
|
let trustedOriginsDefault = "";
|
||||||
|
if (publicUrl) {
|
||||||
|
try {
|
||||||
|
trustedOriginsDefault = new URL(publicUrl).origin;
|
||||||
|
} catch {
|
||||||
|
trustedOriginsDefault = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const heartbeatInterval = process.env.HEARTBEAT_SCHEDULER_INTERVAL_MS ?? DEFAULT_HEARTBEAT_SCHEDULER_INTERVAL_MS;
|
const heartbeatInterval = process.env.HEARTBEAT_SCHEDULER_INTERVAL_MS ?? DEFAULT_HEARTBEAT_SCHEDULER_INTERVAL_MS;
|
||||||
const heartbeatEnabled = process.env.HEARTBEAT_SCHEDULER_ENABLED ?? "true";
|
const heartbeatEnabled = process.env.HEARTBEAT_SCHEDULER_ENABLED ?? "true";
|
||||||
@@ -192,6 +215,24 @@ function collectDeploymentEnvRows(config: PaperclipConfig | null, configPath: st
|
|||||||
required: false,
|
required: false,
|
||||||
note: "HTTP listen port",
|
note: "HTTP listen port",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "PAPERCLIP_PUBLIC_URL",
|
||||||
|
value: publicUrl,
|
||||||
|
source: publicUrlSource,
|
||||||
|
required: false,
|
||||||
|
note: "Canonical public URL for auth/callback/invite origin wiring",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "BETTER_AUTH_TRUSTED_ORIGINS",
|
||||||
|
value: process.env.BETTER_AUTH_TRUSTED_ORIGINS ?? trustedOriginsDefault,
|
||||||
|
source: process.env.BETTER_AUTH_TRUSTED_ORIGINS
|
||||||
|
? "env"
|
||||||
|
: trustedOriginsDefault
|
||||||
|
? "default"
|
||||||
|
: "missing",
|
||||||
|
required: false,
|
||||||
|
note: "Comma-separated auth origin allowlist (auto-derived from PAPERCLIP_PUBLIC_URL when possible)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "PAPERCLIP_AGENT_JWT_TTL_SECONDS",
|
key: "PAPERCLIP_AGENT_JWT_TTL_SECONDS",
|
||||||
value: process.env.PAPERCLIP_AGENT_JWT_TTL_SECONDS ?? DEFAULT_AGENT_JWT_TTL_SECONDS,
|
value: process.env.PAPERCLIP_AGENT_JWT_TTL_SECONDS ?? DEFAULT_AGENT_JWT_TTL_SECONDS,
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ type OnboardOptions = {
|
|||||||
type OnboardDefaults = Pick<PaperclipConfig, "database" | "logging" | "server" | "auth" | "storage" | "secrets">;
|
type OnboardDefaults = Pick<PaperclipConfig, "database" | "logging" | "server" | "auth" | "storage" | "secrets">;
|
||||||
|
|
||||||
const ONBOARD_ENV_KEYS = [
|
const ONBOARD_ENV_KEYS = [
|
||||||
|
"PAPERCLIP_PUBLIC_URL",
|
||||||
"DATABASE_URL",
|
"DATABASE_URL",
|
||||||
"PAPERCLIP_DB_BACKUP_ENABLED",
|
"PAPERCLIP_DB_BACKUP_ENABLED",
|
||||||
"PAPERCLIP_DB_BACKUP_INTERVAL_MINUTES",
|
"PAPERCLIP_DB_BACKUP_INTERVAL_MINUTES",
|
||||||
@@ -60,6 +61,7 @@ const ONBOARD_ENV_KEYS = [
|
|||||||
"PAPERCLIP_AUTH_BASE_URL_MODE",
|
"PAPERCLIP_AUTH_BASE_URL_MODE",
|
||||||
"PAPERCLIP_AUTH_PUBLIC_BASE_URL",
|
"PAPERCLIP_AUTH_PUBLIC_BASE_URL",
|
||||||
"BETTER_AUTH_URL",
|
"BETTER_AUTH_URL",
|
||||||
|
"BETTER_AUTH_BASE_URL",
|
||||||
"PAPERCLIP_STORAGE_PROVIDER",
|
"PAPERCLIP_STORAGE_PROVIDER",
|
||||||
"PAPERCLIP_STORAGE_LOCAL_DIR",
|
"PAPERCLIP_STORAGE_LOCAL_DIR",
|
||||||
"PAPERCLIP_STORAGE_S3_BUCKET",
|
"PAPERCLIP_STORAGE_S3_BUCKET",
|
||||||
@@ -106,6 +108,12 @@ function quickstartDefaultsFromEnv(): {
|
|||||||
const defaultStorage = defaultStorageConfig();
|
const defaultStorage = defaultStorageConfig();
|
||||||
const defaultSecrets = defaultSecretsConfig();
|
const defaultSecrets = defaultSecretsConfig();
|
||||||
const databaseUrl = process.env.DATABASE_URL?.trim() || undefined;
|
const databaseUrl = process.env.DATABASE_URL?.trim() || undefined;
|
||||||
|
const publicUrl =
|
||||||
|
process.env.PAPERCLIP_PUBLIC_URL?.trim() ||
|
||||||
|
process.env.PAPERCLIP_AUTH_PUBLIC_BASE_URL?.trim() ||
|
||||||
|
process.env.BETTER_AUTH_URL?.trim() ||
|
||||||
|
process.env.BETTER_AUTH_BASE_URL?.trim() ||
|
||||||
|
undefined;
|
||||||
const deploymentMode =
|
const deploymentMode =
|
||||||
parseEnumFromEnv<DeploymentMode>(process.env.PAPERCLIP_DEPLOYMENT_MODE, DEPLOYMENT_MODES) ?? "local_trusted";
|
parseEnumFromEnv<DeploymentMode>(process.env.PAPERCLIP_DEPLOYMENT_MODE, DEPLOYMENT_MODES) ?? "local_trusted";
|
||||||
const deploymentExposureFromEnv = parseEnumFromEnv<DeploymentExposure>(
|
const deploymentExposureFromEnv = parseEnumFromEnv<DeploymentExposure>(
|
||||||
@@ -114,8 +122,7 @@ function quickstartDefaultsFromEnv(): {
|
|||||||
);
|
);
|
||||||
const deploymentExposure =
|
const deploymentExposure =
|
||||||
deploymentMode === "local_trusted" ? "private" : (deploymentExposureFromEnv ?? "private");
|
deploymentMode === "local_trusted" ? "private" : (deploymentExposureFromEnv ?? "private");
|
||||||
const authPublicBaseUrl =
|
const authPublicBaseUrl = publicUrl;
|
||||||
(process.env.PAPERCLIP_AUTH_PUBLIC_BASE_URL ?? process.env.BETTER_AUTH_URL)?.trim() || undefined;
|
|
||||||
const authBaseUrlModeFromEnv = parseEnumFromEnv<AuthBaseUrlMode>(
|
const authBaseUrlModeFromEnv = parseEnumFromEnv<AuthBaseUrlMode>(
|
||||||
process.env.PAPERCLIP_AUTH_BASE_URL_MODE,
|
process.env.PAPERCLIP_AUTH_BASE_URL_MODE,
|
||||||
AUTH_BASE_URL_MODES,
|
AUTH_BASE_URL_MODES,
|
||||||
@@ -127,6 +134,15 @@ function quickstartDefaultsFromEnv(): {
|
|||||||
.map((value) => value.trim().toLowerCase())
|
.map((value) => value.trim().toLowerCase())
|
||||||
.filter((value) => value.length > 0)
|
.filter((value) => value.length > 0)
|
||||||
: [];
|
: [];
|
||||||
|
const hostnameFromPublicUrl = publicUrl
|
||||||
|
? (() => {
|
||||||
|
try {
|
||||||
|
return new URL(publicUrl).hostname.trim().toLowerCase();
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
: null;
|
||||||
const storageProvider =
|
const storageProvider =
|
||||||
parseEnumFromEnv<StorageProvider>(process.env.PAPERCLIP_STORAGE_PROVIDER, STORAGE_PROVIDERS) ??
|
parseEnumFromEnv<StorageProvider>(process.env.PAPERCLIP_STORAGE_PROVIDER, STORAGE_PROVIDERS) ??
|
||||||
defaultStorage.provider;
|
defaultStorage.provider;
|
||||||
@@ -164,7 +180,7 @@ function quickstartDefaultsFromEnv(): {
|
|||||||
exposure: deploymentExposure,
|
exposure: deploymentExposure,
|
||||||
host: process.env.HOST ?? "127.0.0.1",
|
host: process.env.HOST ?? "127.0.0.1",
|
||||||
port: Number(process.env.PORT) || 3100,
|
port: Number(process.env.PORT) || 3100,
|
||||||
allowedHostnames: Array.from(new Set(allowedHostnamesFromEnv)),
|
allowedHostnames: Array.from(new Set([...allowedHostnamesFromEnv, ...(hostnameFromPublicUrl ? [hostnameFromPublicUrl] : [])])),
|
||||||
serveUi: parseBooleanFromEnv(process.env.SERVE_UI) ?? true,
|
serveUi: parseBooleanFromEnv(process.env.SERVE_UI) ?? true,
|
||||||
},
|
},
|
||||||
auth: {
|
auth: {
|
||||||
|
|||||||
@@ -42,6 +42,32 @@ Optional overrides:
|
|||||||
PAPERCLIP_PORT=3200 PAPERCLIP_DATA_DIR=./data/pc docker compose -f docker-compose.quickstart.yml up --build
|
PAPERCLIP_PORT=3200 PAPERCLIP_DATA_DIR=./data/pc docker compose -f docker-compose.quickstart.yml up --build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you change host port or use a non-local domain, set `PAPERCLIP_PUBLIC_URL` to the external URL you will use in browser/auth flows.
|
||||||
|
|
||||||
|
## Authenticated Compose (Single Public URL)
|
||||||
|
|
||||||
|
For authenticated deployments, set one canonical public URL and let Paperclip derive auth/callback defaults:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
paperclip:
|
||||||
|
environment:
|
||||||
|
PAPERCLIP_DEPLOYMENT_MODE: authenticated
|
||||||
|
PAPERCLIP_DEPLOYMENT_EXPOSURE: private
|
||||||
|
PAPERCLIP_PUBLIC_URL: https://desk.koker.net
|
||||||
|
```
|
||||||
|
|
||||||
|
`PAPERCLIP_PUBLIC_URL` is used as the primary source for:
|
||||||
|
|
||||||
|
- auth public base URL
|
||||||
|
- Better Auth base URL defaults
|
||||||
|
- bootstrap invite URL defaults
|
||||||
|
- hostname allowlist defaults (hostname extracted from URL)
|
||||||
|
|
||||||
|
Granular overrides remain available if needed (`PAPERCLIP_AUTH_PUBLIC_BASE_URL`, `BETTER_AUTH_URL`, `BETTER_AUTH_TRUSTED_ORIGINS`, `PAPERCLIP_ALLOWED_HOSTNAMES`).
|
||||||
|
|
||||||
|
Set `PAPERCLIP_ALLOWED_HOSTNAMES` explicitly only when you need additional hostnames beyond the public URL host (for example Tailscale/LAN aliases or multiple private hostnames).
|
||||||
|
|
||||||
## Claude + Codex Local Adapters in Docker
|
## Claude + Codex Local Adapters in Docker
|
||||||
|
|
||||||
The image pre-installs:
|
The image pre-installs:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ services:
|
|||||||
ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}"
|
ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}"
|
||||||
PAPERCLIP_DEPLOYMENT_MODE: "authenticated"
|
PAPERCLIP_DEPLOYMENT_MODE: "authenticated"
|
||||||
PAPERCLIP_DEPLOYMENT_EXPOSURE: "private"
|
PAPERCLIP_DEPLOYMENT_EXPOSURE: "private"
|
||||||
PAPERCLIP_ALLOWED_HOSTNAMES: "${PAPERCLIP_ALLOWED_HOSTNAMES:-localhost}"
|
PAPERCLIP_PUBLIC_URL: "${PAPERCLIP_PUBLIC_URL:-http://localhost:3100}"
|
||||||
BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:?BETTER_AUTH_SECRET must be set}"
|
BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:?BETTER_AUTH_SECRET must be set}"
|
||||||
volumes:
|
volumes:
|
||||||
- "${PAPERCLIP_DATA_DIR:-./data/docker-paperclip}:/paperclip"
|
- "${PAPERCLIP_DATA_DIR:-./data/docker-paperclip}:/paperclip"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ services:
|
|||||||
SERVE_UI: "true"
|
SERVE_UI: "true"
|
||||||
PAPERCLIP_DEPLOYMENT_MODE: "authenticated"
|
PAPERCLIP_DEPLOYMENT_MODE: "authenticated"
|
||||||
PAPERCLIP_DEPLOYMENT_EXPOSURE: "private"
|
PAPERCLIP_DEPLOYMENT_EXPOSURE: "private"
|
||||||
PAPERCLIP_ALLOWED_HOSTNAMES: "${PAPERCLIP_ALLOWED_HOSTNAMES:-localhost}"
|
PAPERCLIP_PUBLIC_URL: "${PAPERCLIP_PUBLIC_URL:-http://localhost:3100}"
|
||||||
BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:?BETTER_AUTH_SECRET must be set}"
|
BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET:?BETTER_AUTH_SECRET must be set}"
|
||||||
volumes:
|
volumes:
|
||||||
- paperclip-data:/paperclip
|
- paperclip-data:/paperclip
|
||||||
|
|||||||
@@ -42,13 +42,38 @@ function headersFromExpressRequest(req: Request): Headers {
|
|||||||
return headersFromNodeHeaders(req.headers);
|
return headersFromNodeHeaders(req.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createBetterAuthInstance(db: Db, config: Config): BetterAuthInstance {
|
export function deriveAuthTrustedOrigins(config: Config): string[] {
|
||||||
|
const baseUrl = config.authBaseUrlMode === "explicit" ? config.authPublicBaseUrl : undefined;
|
||||||
|
const trustedOrigins = new Set<string>();
|
||||||
|
|
||||||
|
if (baseUrl) {
|
||||||
|
try {
|
||||||
|
trustedOrigins.add(new URL(baseUrl).origin);
|
||||||
|
} catch {
|
||||||
|
// Better Auth will surface invalid base URL separately.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.deploymentMode === "authenticated") {
|
||||||
|
for (const hostname of config.allowedHostnames) {
|
||||||
|
const trimmed = hostname.trim().toLowerCase();
|
||||||
|
if (!trimmed) continue;
|
||||||
|
trustedOrigins.add(`https://${trimmed}`);
|
||||||
|
trustedOrigins.add(`http://${trimmed}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(trustedOrigins);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createBetterAuthInstance(db: Db, config: Config, trustedOrigins?: string[]): BetterAuthInstance {
|
||||||
const baseUrl = config.authBaseUrlMode === "explicit" ? config.authPublicBaseUrl : undefined;
|
const baseUrl = config.authBaseUrlMode === "explicit" ? config.authPublicBaseUrl : undefined;
|
||||||
const secret = process.env.BETTER_AUTH_SECRET ?? process.env.PAPERCLIP_AGENT_JWT_SECRET ?? "paperclip-dev-secret";
|
const secret = process.env.BETTER_AUTH_SECRET ?? process.env.PAPERCLIP_AGENT_JWT_SECRET ?? "paperclip-dev-secret";
|
||||||
|
const effectiveTrustedOrigins = trustedOrigins ?? deriveAuthTrustedOrigins(config);
|
||||||
|
|
||||||
const authConfig = {
|
const authConfig = {
|
||||||
baseURL: baseUrl,
|
baseURL: baseUrl,
|
||||||
secret,
|
secret,
|
||||||
|
trustedOrigins: effectiveTrustedOrigins,
|
||||||
database: drizzleAdapter(db, {
|
database: drizzleAdapter(db, {
|
||||||
provider: "pg",
|
provider: "pg",
|
||||||
schema: {
|
schema: {
|
||||||
|
|||||||
@@ -130,9 +130,12 @@ export function loadConfig(): Config {
|
|||||||
AUTH_BASE_URL_MODES.includes(authBaseUrlModeFromEnvRaw as AuthBaseUrlMode)
|
AUTH_BASE_URL_MODES.includes(authBaseUrlModeFromEnvRaw as AuthBaseUrlMode)
|
||||||
? (authBaseUrlModeFromEnvRaw as AuthBaseUrlMode)
|
? (authBaseUrlModeFromEnvRaw as AuthBaseUrlMode)
|
||||||
: null;
|
: null;
|
||||||
|
const publicUrlFromEnv = process.env.PAPERCLIP_PUBLIC_URL;
|
||||||
const authPublicBaseUrlRaw =
|
const authPublicBaseUrlRaw =
|
||||||
process.env.PAPERCLIP_AUTH_PUBLIC_BASE_URL ??
|
process.env.PAPERCLIP_AUTH_PUBLIC_BASE_URL ??
|
||||||
process.env.BETTER_AUTH_URL ??
|
process.env.BETTER_AUTH_URL ??
|
||||||
|
process.env.BETTER_AUTH_BASE_URL ??
|
||||||
|
publicUrlFromEnv ??
|
||||||
fileConfig?.auth?.publicBaseUrl;
|
fileConfig?.auth?.publicBaseUrl;
|
||||||
const authPublicBaseUrl = authPublicBaseUrlRaw?.trim() || undefined;
|
const authPublicBaseUrl = authPublicBaseUrlRaw?.trim() || undefined;
|
||||||
const authBaseUrlMode: AuthBaseUrlMode =
|
const authBaseUrlMode: AuthBaseUrlMode =
|
||||||
@@ -146,8 +149,24 @@ export function loadConfig(): Config {
|
|||||||
.map((value) => value.trim().toLowerCase())
|
.map((value) => value.trim().toLowerCase())
|
||||||
.filter((value) => value.length > 0)
|
.filter((value) => value.length > 0)
|
||||||
: null;
|
: null;
|
||||||
|
const publicUrlHostname = authPublicBaseUrl
|
||||||
|
? (() => {
|
||||||
|
try {
|
||||||
|
return new URL(authPublicBaseUrl).hostname.trim().toLowerCase();
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
: null;
|
||||||
const allowedHostnames = Array.from(
|
const allowedHostnames = Array.from(
|
||||||
new Set((allowedHostnamesFromEnv ?? fileConfig?.server.allowedHostnames ?? []).map((value) => value.trim().toLowerCase()).filter(Boolean)),
|
new Set(
|
||||||
|
[
|
||||||
|
...(allowedHostnamesFromEnv ?? fileConfig?.server.allowedHostnames ?? []),
|
||||||
|
...(publicUrlHostname ? [publicUrlHostname] : []),
|
||||||
|
]
|
||||||
|
.map((value) => value.trim().toLowerCase())
|
||||||
|
.filter(Boolean),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
const companyDeletionEnvRaw = process.env.PAPERCLIP_ENABLE_COMPANY_DELETION;
|
const companyDeletionEnvRaw = process.env.PAPERCLIP_ENABLE_COMPANY_DELETION;
|
||||||
const companyDeletionEnabled =
|
const companyDeletionEnabled =
|
||||||
|
|||||||
@@ -412,6 +412,7 @@ if (config.deploymentMode === "authenticated") {
|
|||||||
const {
|
const {
|
||||||
createBetterAuthHandler,
|
createBetterAuthHandler,
|
||||||
createBetterAuthInstance,
|
createBetterAuthInstance,
|
||||||
|
deriveAuthTrustedOrigins,
|
||||||
resolveBetterAuthSession,
|
resolveBetterAuthSession,
|
||||||
resolveBetterAuthSessionFromHeaders,
|
resolveBetterAuthSessionFromHeaders,
|
||||||
} = await import("./auth/better-auth.js");
|
} = await import("./auth/better-auth.js");
|
||||||
@@ -422,7 +423,25 @@ if (config.deploymentMode === "authenticated") {
|
|||||||
"authenticated mode requires BETTER_AUTH_SECRET (or PAPERCLIP_AGENT_JWT_SECRET) to be set",
|
"authenticated mode requires BETTER_AUTH_SECRET (or PAPERCLIP_AGENT_JWT_SECRET) to be set",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const auth = createBetterAuthInstance(db as any, config);
|
const derivedTrustedOrigins = deriveAuthTrustedOrigins(config);
|
||||||
|
const envTrustedOrigins = (process.env.BETTER_AUTH_TRUSTED_ORIGINS ?? "")
|
||||||
|
.split(",")
|
||||||
|
.map((value) => value.trim())
|
||||||
|
.filter((value) => value.length > 0);
|
||||||
|
const effectiveTrustedOrigins = Array.from(new Set([...derivedTrustedOrigins, ...envTrustedOrigins]));
|
||||||
|
logger.info(
|
||||||
|
{
|
||||||
|
authBaseUrlMode: config.authBaseUrlMode,
|
||||||
|
authPublicBaseUrl: config.authPublicBaseUrl ?? null,
|
||||||
|
trustedOrigins: effectiveTrustedOrigins,
|
||||||
|
trustedOriginsSource: {
|
||||||
|
derived: derivedTrustedOrigins.length,
|
||||||
|
env: envTrustedOrigins.length,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Authenticated mode auth origin configuration",
|
||||||
|
);
|
||||||
|
const auth = createBetterAuthInstance(db as any, config, effectiveTrustedOrigins);
|
||||||
betterAuthHandler = createBetterAuthHandler(auth);
|
betterAuthHandler = createBetterAuthHandler(auth);
|
||||||
resolveSession = (req) => resolveBetterAuthSession(auth, req);
|
resolveSession = (req) => resolveBetterAuthSession(auth, req);
|
||||||
resolveSessionFromHeaders = (headers) => resolveBetterAuthSessionFromHeaders(auth, headers);
|
resolveSessionFromHeaders = (headers) => resolveBetterAuthSessionFromHeaders(auth, headers);
|
||||||
|
|||||||
Reference in New Issue
Block a user