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
This commit is contained in:
Broque Thomas 2026-03-19 08:02:12 -07:00
parent 9a6ee0e229
commit 0a3cca4f1f
2 changed files with 24 additions and 11 deletions

View file

@ -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)

View file

@ -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);