diff --git a/app/server/lib/auth.ts b/app/server/lib/auth.ts index 8b6c39a9..bbcf61f3 100644 --- a/app/server/lib/auth.ts +++ b/app/server/lib/auth.ts @@ -13,6 +13,7 @@ import { sso } from "@better-auth/sso"; import { config } from "../core/config"; import { db } from "../db/db"; import { cryptoUtils } from "../utils/crypto"; +import { logger } from "../utils/logger"; import { authService } from "../modules/auth/auth.service"; import { tanstackStartCookies } from "better-auth/tanstack-start"; import { isValidUsername, normalizeUsername } from "~/lib/username"; @@ -23,13 +24,21 @@ import { validateSsoProviderId } from "./auth/middlewares/validate-sso-provider- import { createUserDefaultOrg } from "./auth/helpers/create-default-org"; import { isSsoCallbackRequest, requireSsoInvitation } from "./auth/middlewares/require-sso-invitation"; import { resolveTrustedProvidersForRequest } from "./auth/middlewares/trust-sso-provider-for-linking"; +import { buildAllowedHosts } from "./auth/base-url"; export type AuthMiddlewareContext = MiddlewareContext>; +const authOrigins = [config.baseUrl, ...config.trustedOrigins]; +const { allowedHosts, invalidOrigins } = buildAllowedHosts(authOrigins); + +for (const origin of invalidOrigins) { + logger.warn(`Ignoring invalid auth origin in configuration: ${origin}`); +} + export const auth = betterAuth({ secret: await cryptoUtils.deriveSecret("better-auth"), baseURL: { - allowedHosts: [new URL(config.baseUrl).host, ...config.trustedOrigins.map((origin) => new URL(origin).host)], + allowedHosts, protocol: "auto", }, trustedOrigins: config.trustedOrigins, diff --git a/app/server/lib/auth/base-url.ts b/app/server/lib/auth/base-url.ts new file mode 100644 index 00000000..f80c8314 --- /dev/null +++ b/app/server/lib/auth/base-url.ts @@ -0,0 +1,22 @@ +type AllowedHostsResult = { + allowedHosts: string[]; + invalidOrigins: string[]; +}; + +export function buildAllowedHosts(origins: string[]): AllowedHostsResult { + const validHosts = new Set(); + const invalidOrigins: string[] = []; + + for (const origin of origins) { + try { + validHosts.add(new URL(origin).host); + } catch { + invalidOrigins.push(origin); + } + } + + return { + allowedHosts: Array.from(validHosts), + invalidOrigins, + }; +} diff --git a/app/server/modules/auth/auth.errors.ts b/app/server/modules/auth/auth.errors.ts index 65a139e8..9837461a 100644 --- a/app/server/modules/auth/auth.errors.ts +++ b/app/server/modules/auth/auth.errors.ts @@ -12,7 +12,13 @@ const INVITE_REQUIRED_ERRORS = new Set([ ]); export function mapAuthErrorToCode(error: string): LoginErrorCode { - const decoded = decodeURIComponent(error); + let decoded: string; + + try { + decoded = decodeURIComponent(error); + } catch { + return "SSO_LOGIN_FAILED"; + } if (decoded === "account not linked") { return "ACCOUNT_LINK_REQUIRED";