login page polishing

frdel committed Sep 23, 2025 at 09:49 UTC 68147778690483dd1b0b9f80436a1f77dbe58084
3 files changed +17 -8
run_ui.py
+14 -7
@@ -1,6 +1,7 @@
1 from datetime import timedelta
2 import os
3 import secrets
4 +import hashlib
5 import time
6 import socket
7 import struct
@@ -38,7 +39,6 @@ webapp.config.update(
39 PERMANENT_SESSION_LIFETIME=timedelta(days=1)
40 )
41
41 -
42 lock = threading.Lock()
43
44 # Set up basic authentication for UI and API but not MCP
@@ -114,16 +114,23 @@ def requires_loopback(f):
114 return decorated
115
116
117 +def _get_credentials_hash():
118 + user = dotenv.get_dotenv_value("AUTH_LOGIN")
119 + password = dotenv.get_dotenv_value("AUTH_PASSWORD")
120 + if not user:
121 + return None
122 + return hashlib.sha256(f"{user}:{password}".encode()).hexdigest()
123 +
124 # require authentication for handlers
125 def requires_auth(f):
126 @wraps(f)
127 async def decorated(*args, **kwargs):
121 - user = dotenv.get_dotenv_value("AUTH_LOGIN")
128 + user_pass_hash = _get_credentials_hash()
129 # If no auth is configured, just proceed
123 - if not user:
130 + if not user_pass_hash:
131 return await f(*args, **kwargs)
125 -
126 - if not session.get('authenticated'):
132 +
133 + if session.get('authentication') != user_pass_hash:
134 return redirect(url_for('login'))
135
136 return await f(*args, **kwargs)
@@ -151,7 +158,7 @@ async def login():
158 password = dotenv.get_dotenv_value("AUTH_PASSWORD")
159
160 if request.form['username'] == user and request.form['password'] == password:
154 - session['authenticated'] = True
161 + session['authentication'] = _get_credentials_hash()
162 return redirect(url_for('serve_index'))
163 else:
164 error = 'Invalid Credentials. Please try again.'
@@ -161,7 +168,7 @@ async def login():
168
169 @webapp.route("/logout")
170 async def logout():
164 - session.pop('authenticated', None)
171 + session.pop('authentication', None)
172 return redirect(url_for('login'))
173
174 # handle default address, load index
webui/index.html
-1
@@ -130,7 +130,6 @@
130 <script type="text/javascript" src="js/settings.js"></script>
131 <script type="text/javascript" src="js/file_browser.js"></script>
132 <script type="text/javascript" src="js/modal.js"></script>
133 - <script type="module" src="js/tunnel.js"></script>
133 </head>
134
135 <body class="dark-mode device-pointer">
webui/js/api.js
+3
@@ -52,6 +52,9 @@ export async function fetchApi(url, request) {
52 // retry the request with new token
53 csrfToken = null;
54 return await _wrap(false);
55 + }else if(response.redirected && response.url.endsWith("/login")){
56 + // redirect to login
57 + window.location.href = response.url;
58 }
59
60 // return the response