fix(hub): persistent browser profile so logout survives window reopen
The Hub launcher used an ephemeral /tmp profile deleted on exit, which wiped the hub_manual_logout marker cookie. On reopen, /auto-login minted a new session and logged the user straight back in without a password. Use a persistent per-user profile under XDG_STATE_HOME and drop the deletion trap so the logout marker survives close/reopen. Keep --skip-origin-startup-dialog. Adds regression tests.
This commit is contained in:
+8
-4
@@ -17,10 +17,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
Sovran Hub launcher runs it.
|
Sovran Hub launcher runs it.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- The Hub launcher now passes `--skip-origin-startup-dialog` to Brave
|
- The Hub launcher now uses a persistent per-user browser profile
|
||||||
Origin so its one-time "Proceed with Origin for free on Linux" onboarding
|
(`$XDG_STATE_HOME/sovran-hub-browser`) instead of a throwaway `/tmp`
|
||||||
screen doesn't reappear (and block auto-login) on every launch, since the
|
profile that was deleted on exit. The throwaway profile wiped the
|
||||||
Hub runs with a throwaway profile.
|
`hub_manual_logout` marker cookie on every close, so `/auto-login`
|
||||||
|
silently logged the user straight back in after they signed out and
|
||||||
|
reopened the Hub window. The persistent profile makes explicit logout
|
||||||
|
stick (until the next password login) while still skipping Brave Origin's
|
||||||
|
one-time startup dialog.
|
||||||
- The Hub now redirects to the login page when an API request finds an
|
- The Hub now redirects to the login page when an API request finds an
|
||||||
expired browser session, instead of leaving the dashboard tile grid in a
|
expired browser session, instead of leaving the dashboard tile grid in a
|
||||||
loading state while `/api/services` continues returning 401 every five
|
loading state while `/api/services` continues returning 401 every five
|
||||||
|
|||||||
@@ -282,19 +282,29 @@ let
|
|||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# ── Brave Origin launcher wrapper: stable profile dir so Wayland app_id is
|
# ── Brave Origin launcher wrapper: a *persistent* per-user profile dir.
|
||||||
# deterministic and GNOME Shell can match the window to the .desktop
|
# It must NOT be wiped on exit: the Hub's logout marker cookie
|
||||||
# entry (fixes generic gear icon appearing in the dock).
|
# (hub_manual_logout) and the session cookie live in this profile.
|
||||||
|
# Launching from a fresh/ephemeral profile every time throws away the
|
||||||
|
# marker, so /auto-login would mint a new session and silently log the
|
||||||
|
# user straight back in after they signed out and reopened the window.
|
||||||
|
# A stable directory also keeps the Wayland app_id deterministic so
|
||||||
|
# GNOME Shell can match the window to the .desktop entry (dock icon).
|
||||||
hub-brave-wrapper = pkgs.writeShellScript "sovran-hub-brave.sh" ''
|
hub-brave-wrapper = pkgs.writeShellScript "sovran-hub-brave.sh" ''
|
||||||
export PATH="${lib.makeBinPath [ pkgs.brave-origin pkgs.coreutils ]}:$PATH"
|
export PATH="${lib.makeBinPath [ pkgs.brave-origin pkgs.coreutils ]}:$PATH"
|
||||||
HUB_DATA="/tmp/sovran-hub-brave-$(id -u)"
|
# Per-user, persistent browser state. $XDG_STATE_HOME keeps it out of the
|
||||||
|
# way of backups and survives reboots and window close/reopen.
|
||||||
|
if [ -n "$XDG_STATE_HOME" ]; then
|
||||||
|
HUB_DATA="$XDG_STATE_HOME/sovran-hub-browser"
|
||||||
|
else
|
||||||
|
HUB_DATA="$HOME/.local/state/sovran-hub-browser"
|
||||||
|
fi
|
||||||
mkdir -p "$HUB_DATA"
|
mkdir -p "$HUB_DATA"
|
||||||
trap '[ -n "$HUB_DATA" ] && rm -rf "$HUB_DATA"' EXIT INT TERM
|
|
||||||
export BAMF_DESKTOP_FILE_HINT="/run/current-system/sw/share/applications/sovran-hub.desktop"
|
export BAMF_DESKTOP_FILE_HINT="/run/current-system/sw/share/applications/sovran-hub.desktop"
|
||||||
export GIO_LAUNCHED_DESKTOP_FILE="/run/current-system/sw/share/applications/sovran-hub.desktop"
|
export GIO_LAUNCHED_DESKTOP_FILE="/run/current-system/sw/share/applications/sovran-hub.desktop"
|
||||||
# The Hub launches with a throwaway profile, so Brave Origin's one-time
|
# With a persistent profile Brave Origin's one-time "Proceed with Origin
|
||||||
# "Proceed with Origin for free on Linux" onboarding dialog would reappear
|
# for free on Linux" onboarding dialog only appears once; keep skipping it
|
||||||
# on every launch and block the auto-login. Skip it (Linux-only switch).
|
# anyway so it can never block auto-login (Linux-only switch).
|
||||||
brave-origin --app=http://localhost:8937/auto-login \
|
brave-origin --app=http://localhost:8937/auto-login \
|
||||||
--skip-origin-startup-dialog \
|
--skip-origin-startup-dialog \
|
||||||
--class=sovran-hub \
|
--class=sovran-hub \
|
||||||
|
|||||||
@@ -424,6 +424,41 @@ class TestManualLogoutPersistence(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestHubBrowserProfilePersistence(unittest.TestCase):
|
||||||
|
"""The desktop launcher must keep a persistent browser profile.
|
||||||
|
|
||||||
|
The hub_manual_logout marker (and the session cookie) are stored in this
|
||||||
|
profile. If the launcher used an ephemeral /tmp profile that it deleted on
|
||||||
|
exit, closing and reopening the Hub window would wipe the marker and
|
||||||
|
/auto-login would silently log the user back in without a password.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
path = os.path.join(
|
||||||
|
_REPO_ROOT, "modules", "core", "sovran-hub.nix"
|
||||||
|
)
|
||||||
|
with open(path, encoding="utf-8") as f:
|
||||||
|
cls.wrapper = f.read()
|
||||||
|
|
||||||
|
def test_profile_is_not_under_tmp(self):
|
||||||
|
# The profile must live in a persistent per-user location, not /tmp.
|
||||||
|
self.assertNotIn("/tmp/sovran-hub-brave", self.wrapper)
|
||||||
|
|
||||||
|
def test_profile_is_not_deleted_on_exit(self):
|
||||||
|
# There must be no trap that removes the user-data-dir on exit.
|
||||||
|
self.assertNotRegex(self.wrapper, r"rm\s+-rf\s+.*HUB_DATA")
|
||||||
|
self.assertNotIn("trap '", self.wrapper)
|
||||||
|
|
||||||
|
def test_profile_is_persistent_per_user_location(self):
|
||||||
|
self.assertIn("sovran-hub-browser", self.wrapper)
|
||||||
|
# It should honour XDG_STATE_HOME (standard, persistent per-user dir).
|
||||||
|
self.assertIn("XDG_STATE_HOME", self.wrapper)
|
||||||
|
|
||||||
|
def test_launcher_still_uses_user_data_dir(self):
|
||||||
|
self.assertIn("--user-data-dir=", self.wrapper)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Persistent session store
|
# Persistent session store
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user