Skip to content

Fix Spotify playback authorization - #16

Merged
rianjs merged 2 commits into
mainfrom
fix/spotify-playback-authorization
Aug 11, 2026
Merged

Fix Spotify playback authorization#16
rianjs merged 2 commits into
mainfrom
fix/spotify-playback-authorization

Conversation

@rianjs

@rianjs rianjs commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Closes #15

What changed

  • separates Spotify Web API tokens from reusable librespot playback credentials
  • adds a one-time playback authorization flow using librespot's built-in client identity, PKCE, the streaming scope, and the /login callback
  • stops playback before queue commit/advance when authorization is missing or rejected
  • preserves local-file playback and genuine per-track unavailable behavior
  • shows a descriptive modal, retains the intended track, and retries it exactly once after authorization
  • logs playback phase, URI, librespot error kind, and full underlying error without credential material
  • documents the August 2026 Spotify/login5 compatibility change and the research behind the fix

Root cause

Spotify Web API OAuth still permitted library sync and search, but Spotify's login5 playback path began rejecting credentials minted under Retune's Web API client identity. The prior implementation reused one OAuth credential for two independent contracts, so browsing remained healthy while every audio load failed with FailedPrecondition / INVALID_CREDENTIALS.

Validation

  • cargo test --workspace
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo fmt --all -- --check
  • frontend tests, TypeScript check, lint, and production build
  • live macOS validation in light and dark themes:
    • missing auth stopped before queue advancement
    • Cancel left playback stopped
    • OAuth authorization preserved library access
    • the intended first track resumed exactly once and produced audio
    • unrelated settings remained saveable while playback auth was missing

@rianjs

rianjs commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

TDD assessment — PR #16

Blocker

None.

Major

  • Rejected and transient authorization are not tested at the controller boundary. playback/mod.rs:1841-1893 proves only the missing-credential start/advance paths. The rejected/transient tests stop at local.rs:565-624, directly calling session_error; they never exercise step_locked (playback/mod.rs:1092-1100) or start with a semantic login5 failure. A regression that advances/commits before handling a rejected credential, or turns a transient failure into an authorization prompt, would pass. Add controller tests for rejected start/advance retaining the original snapshot/index, and transient failure retaining credentials without authorization handling.

  • The Cancel/Escape and exactly-once retry journey has no behavioral test. The retry and cancellation state live in App.tsx:346-357, while the modal delegates Escape/Cancel through dialogViews.tsx:165-170. test/ui.test.ts:135-148 tests only pure predicates, not usePlayer's pending reference or calls to start. It would remain green if Cancel left a pending target or a successful authorization invoked play_tracks twice. Add a small hook/component-level test that drives prompt → Cancel/Escape and prompt → authorized connection change, asserting zero and one retry respectively for the original target.

  • Durable playback credentials are not covered through the stores and Web reconnect path. The encrypted-store test at tokens.rs:510-537 saves a token with playback_credentials: None; the base64 test at tokens.rs:441-454 bypasses the store. The reconnect preservation added in spotify_commands.rs:58-71 has no test, and the new authorization command is covered only by the field-validation helper at spotify_commands.rs:451-465. A change that drops the reusable credential during encrypted persistence or a normal Web OAuth reconnect would pass. Add a round trip through EncryptedFsTokenStore with credentials and a reconnect-token replacement test preserving them.

  • The no-secret diagnostic requirement has no regression check. local.rs:327-329 logs the complete librespot error with {:?}, and the new authentication paths surface error.to_string() (spotify_commands.rs:116-123). No test captures diagnostics with sentinel access/auth values and asserts they are absent. Add one logging/error-boundary test so future dependency/error-format changes cannot leak reusable credentials.

Minor

  • Offline-safe preference saves are untested. The changed behavior is in lib.rs:679-703, but no test invokes this branch with local playback selected and an unavailable Spotify client/token store. The current code is straightforward, so this is lower risk, but a focused command/state test should prove the setting persists without attempting activation or emitting an operation error.

Nit

None.

@rianjs
rianjs marked this pull request as ready for review August 11, 2026 12:36
@rianjs
rianjs merged commit 3f71227 into main Aug 11, 2026
4 checks passed
@rianjs
rianjs deleted the fix/spotify-playback-authorization branch August 11, 2026 12:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix Spotify playback authorization after login5 credential enforcement

1 participant