Skip to content
File

Blob: src/worker/config.ts

typescript57 lines
1import { isLoopbackHost } from "@/worker/services/url";
2 
3// Canonicalize a configured URL down to its origin. Rejects values that
4// would split discovery, ID-token `iss`, cookie domain, or invite URLs:
5// non-http(s) schemes, non-loopback http, embedded credentials, query,
6// hash, or any non-root pathname. Throw shape never echoes the offending
7// value because env vars may carry username/password components.
8const canonicalOrigin = (value: string, envName: string): string => {
9 let url: URL;
10 try {
11 url = new URL(value.trim());
12 } catch {
13 throw new Error(`${envName} is not a valid URL.`);
14 }
15 if (url.protocol !== "https:" && url.protocol !== "http:") {
16 throw new Error(`${envName} must use http or https.`);
17 }
18 if (url.protocol === "http:" && !isLoopbackHost(url.hostname)) {
19 throw new Error(`${envName} with http: is allowed only on loopback hosts.`);
20 }
21 if (url.username || url.password || url.search || url.hash) {
22 throw new Error(`${envName} must not include path, query, hash, or credentials.`);
23 }
24 if (url.pathname !== "/" && url.pathname !== "") {
25 throw new Error(`${envName} must not include path, query, hash, or credentials.`);
26 }
27 return url.origin;
28};
29 
30/**
31 * Resolve the public base URL the worker should advertise (OIDC `iss`,
32 * Better Auth `baseURL`, cookie domain).
33 *
34 * Resolution order:
35 * 1. `BETTER_AUTH_URL` env var if set and non-empty.
36 * 2. The request's own origin.
37 *
38 * Production always sets `BETTER_AUTH_URL` (wrangler.jsonc `vars`), so the
39 * fallback only fires in dev/test where the worker should reflect whatever
40 * hostname the request was made to.
41 */
42export const resolveBaseUrl = (env: Env, requestUrl: string): string => {
43 const configured = env.BETTER_AUTH_URL?.trim();
44 if (configured) {
45 return canonicalOrigin(configured, "BETTER_AUTH_URL");
46 }
47 return new URL(requestUrl).origin;
48};
49 
50export const resolveIssuer = (env: Env, requestUrl: string): string => {
51 const configured = env.OIDC_ISSUER?.trim();
52 if (configured) {
53 return canonicalOrigin(configured, "OIDC_ISSUER");
54 }
55 return resolveBaseUrl(env, requestUrl);
56};