From e40aa9cbba8334d04178b51d4736696f2aca6721 Mon Sep 17 00:00:00 2001 From: awais786 Date: Fri, 1 May 2026 20:07:14 +0500 Subject: [PATCH] fix(logout): drive portal-host prefix from required SMB_NAME env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The logout redirect hardcoded "moneta." as the portal-host prefix in auth-utils.ts (PR #10's quick fix for the foss → moneta cutover). Both "foss." and "moneta." silently break the next domain move. Make the prefix env-driven via a required SMB_NAME var (no default — docker-entrypoint.js exits non-zero at startup if the backend AUTH_TYPE is SSO and SMB_NAME is unset, instead of silently rendering the wrong portal host at logout). Plumbed via the same Pattern B2 mechanism as every other NEXT_PUBLIC_* var: - Dockerfile: ARG/ENV NEXT_PUBLIC_SMB_NAME=__NEXT_PUBLIC_SMB_NAME__ - docker-entrypoint.js: substitutes from process.env.SMB_NAME - auth-utils.ts: reads process.env.NEXT_PUBLIC_SMB_NAME!.trim() Container env name (SMB_NAME) is uniform across every devstack app behind ForwardAuth. See sso-rules RULES.md section 1 Logout. Co-Authored-By: Claude Opus 4.7 (1M context) --- surfsense_web/.env.example | 8 ++++++++ surfsense_web/Dockerfile | 2 ++ surfsense_web/docker-entrypoint.js | 13 +++++++++++++ surfsense_web/lib/auth-utils.ts | 7 ++++--- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/surfsense_web/.env.example b/surfsense_web/.env.example index 2313f3e56f..45f6289e55 100644 --- a/surfsense_web/.env.example +++ b/surfsense_web/.env.example @@ -5,6 +5,14 @@ NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE=LOCAL or GOOGLE NEXT_PUBLIC_OIDC_LOGOUT_URL=https:///logout NEXT_PUBLIC_OIDC_CLIENT_ID= NEXT_PUBLIC_OAUTH2_PROXY_URL=https://auth. + +# Required when AUTH_TYPE=SSO: portal hostname prefix for the SPA +# logout redirect (-..). Must +# match the ForwardAuth portal host. Same env name across every +# devstack app — see sso-rules RULES.md §1 Logout. The container reads +# SMB_NAME; docker-entrypoint.js substitutes the bundle's +# __NEXT_PUBLIC_SMB_NAME__ placeholder at startup. +SMB_NAME= NEXT_PUBLIC_ETL_SERVICE=UNSTRUCTURED or LLAMACLOUD or DOCLING NEXT_PUBLIC_ZERO_CACHE_URL=http://localhost:4848 diff --git a/surfsense_web/Dockerfile b/surfsense_web/Dockerfile index b16b3f066c..0cfa288730 100644 --- a/surfsense_web/Dockerfile +++ b/surfsense_web/Dockerfile @@ -38,6 +38,7 @@ ARG NEXT_PUBLIC_ETL_SERVICE=__NEXT_PUBLIC_ETL_SERVICE__ ARG NEXT_PUBLIC_ZERO_CACHE_URL=__NEXT_PUBLIC_ZERO_CACHE_URL__ ARG NEXT_PUBLIC_DEPLOYMENT_MODE=__NEXT_PUBLIC_DEPLOYMENT_MODE__ ARG NEXT_PUBLIC_OAUTH2_PROXY_URL=__NEXT_PUBLIC_OAUTH2_PROXY_URL__ +ARG NEXT_PUBLIC_SMB_NAME=__NEXT_PUBLIC_SMB_NAME__ # These are baked at build time (not placeholder-substituted). Next.js inlines # them as literal strings and terser dead-code-eliminates branches based on # truthiness; placeholder tokens look truthy and defeat that optimization. @@ -51,6 +52,7 @@ ENV NEXT_PUBLIC_ETL_SERVICE=$NEXT_PUBLIC_ETL_SERVICE ENV NEXT_PUBLIC_ZERO_CACHE_URL=$NEXT_PUBLIC_ZERO_CACHE_URL ENV NEXT_PUBLIC_DEPLOYMENT_MODE=$NEXT_PUBLIC_DEPLOYMENT_MODE ENV NEXT_PUBLIC_OAUTH2_PROXY_URL=$NEXT_PUBLIC_OAUTH2_PROXY_URL +ENV NEXT_PUBLIC_SMB_NAME=$NEXT_PUBLIC_SMB_NAME ENV NEXT_PUBLIC_LOGOUT_REDIRECT_URL=$NEXT_PUBLIC_LOGOUT_REDIRECT_URL ENV NEXT_PUBLIC_OIDC_LOGOUT_URL=$NEXT_PUBLIC_OIDC_LOGOUT_URL ENV NEXT_PUBLIC_OIDC_CLIENT_ID=$NEXT_PUBLIC_OIDC_CLIENT_ID diff --git a/surfsense_web/docker-entrypoint.js b/surfsense_web/docker-entrypoint.js index b71264550f..82181d269c 100644 --- a/surfsense_web/docker-entrypoint.js +++ b/surfsense_web/docker-entrypoint.js @@ -12,6 +12,18 @@ const fs = require("fs"); const path = require("path"); +// SMB_NAME is required when AUTH_TYPE=SSO. No default — fail loudly at +// startup so the SPA never silently rewrites the logout host to the wrong +// domain. Same env name across every devstack app — see sso-rules +// RULES.md §1 Logout. +if (!process.env.SMB_NAME && process.env.NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE === "SSO") { + console.error( + "[entrypoint] ERROR: SMB_NAME env is required when NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE=SSO." + ); + console.error("[entrypoint] Set it to the portal hostname prefix (e.g. 'moneta')."); + process.exit(1); +} + const replacements = [ [ "__NEXT_PUBLIC_FASTAPI_BACKEND_URL__", @@ -28,6 +40,7 @@ const replacements = [ ], ["__NEXT_PUBLIC_DEPLOYMENT_MODE__", process.env.NEXT_PUBLIC_DEPLOYMENT_MODE || "self-hosted"], ["__NEXT_PUBLIC_OAUTH2_PROXY_URL__", process.env.NEXT_PUBLIC_OAUTH2_PROXY_URL || ""], + ["__NEXT_PUBLIC_SMB_NAME__", process.env.SMB_NAME || ""], ]; let filesProcessed = 0; diff --git a/surfsense_web/lib/auth-utils.ts b/surfsense_web/lib/auth-utils.ts index aadc550d09..5ddc702f61 100644 --- a/surfsense_web/lib/auth-utils.ts +++ b/surfsense_web/lib/auth-utils.ts @@ -239,9 +239,10 @@ export async function logout(): Promise { clearAllTokens(); if (typeof window !== "undefined") { - // Rewrite "foss-." → "foss." so we land on the portal - // (outside ForwardAuth) instead of SurfSense's own root, which would silently re-auth. - const portalHost = window.location.hostname.replace(/^[^.]*\./, "moneta."); + // Rewrite "-." → "." so we land on the + // portal (outside ForwardAuth) instead of SurfSense's own root, which would silently re-auth. + const smbName = process.env.NEXT_PUBLIC_SMB_NAME!.trim(); + const portalHost = window.location.hostname.replace(/^[^.]*\./, `${smbName}.`); window.location.href = `${window.location.protocol}//${portalHost}`; return true; }