diff --git a/webui/app.py b/webui/app.py
index 945b777..27dabc2 100644
--- a/webui/app.py
+++ b/webui/app.py
@@ -594,10 +594,18 @@ def api_change_password():
@app.route("/api/logout")
def api_logout():
- """Возвращаем 401 с новым realm — браузер сбрасывает кеш Basic Auth."""
+ """401 + смена realm — подсказка браузеру забыть предыдущий Basic Auth.
+
+ Клиент должен дергать этот URL через XMLHttpRequest.open(..., user, password)
+ с заведомо неверной парой; иначе многие браузеры подставят сохранённые креды."""
return Response(
- "logged out", 401,
- {"WWW-Authenticate": f'Basic realm="kaskad-logout-{os.urandom(4).hex()}"'},
+ "logged out\n",
+ 401,
+ {
+ "WWW-Authenticate": f'Basic realm="kaskad-logout-{os.urandom(4).hex()}"',
+ "Cache-Control": "no-store, no-cache, must-revalidate",
+ "Pragma": "no-cache",
+ },
)
diff --git a/webui/templates/index.html b/webui/templates/index.html
index b602b4b..45711bd 100644
--- a/webui/templates/index.html
+++ b/webui/templates/index.html
@@ -10,13 +10,13 @@
Каскад
-
+
загружаю…
-
-
+
+
@@ -282,18 +282,29 @@
setTimeout(() => this.logout(), 1200);
},
- async logout() {
- // 1) бьём бэк бракованными credentials — он отвечает 401 с новым realm,
- // браузер сбрасывает кешированный Basic Auth
+ logout() {
+ // HTTP Basic Auth живёт в браузере отдельно от cookies. fetch() с ручным
+ // Authorization часто ИГНОРИРУЕТСЯ — Chrome подставляет сохранённый пароль.
+ // XMLHttpRequest.open(..., user, password) заставляет уйти именно эта пара
+ // (заведомо ложная), сервер отвечает 401 + новый realm — после этого
+ // location.replace('/') обычно снова показывает окно входа.
+ const go = () => {
+ const u = new URL(window.location.href);
+ u.pathname = '/';
+ u.search = '_bye=' + Date.now();
+ u.hash = '';
+ window.location.replace(u.href);
+ };
+ const noise = () => Math.random().toString(36).slice(2) + Date.now();
try {
- await fetch('/api/logout', {
- headers: { 'Authorization': 'Basic ' + btoa('logout:' + Math.random()) },
- cache: 'no-store',
- credentials: 'omit',
- });
- } catch {}
- // 2) редиректим на корень с уникальным query — браузер заново спросит логин/пароль
- window.location.href = '/?_=' + Date.now();
+ const xhr = new XMLHttpRequest();
+ xhr.timeout = 12000;
+ xhr.open('GET', '/api/logout?_=' + noise(), true, '__logout', noise());
+ xhr.onload = xhr.onerror = xhr.ontimeout = go;
+ xhr.send();
+ } catch (e) {
+ go();
+ }
},
fmtAge(s) {