From 77df09974cefae3fe371a9001fcf01df9faf8f35 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Wed, 29 Apr 2026 14:11:22 +0000 Subject: [PATCH] fix(auth): restore /echo prefix after proxy strips it from next param The reverse proxy strips /echo/ before Python, so next=/workspace.html. Both the JS redirect and the server-side already-logged-in path now prepend /echo to produce a valid public URL. Co-Authored-By: Claude Sonnet 4.6 --- dashboard/api.py | 16 ++++++++++++---- dashboard/login.html | 7 +++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/dashboard/api.py b/dashboard/api.py index 16d11e9..3b5dff1 100644 --- a/dashboard/api.py +++ b/dashboard/api.py @@ -10,7 +10,7 @@ import os import sys from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path -from urllib.parse import quote as _urlquote +from urllib.parse import quote as _urlquote, parse_qs, urlparse # Make dashboard/ importable for the handler submodules (constants, # habits_helpers, handlers.*). Tests rely on this as well. @@ -243,11 +243,19 @@ class TaskBoardHandler( return elif self.path in ('/echo/login', '/login') or \ self.path.startswith(('/echo/login?', '/login?')): - # If already logged in, redirect to workspace; otherwise serve - # login.html (created in Lane B2). + # If already logged in, redirect to next (or workspace); otherwise serve login.html. if self._check_dashboard_cookie(): + qs = parse_qs(urlparse(self.path).query) + next_vals = qs.get('next', []) + nxt = next_vals[0] if next_vals else '' + # Proxy strips /echo/ before Python, so nxt is e.g. /workspace.html. + # Re-add the prefix so the browser lands on the right public URL. + if nxt and nxt.startswith('/') and '://' not in nxt: + dest = '/echo' + nxt + else: + dest = '/echo/workspace.html' self.send_response(302) - self.send_header('Location', '/echo/workspace.html') + self.send_header('Location', dest) self.send_header('Content-Length', '0') self.end_headers() return diff --git a/dashboard/login.html b/dashboard/login.html index 46d5d36..b584190 100644 --- a/dashboard/login.html +++ b/dashboard/login.html @@ -257,8 +257,11 @@ // relative /echo/ path to prevent open-redirect attacks. var params = new URLSearchParams(window.location.search); var next = params.get('next') || ''; - var dest = (next && /^\/echo\/[^/]/.test(next) && next.indexOf('/echo/login') !== 0) - ? next + // The proxy strips /echo/ before Python, so `next` is + // e.g. "/workspace.html". Re-add the /echo prefix for + // the browser. Guard against open-redirect (no ://). + var dest = (next && /^\/[^/]/.test(next) && next.indexOf('://') === -1) + ? '/echo' + next : '/echo/workspace.html'; window.location.assign(dest); return;