logout inactive on exteranal auth

This commit is contained in:
Mateusz Gruszczyński
2026-05-25 10:07:51 +02:00
parent 9021b09bc5
commit ff7d836b77
6 changed files with 30 additions and 4 deletions

View File

@@ -64,4 +64,7 @@ PYTORRENT_AUTH_PROXY_AUTO_CREATE_PERMISSION=rw
PYTORRENT_PROXY_FIX_ENABLE=true PYTORRENT_PROXY_FIX_ENABLE=true
PYTORRENT_SESSION_COOKIE_SECURE=false PYTORRENT_SESSION_COOKIE_SECURE=false
#PYTORRENT_SOCKETIO_CORS_ALLOWED_ORIGINS=https://pytorrent.domain.com #PYTORRENT_SOCKETIO_CORS_ALLOWED_ORIGINS=https://pytorrent.domain.com
#PYTORRENT_API_ALLOWED_ORIGINS=https://pytorrent.domain.com #PYTORRENT_API_ALLOWED_ORIGINS=https://pytorrent.domain.com
# bypass auth on specific hosts (ex. local ip)
PYTORRENT_AUTH_BYPASS_HOSTS=10.11.1.11:8090,10.11.1.11

View File

@@ -217,3 +217,8 @@ PYTORRENT_AUTH_PROXY_USER_HEADER=Remote-User
``` ```
The configured header must contain a non-empty username. The configured header must contain a non-empty username.
## External provider logout
When `PYTORRENT_AUTH_PROVIDER=tinyauth` or `PYTORRENT_AUTH_PROVIDER=proxy` is used, pyTorrent does not render an active logout action. The authenticated session is owned by the upstream provider, so logging out must be handled by that provider, for example through the Tinyauth logout endpoint or its own UI.
The `/logout` route becomes a safe no-op redirect to the main page for external auth providers. Local authentication keeps the original pyTorrent logout behavior.

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from flask import abort, jsonify, request from flask import abort, jsonify, request
from ..services.auth import current_user, list_users, save_user, delete_user, login_user, logout_user, enabled as auth_enabled, provider as auth_provider, list_api_tokens, create_api_token, revoke_api_token from ..services.auth import current_user, list_users, save_user, delete_user, login_user, logout_user, enabled as auth_enabled, provider as auth_provider, uses_external_provider, list_api_tokens, create_api_token, revoke_api_token
def _ok(payload=None): def _ok(payload=None):
@@ -33,6 +33,8 @@ def register_auth_routes(bp):
def auth_logout(): def auth_logout():
if not auth_enabled(): if not auth_enabled():
abort(404) abort(404)
if uses_external_provider():
return _ok({"logout_managed_by_provider": True, "auth_provider": auth_provider()})
logout_user() logout_user()
return _ok() return _ok()

View File

@@ -195,6 +195,9 @@ def login():
@bp.get("/logout") @bp.get("/logout")
def logout(): def logout():
# Note: External providers such as Tinyauth own the login session, so pyTorrent must not pretend to log the user out locally.
if auth.uses_external_provider():
return redirect(url_for("main.index"))
auth.logout_user() auth.logout_user()
if not auth.enabled(): if not auth.enabled():
return redirect(url_for("main.index")) return redirect(url_for("main.index"))
@@ -212,6 +215,8 @@ def index():
bootstrap_themes=BOOTSTRAP_THEMES, bootstrap_themes=BOOTSTRAP_THEMES,
font_families=FONT_FAMILIES, font_families=FONT_FAMILIES,
auth_enabled=auth.enabled(), auth_enabled=auth.enabled(),
auth_provider=auth.provider(),
external_auth=auth.uses_external_provider(),
current_user=auth.current_user(), current_user=auth.current_user(),
) )

View File

@@ -4983,3 +4983,8 @@ body.compact-torrent-list .mobile-progress {
body.compact-torrent-list .mobile-progress .torrent-progress { body.compact-torrent-list .mobile-progress .torrent-progress {
height: 10px; height: 10px;
} }
.auth-provider-user {
cursor: default;
opacity: 0.85;
pointer-events: none;
}

View File

@@ -48,7 +48,13 @@
<button class="btn btn-xs btn-outline-info nav-btn" id="mobileToggle" title="Mobile/simple mode"><i class="fa-solid fa-mobile-screen"></i></button> <button class="btn btn-xs btn-outline-info nav-btn" id="mobileToggle" title="Mobile/simple mode"><i class="fa-solid fa-mobile-screen"></i></button>
<button id="themeToggle" class="btn btn-xs btn-outline-secondary nav-btn" title="Change theme"><i class="fa-solid fa-moon"></i></button> <button id="themeToggle" class="btn btn-xs btn-outline-secondary nav-btn" title="Change theme"><i class="fa-solid fa-moon"></i></button>
<button class="btn btn-xs btn-outline-secondary nav-btn about-nav-btn" data-bs-toggle="modal" data-bs-target="#aboutModal" title="About pyTorrent"><i class="fa-solid fa-circle-info"></i></button> <button class="btn btn-xs btn-outline-secondary nav-btn about-nav-btn" data-bs-toggle="modal" data-bs-target="#aboutModal" title="About pyTorrent"><i class="fa-solid fa-circle-info"></i></button>
{% if auth_enabled %}<a class="btn btn-xs btn-outline-danger nav-btn" href="/logout" title="Log out"><i class="fa-solid fa-right-from-bracket"></i><span> {{ current_user.username if current_user else 'logout' }}</span></a>{% endif %} {% if auth_enabled %}
{% if external_auth %}
<button class="btn btn-xs btn-outline-secondary nav-btn auth-provider-user" type="button" disabled title="Logout is managed by {{ auth_provider }}"><i class="fa-solid fa-user-shield"></i><span> {{ current_user.username if current_user else auth_provider }}</span></button>
{% else %}
<a class="btn btn-xs btn-outline-danger nav-btn" href="/logout" title="Log out"><i class="fa-solid fa-right-from-bracket"></i><span> {{ current_user.username if current_user else 'logout' }}</span></a>
{% endif %}
{% endif %}
</div> </div>
</header> </header>
@@ -367,7 +373,7 @@
<div id="toastHost" class="toast-host"></div> <div id="toastHost" class="toast-host"></div>
<script src="{{ frontend_asset_url('socket_io_js') }}"></script> <script src="{{ frontend_asset_url('socket_io_js') }}"></script>
<script src="{{ frontend_asset_url('bootstrap_js') }}"></script> <script src="{{ frontend_asset_url('bootstrap_js') }}"></script>
<script>window.PYTORRENT = {authEnabled: {{ 1 if auth_enabled else 0 }}, currentUser: {% if current_user %}{{ current_user | tojson }}{% else %}null{% endif %}, activeProfile: {{ active_profile.id if active_profile else 'null' }}, tableColumns: {{ (prefs.table_columns_json or '{}') | safe }}, torrentSort: {{ (prefs.torrent_sort_json or '{}') | safe }}, activeFilter: {{ (prefs.active_filter if prefs and prefs.active_filter else 'all') | tojson }}, detailPanelHeight: {{ prefs.detail_panel_height if prefs and prefs.detail_panel_height else 255 }}, peersRefreshSeconds: {{ prefs.peers_refresh_seconds if prefs else 0 }}, portCheckEnabled: {{ 1 if prefs and prefs.port_check_enabled else 0 }}, interfaceScale: {{ prefs.interface_scale if prefs and prefs.interface_scale else 100 }}, compactTorrentListEnabled: {{ 1 if prefs and prefs.compact_torrent_list_enabled else 0 }}, titleSpeedEnabled: {{ 1 if prefs and prefs.title_speed_enabled else 0 }}, trackerFaviconsEnabled: {{ 1 if prefs and prefs.tracker_favicons_enabled else 0 }}, reverseDnsEnabled: {{ 1 if prefs and prefs.reverse_dns_enabled else 0 }}, automationToastsEnabled: {{ 1 if not prefs or prefs.automation_toasts_enabled else 0 }}, smartQueueToastsEnabled: {{ 1 if not prefs or prefs.smart_queue_toasts_enabled else 0 }}, diskMonitorPaths: {{ (prefs.disk_monitor_paths_json or "[]") | safe }}, diskMonitorMode: {{ (prefs.disk_monitor_mode if prefs and prefs.disk_monitor_mode else "default") | tojson }}, diskMonitorSelectedPath: {{ (prefs.disk_monitor_selected_path if prefs and prefs.disk_monitor_selected_path else "") | tojson }}, bootstrapTheme: {{ (prefs.bootstrap_theme if prefs and prefs.bootstrap_theme else 'default') | tojson }}, fontFamily: {{ (prefs.font_family if prefs and prefs.font_family else 'default') | tojson }}, footerItems: {{ (prefs.footer_items_json or '{}') | safe }}, bootstrapThemes: {{ bootstrap_themes | tojson }}, bootstrapThemeUrls: { {% for key in bootstrap_themes.keys() %}{{ key | tojson }}: {{ bootstrap_theme_url(key) | tojson }}{% if not loop.last %}, {% endif %}{% endfor %} }, fontFamilies: {{ font_families | tojson }}};</script> <script>window.PYTORRENT = {authEnabled: {{ 1 if auth_enabled else 0 }}, authProvider: {{ auth_provider | tojson }}, externalAuth: {{ 1 if external_auth else 0 }}, currentUser: {% if current_user %}{{ current_user | tojson }}{% else %}null{% endif %}, activeProfile: {{ active_profile.id if active_profile else 'null' }}, tableColumns: {{ (prefs.table_columns_json or '{}') | safe }}, torrentSort: {{ (prefs.torrent_sort_json or '{}') | safe }}, activeFilter: {{ (prefs.active_filter if prefs and prefs.active_filter else 'all') | tojson }}, detailPanelHeight: {{ prefs.detail_panel_height if prefs and prefs.detail_panel_height else 255 }}, peersRefreshSeconds: {{ prefs.peers_refresh_seconds if prefs else 0 }}, portCheckEnabled: {{ 1 if prefs and prefs.port_check_enabled else 0 }}, interfaceScale: {{ prefs.interface_scale if prefs and prefs.interface_scale else 100 }}, compactTorrentListEnabled: {{ 1 if prefs and prefs.compact_torrent_list_enabled else 0 }}, titleSpeedEnabled: {{ 1 if prefs and prefs.title_speed_enabled else 0 }}, trackerFaviconsEnabled: {{ 1 if prefs and prefs.tracker_favicons_enabled else 0 }}, reverseDnsEnabled: {{ 1 if prefs and prefs.reverse_dns_enabled else 0 }}, automationToastsEnabled: {{ 1 if not prefs or prefs.automation_toasts_enabled else 0 }}, smartQueueToastsEnabled: {{ 1 if not prefs or prefs.smart_queue_toasts_enabled else 0 }}, diskMonitorPaths: {{ (prefs.disk_monitor_paths_json or "[]") | safe }}, diskMonitorMode: {{ (prefs.disk_monitor_mode if prefs and prefs.disk_monitor_mode else "default") | tojson }}, diskMonitorSelectedPath: {{ (prefs.disk_monitor_selected_path if prefs and prefs.disk_monitor_selected_path else "") | tojson }}, bootstrapTheme: {{ (prefs.bootstrap_theme if prefs and prefs.bootstrap_theme else 'default') | tojson }}, fontFamily: {{ (prefs.font_family if prefs and prefs.font_family else 'default') | tojson }}, footerItems: {{ (prefs.footer_items_json or '{}') | safe }}, bootstrapThemes: {{ bootstrap_themes | tojson }}, bootstrapThemeUrls: { {% for key in bootstrap_themes.keys() %}{{ key | tojson }}: {{ bootstrap_theme_url(key) | tojson }}{% if not loop.last %}, {% endif %}{% endfor %} }, fontFamilies: {{ font_families | tojson }}};</script>
<!-- Rollback: uncomment the legacy include below and comment the module include. --> <!-- Rollback: uncomment the legacy include below and comment the module include. -->
<!-- <script src="{{ static_url('app.js') }}"></script> --> <!-- <script src="{{ static_url('app.js') }}"></script> -->
<script type="module" src="{{ static_url('js/app.js') }}"></script> <script type="module" src="{{ static_url('js/app.js') }}"></script>