From edaf16a433342654d86bbbf08de507bad8f6b60d Mon Sep 17 00:00:00 2001 From: Mike Teehan Date: Fri, 24 Jul 2026 03:06:31 +0000 Subject: [PATCH] fix: remove dead auth_refresh code, fix logout storage, align bootstrap TTL, add hash rehash, tighten CSP - Remove duplicate dead code in daemon/handlers/auth.py (auth_refresh) - Fix logout reading refresh token from localStorage instead of sessionStorage - Align bootstrap auth config access_token_ttl (900 -> 300) with hardened default - Add password hash rehash check on successful login (needs_rehash was unused) - Remove 'unsafe-inline' from CSP style-src (all styles are applied via JS DOM API) --- daemon/handlers/auth.py | 25 ------------------------- lib/auth_users.py | 7 ++++++- scripts/bootstrap_auth.py | 2 +- webui/server.py | 2 +- webui/static/hoover/components/auth.js | 2 +- 5 files changed, 9 insertions(+), 29 deletions(-) diff --git a/daemon/handlers/auth.py b/daemon/handlers/auth.py index 64f8534..b68dddd 100644 --- a/daemon/handlers/auth.py +++ b/daemon/handlers/auth.py @@ -195,31 +195,6 @@ def auth_refresh(_request: Any, body: Any) -> dict[str, Any]: }, "permissions": permissions, } - if payload is None: - raise ValueError("Invalid or expired refresh token") - - username = payload["sub"] - user = get_user(username) - if user is None: - raise ValueError("User not found") - - jti = payload.get("jti") - if jti: - blacklist_token(jti, token_type="refresh") - if username: - _clear_refresh_token_after_rotation(username) - permissions = user["permissions"] - tokens = generate_tokens(username, permissions) - - return { - "tokens": tokens, - "access_ttl": get_access_ttl(), - "user": { - "id": user["id"], - "username": user["username"], - }, - "permissions": permissions, - } @registry.register(GET_AUTH_SESSION) diff --git a/lib/auth_users.py b/lib/auth_users.py index af1fa27..f178850 100644 --- a/lib/auth_users.py +++ b/lib/auth_users.py @@ -27,7 +27,7 @@ from lib.db import ( Q_UPSERT_PERMISSION, get_db, ) -from lib.password import hash_password, verify_password +from lib.password import hash_password, needs_rehash, verify_password logger = logging.getLogger(__name__) @@ -116,6 +116,11 @@ def verify_user_password(username: str, password: str) -> dict[str, Any] | None: return None if not verify_password(password, user["password_hash"]): return None + if needs_rehash(user["password_hash"]): + new_hash = hash_password(password) + db = get_db() + db.run(Q_UPDATE_PASSWORD, (new_hash, username)) + logger.info("Password hash rehashed for %r (param upgrade)", username) return { "id": user["id"], "username": user["username"], diff --git a/scripts/bootstrap_auth.py b/scripts/bootstrap_auth.py index 673ccf3..70c47b4 100644 --- a/scripts/bootstrap_auth.py +++ b/scripts/bootstrap_auth.py @@ -45,7 +45,7 @@ def main() -> None: config = { "jwt": { - "access_token_ttl": 900, + "access_token_ttl": 300, "refresh_token_ttl": 604800, "algorithm": "HS256", }, diff --git a/webui/server.py b/webui/server.py index 70b462d..22daad7 100644 --- a/webui/server.py +++ b/webui/server.py @@ -238,7 +238,7 @@ def _log_request_finish(response): response.headers["Content-Security-Policy"] = ( "default-src 'self'; " "script-src 'self'; " - "style-src 'self' 'unsafe-inline'; " + "style-src 'self'; " "img-src 'self' data:; " "font-src 'self'; " "connect-src 'self'; " diff --git a/webui/static/hoover/components/auth.js b/webui/static/hoover/components/auth.js index 8507f7e..9538cf8 100644 --- a/webui/static/hoover/components/auth.js +++ b/webui/static/hoover/components/auth.js @@ -69,7 +69,7 @@ export async function logout() { 'Accept': 'application/json', 'Authorization': 'Bearer ' + token, }; - const refresh = localStorage.getItem('vw:refresh'); + const refresh = sessionStorage.getItem('vw:refresh'); await fetch('/api/auth/logout', { method: 'POST', headers,