From 0a3cca4f1fddd77f83c2029e5c55770923b60d23 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Thu, 19 Mar 2026 08:02:12 -0700 Subject: [PATCH] Fix Spotify invalid_client and Tidal redirect URI mismatch on auth - Duplicate `spotify` key in saveSettings() object literal caused second definition (embed_tags/tags) to silently overwrite the first (client_id/client_secret/redirect_uri), destroying credentials on every save. Merged into single key. - authenticateSpotify() and authenticateTidal() now await saveSettings() before opening auth window, ensuring credentials are persisted. - Tidal auth now dynamically sets redirect_uri from request host for LAN/Docker users and stores it in tidal_oauth_state so the callback token exchange uses the same URI. Fixes #191 --- web_server.py | 17 +++++++++++++++-- webui/static/script.js | 18 +++++++++--------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/web_server.py b/web_server.py index ded5a149..0f5ad099 100644 --- a/web_server.py +++ b/web_server.py @@ -5858,8 +5858,19 @@ def auth_tidal(): tidal_oauth_state["code_verifier"] = temp_tidal_client.code_verifier tidal_oauth_state["code_challenge"] = temp_tidal_client.code_challenge + # Dynamically set redirect_uri based on how the user is accessing the app + request_host = request.host.split(':')[0] + if request_host not in ('127.0.0.1', 'localhost'): + dynamic_redirect = f"http://{request_host}:8889/tidal/callback" + temp_tidal_client.redirect_uri = dynamic_redirect + print(f"🔗 Tidal redirect_uri updated for remote access: {dynamic_redirect}") + + # Store PKCE + redirect_uri for callback to use the same values + with tidal_oauth_lock: + tidal_oauth_state["redirect_uri"] = temp_tidal_client.redirect_uri + print(f"🔐 Stored PKCE - verifier: {temp_tidal_client.code_verifier[:20]}... challenge: {temp_tidal_client.code_challenge[:20]}...") - + # Create OAuth URL import urllib.parse params = { @@ -6083,10 +6094,12 @@ def tidal_callback(): # Create a temporary client for the token exchange temp_tidal_client = TidalClient() - # Restore PKCE values from the auth request (matches HTTPServer handler logic) + # Restore PKCE values and redirect_uri from the auth request with tidal_oauth_lock: temp_tidal_client.code_verifier = tidal_oauth_state["code_verifier"] temp_tidal_client.code_challenge = tidal_oauth_state["code_challenge"] + if "redirect_uri" in tidal_oauth_state: + temp_tidal_client.redirect_uri = tidal_oauth_state["redirect_uri"] success = temp_tidal_client.fetch_token_from_code(auth_code) diff --git a/webui/static/script.js b/webui/static/script.js index ae4372aa..8991af5b 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -5708,7 +5708,9 @@ async function saveSettings(quiet = false) { spotify: { client_id: document.getElementById('spotify-client-id').value, client_secret: document.getElementById('spotify-client-secret').value, - redirect_uri: document.getElementById('spotify-redirect-uri').value + redirect_uri: document.getElementById('spotify-redirect-uri').value, + embed_tags: document.getElementById('embed-spotify').checked, + tags: _collectServiceTags('spotify') }, tidal: { client_id: document.getElementById('tidal-client-id').value, @@ -5795,10 +5797,6 @@ async function saveSettings(quiet = false) { genre_merge: _getTagConfig('metadata_enhancement.tags.genre_merge') } }, - spotify: { - embed_tags: document.getElementById('embed-spotify').checked, - tags: _collectServiceTags('spotify') - }, musicbrainz: { embed_tags: document.getElementById('embed-musicbrainz').checked, tags: _collectServiceTags('musicbrainz') @@ -6238,7 +6236,9 @@ function updateStatusDisplays() { async function authenticateSpotify() { try { - showLoadingOverlay('Starting Spotify authentication...'); + showLoadingOverlay('Saving credentials and starting Spotify authentication...'); + // Save settings first to ensure client_id/client_secret are persisted + await saveSettings(); showToast('Spotify authentication started', 'success'); window.open('/auth/spotify', '_blank'); } catch (error) { @@ -6387,10 +6387,10 @@ function formatRateLimitDuration(seconds) { async function authenticateTidal() { try { - showLoadingOverlay('Starting Tidal authentication...'); - // This would trigger the OAuth flow + showLoadingOverlay('Saving credentials and starting Tidal authentication...'); + // Save settings first to ensure credentials are persisted + await saveSettings(); showToast('Tidal authentication started', 'success'); - // In a real implementation, this would open the OAuth URL window.open('/auth/tidal', '_blank'); } catch (error) { console.error('Error authenticating Tidal:', error);