From f53146af3eda0ea7e9f7d9ce47156cdcc6f4e8b5 Mon Sep 17 00:00:00 2001 From: TheAchiever6823 <61914223+ShawnMadadha@users.noreply.github.com> Date: Mon, 18 May 2026 15:04:08 -0700 Subject: [PATCH] =?UTF-8?q?[shawn]=20fix:=20refuse=20Spotify=20token=20if?= =?UTF-8?q?=20scopes=20missing=20=E2=80=94=20show=20exact=20missing=20list?= =?UTF-8?q?=20inline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/apps/tools_lib/tools_lib.py | 41 ++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/backend/apps/tools_lib/tools_lib.py b/backend/apps/tools_lib/tools_lib.py index 7113aa32..3782e6fd 100644 --- a/backend/apps/tools_lib/tools_lib.py +++ b/backend/apps/tools_lib/tools_lib.py @@ -1752,15 +1752,36 @@ async def spotify_callback(code: str = "", state: str = "", error: str = "") -> if not refresh_token: return HTMLResponse("
Retry from OpenSwarm.
", status_code=400) - # Log what Spotify ACTUALLY granted vs what we requested. If a user - # complains about "scope not granted" later, this is the first place - # to check — the granted set should equal _SPOTIFY_SCOPES. + # Spotify returns the actual granted scopes in the token response. + # Compare against what we asked for and bail HARD if any are missing — + # otherwise the user thinks they're connected but every playlist / + # library mutation will 403 later with a cryptic error. granted_scopes = data.get("scope", "") requested_set = set(_SPOTIFY_SCOPES.split()) granted_set = set(granted_scopes.split()) missing = requested_set - granted_set if missing: - logger.warning(f"spotify: Spotify granted fewer scopes than requested. Missing: {missing}") + logger.warning(f"spotify: Spotify granted fewer scopes than requested. Missing: {sorted(missing)}") + missing_pretty = ", ".join(sorted(missing)) + return HTMLResponse( + "" + "Spotify only granted {len(granted_set)} of {len(requested_set)} scopes. " + f"Missing:
" + f"{html.escape(missing_pretty)}"
+ "Your token was NOT saved. The Spotify tile in OpenSwarm is still disconnected.
" + "You can close this tab and return to OpenSwarm.
" + f"All {len(granted_set)} permissions granted, including playlist write access.
" + "You can close this tab and return to OpenSwarm.
" "