From 38259f3cfd12e727d73465d9f551c7cb24fb542e Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Wed, 29 Apr 2026 13:38:27 +0000 Subject: [PATCH] fix(auth): redirect to original URL after login Pass current path as ?next= when bouncing unauthenticated requests to /echo/login; after successful auth, JS reads and validates the param (must start with /echo/, not /echo/login) before redirecting. Co-Authored-By: Claude Sonnet 4.6 --- dashboard/api.py | 4 +++- dashboard/login.html | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dashboard/api.py b/dashboard/api.py index 059ed86..16d11e9 100644 --- a/dashboard/api.py +++ b/dashboard/api.py @@ -10,6 +10,7 @@ import os import sys from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path +from urllib.parse import quote as _urlquote # Make dashboard/ importable for the handler submodules (constants, # habits_helpers, handlers.*). Tests rely on this as well. @@ -284,7 +285,8 @@ class TaskBoardHandler( if fpath.is_file(): if fpath.name != 'login.html' and not self._check_dashboard_cookie(): self.send_response(302) - self.send_header('Location', '/echo/login') + next_param = _urlquote(self.path, safe='/?=&#') + self.send_header('Location', f'/echo/login?next={next_param}') self.send_header('Content-Length', '0') self.end_headers() return diff --git a/dashboard/login.html b/dashboard/login.html index 5106395..46d5d36 100644 --- a/dashboard/login.html +++ b/dashboard/login.html @@ -252,7 +252,14 @@ // Browsers auto-follow 302, so a successful login surfaces // here as a 2xx (workspace.html) or an opaqueredirect. if (res.ok || res.type === 'opaqueredirect' || res.redirected) { - var dest = res.url && res.redirected ? res.url : '/echo/workspace.html'; + // Redirect back to the page the user originally wanted, + // passed as ?next= by the server. Validate it's a safe + // 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 + : '/echo/workspace.html'; window.location.assign(dest); return; }