From 93f7ffdfe490ed41a8e8324af5fe78be9864da6e Mon Sep 17 00:00:00 2001 From: awais786 Date: Fri, 1 May 2026 23:10:51 +0500 Subject: [PATCH 1/2] fix(logout): make SMB_NAME required, drop "moneta" defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Layered on top of #12. The "moneta" defaults scattered across auth-utils.ts, docker-entrypoint.js, both .env.example files, and both compose files re-introduce the bug class the env var is meant to close: a hardcoded literal that breaks silently the next time the deployment moves domains. Aligns SurfSense with the sso-rules invariant merged in awais786/sso-rules#1 (required env, no default, fail loudly) and the sibling PRs in Outline #10, Penpot #11, Twenty #2 (all fail-fast). Changes ------- - auth-utils.ts: drop `|| "moneta"` fallback. Use process.env.NEXT_PUBLIC_SMB_NAME!.trim(). Rename smbLabel → smbName to match the variable name in every other app's logout file. Drop the dead 3-line comment about the fallback. - docker-entrypoint.js: add fail-fast SSO check before the replacements array. Drop the (... || "moneta").trim() || "moneta" double-fallback — substitute process.env.SMB_NAME directly. - surfsense_web/.env.example + docker/.env.example: drop the =moneta default. Document as required under SSO with sso-rules cross-reference. - docker/docker-compose.yml: drop :-moneta. Use ${SMB_NAME:?...} so compose-up fails with a clear message if unset. - docker/docker-compose.dev.yml: same — drop the ${SMB_NAME:-${NEXT_PUBLIC_SMB_NAME:-moneta}} chain in favor of ${SMB_NAME:?...}. Co-Authored-By: Claude Opus 4.7 (1M context) --- docker/.env.example | 8 ++++++-- docker/docker-compose.dev.yml | 5 ++++- docker/docker-compose.yml | 4 +++- surfsense_web/.env.example | 11 +++++++++-- surfsense_web/docker-entrypoint.js | 17 +++++++++++++---- surfsense_web/lib/auth-utils.ts | 10 ++++------ 6 files changed, 39 insertions(+), 16 deletions(-) diff --git a/docker/.env.example b/docker/.env.example index 4bfe74abd6..2ab6b4b7ec 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -20,8 +20,12 @@ SECRET_KEY=replace_me_with_a_random_string # modes are not registered in this fork. AUTH_TYPE=SSO -# First DNS label for post-logout portal redirect (web container reads SMB_NAME; default moneta) -SMB_NAME=moneta +# Required under SSO: portal hostname prefix for the SPA logout +# redirect (-..). Same env name +# across every devstack app — see sso-rules RULES.md §1 Logout. No +# default — unset crashes the web container at startup rather than +# silently rewriting to the wrong host. +SMB_NAME= # Allow new user registrations (TRUE or FALSE) # REGISTRATION_ENABLED=TRUE diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index b717369d15..d3ca14a803 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -208,7 +208,10 @@ services: NEXT_PUBLIC_ETL_SERVICE: ${NEXT_PUBLIC_ETL_SERVICE:-DOCLING} NEXT_PUBLIC_ZERO_CACHE_URL: ${NEXT_PUBLIC_ZERO_CACHE_URL:-http://localhost:${ZERO_CACHE_PORT:-4848}} NEXT_PUBLIC_DEPLOYMENT_MODE: ${NEXT_PUBLIC_DEPLOYMENT_MODE:-self-hosted} - NEXT_PUBLIC_SMB_NAME: ${SMB_NAME:-${NEXT_PUBLIC_SMB_NAME:-moneta}} + # Required under SSO. Baked into the dev image at build time + # (this override bypasses the placeholder pattern used in prod). + # See sso-rules RULES.md §1 Logout. + NEXT_PUBLIC_SMB_NAME: ${SMB_NAME:?SMB_NAME is required (portal hostname prefix, e.g. moneta)} ports: - "${FRONTEND_PORT:-3000}:3000" env_file: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index d4eed9e820..91292e940a 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -198,7 +198,9 @@ services: NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE: ${AUTH_TYPE:-SSO} NEXT_PUBLIC_ETL_SERVICE: ${ETL_SERVICE:-DOCLING} NEXT_PUBLIC_DEPLOYMENT_MODE: ${DEPLOYMENT_MODE:-self-hosted} - SMB_NAME: ${SMB_NAME:-moneta} + # Required under SSO. docker-entrypoint.js exits non-zero at startup + # if AUTH_TYPE=SSO and SMB_NAME is unset. See sso-rules RULES.md §1. + SMB_NAME: ${SMB_NAME:?SMB_NAME is required (portal hostname prefix, e.g. moneta)} labels: - "com.centurylinklabs.watchtower.enable=true" depends_on: diff --git a/surfsense_web/.env.example b/surfsense_web/.env.example index 08ac2ec8d3..bde4595f11 100644 --- a/surfsense_web/.env.example +++ b/surfsense_web/.env.example @@ -1,8 +1,15 @@ NEXT_PUBLIC_FASTAPI_BACKEND_URL=http://localhost:8000 NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE=LOCAL or GOOGLE -# Platform portal first DNS label for post-logout redirect (SMB_NAME in Docker; default moneta) -NEXT_PUBLIC_SMB_NAME=moneta +# Required under SSO: portal hostname prefix for the SPA logout +# redirect (-..). 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. For local +# `next dev` set NEXT_PUBLIC_SMB_NAME directly here. No default — +# unset under SSO crashes startup loudly instead of silently rewriting +# to the wrong host. +NEXT_PUBLIC_SMB_NAME= # mPass proxy auth — set when deployed behind oauth2-proxy + Traefik ForwardAuth NEXT_PUBLIC_OIDC_LOGOUT_URL=https:///logout diff --git a/surfsense_web/docker-entrypoint.js b/surfsense_web/docker-entrypoint.js index 1b8ebe54a5..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,10 +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 || process.env.NEXT_PUBLIC_SMB_NAME || "moneta").trim() || "moneta", - ], + ["__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 7dcdcb7862..5ddc702f61 100644 --- a/surfsense_web/lib/auth-utils.ts +++ b/surfsense_web/lib/auth-utils.ts @@ -239,12 +239,10 @@ export async function logout(): Promise { clearAllTokens(); if (typeof window !== "undefined") { - // Rewrite "." → "." so we land on the platform portal - // (outside ForwardAuth) instead of SurfSense's own root, which would silently re-auth. - // Docker: set SMB_NAME on the container; docker-entrypoint substitutes NEXT_PUBLIC_SMB_NAME. - // Local dev: set NEXT_PUBLIC_SMB_NAME in .env (default moneta). - const smbLabel = process.env.NEXT_PUBLIC_SMB_NAME?.trim() || "moneta"; - const portalHost = window.location.hostname.replace(/^[^.]*\./, `${smbLabel}.`); + // 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; } From de58ba56615ea08412493c73fb76c1fb32024f78 Mon Sep 17 00:00:00 2001 From: awais786 Date: Sat, 2 May 2026 20:37:30 +0500 Subject: [PATCH 2/2] fix(logout): derive portal prefix from hostname instead of SMB_NAME env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drops the NEXT_PUBLIC_SMB_NAME read on the SPA logout path. The previous approach required threading SMB_NAME through devstack docker-compose runtime env → docker-entrypoint.js placeholder substitution → bundle; any broken link silently routed logout to the wrong host or crashed. Switching to a regex on window.location.hostname removes the env dependency and works for any `-.` shape: - foss-research.local.moneta.dev → foss.local.moneta.dev - moneta-research.askii.ai → moneta.askii.ai The __NEXT_PUBLIC_SMB_NAME__ placeholder + entrypoint substitution are now dead; will be removed in a separate cleanup pass. --- surfsense_web/lib/auth-utils.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/surfsense_web/lib/auth-utils.ts b/surfsense_web/lib/auth-utils.ts index 5ddc702f61..8be985c690 100644 --- a/surfsense_web/lib/auth-utils.ts +++ b/surfsense_web/lib/auth-utils.ts @@ -239,10 +239,9 @@ export async function logout(): Promise { clearAllTokens(); if (typeof window !== "undefined") { - // Rewrite "-." → "." so we land on the + // 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}.`); + const portalHost = window.location.hostname.replace(/^([^-]+)-[^.]+\.(.+)/, "$1.$2"); window.location.href = `${window.location.protocol}//${portalHost}`; return true; }