From 09a6745b31b57ab4b574af11cc53f6a7fb2fc853 Mon Sep 17 00:00:00 2001 From: Volod Date: Tue, 14 Jul 2026 15:39:40 -0700 Subject: [PATCH 1/2] fix(spotify): take profile identity from the session, not spclient (v1.1.0) `/user-profile-view/v3/profile/me` on spclient.wg.spotify.com is NOT session-bound: called with a token that is not tied to a user it still returns 200, with `uri: "spotify:user:me"` and a fixed stranger's name/avatar/follower counts (reproduced with a fresh anonymous token - every caller gets the same foreign profile). The connector read display_name/avatar/counts from there while taking id/uri from the session GraphQL query, so every user's export was stamped with that stranger's identity over their own id and their own collected data. Identity now comes only from the session-bound GraphQL `me` (profileAttributes). spclient is used strictly as enrichment (avatar + counts, which GraphQL does not expose) and only when its `uri` matches the resolved identity; the "spotify:user:me" sentinel is rejected. If the session profile cannot be resolved the run fails clearly instead of silently importing someone else. Claude-Session: https://claude.ai/code/session_01DYpJrHgZxFcFW9apg9uPaa --- .../spotify-playwright-1.1.0.tgz | Bin 0 -> 11899 bytes connector-index.json | 34 ++++++++++ connectors/spotify/spotify-playwright.js | 63 ++++++++++++++---- connectors/spotify/spotify-playwright.json | 2 +- registry.json | 2 +- 5 files changed, 85 insertions(+), 16 deletions(-) create mode 100644 artifacts/spotify-playwright/spotify-playwright-1.1.0.tgz diff --git a/artifacts/spotify-playwright/spotify-playwright-1.1.0.tgz b/artifacts/spotify-playwright/spotify-playwright-1.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..f0d867e58b6ed68f6dc4ccf65a843e64e96a6b68 GIT binary patch literal 11899 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 literal 0 HcmV?d00001 diff --git a/connector-index.json b/connector-index.json index efbf5aa..c401755 100644 --- a/connector-index.json +++ b/connector-index.json @@ -779,6 +779,40 @@ "iconKey": "spotify", "defaultScope": "spotify.savedTracks" } + }, + { + "connectorId": "spotify-playwright", + "company": "spotify", + "version": "1.1.0", + "name": "Spotify", + "status": "stable", + "description": "Exports your Spotify library, playlists, listening history, and preferences.", + "sourceFiles": { + "script": "spotify/spotify-playwright.js", + "metadata": "spotify/spotify-playwright.json" + }, + "publishedAt": "2026-07-13T00:00:00Z", + "sourceTag": "volod/spotify-profile-identity", + "sourceCommit": "7945418ce1f3fc60a3a64769ac4dfd61c11d4132", + "releaseId": "connectors-7945418ce1f3", + "pageApiVersion": 1, + "manifestSha256": "sha256:e5f5df94f724a2417b66b52edd0a4f12a3278b2566c9aaf5a2a69b31f80cbb2a", + "scriptSha256": "sha256:3851320fb0020fb8bbd5d79e4cea150d579ee2f463bf6f45c41a84f08cc7d5db", + "artifactSha256": "sha256:8bed73a63f5ab6009077fc850e50b59fd1326effa260c4c3311c87e3cf48c9a9", + "artifactPath": "artifacts/spotify-playwright/spotify-playwright-1.1.0.tgz", + "artifactUrl": "https://raw.githubusercontent.com/vana-com/data-connectors/7945418ce1f3fc60a3a64769ac4dfd61c11d4132/artifacts/spotify-playwright/spotify-playwright-1.1.0.tgz", + "scopes": [ + "spotify.profile", + "spotify.savedTracks", + "spotify.playlists" + ], + "consumerMetadata": { + "sourceId": "spotify", + "displayName": "Spotify", + "brandDomain": "spotify.com", + "iconKey": "spotify", + "defaultScope": "spotify.savedTracks" + } } ], "steam-playwright": [ diff --git a/connectors/spotify/spotify-playwright.js b/connectors/spotify/spotify-playwright.js index 51e0515..f7c0086 100644 --- a/connectors/spotify/spotify-playwright.js +++ b/connectors/spotify/spotify-playwright.js @@ -554,28 +554,63 @@ const spClientFetch = async (path) => { message: 'Loading profile data...', }); - // Use spclient for profile (always works, returns JSON directly) - const profileData = await spClientFetch('/user-profile-view/v3/profile/me'); - - // Also get profileAttributes via GraphQL for username/uri + // IDENTITY comes from the session-bound GraphQL `me` query, never from + // spclient. `/user-profile-view/v3/profile/me` is NOT session-bound: called + // with a token that is not tied to a user it still answers 200, with + // `uri: "spotify:user:me"` and some unrelated person's name/avatar/counts + // (reproduced with a fresh anonymous token: it returns a fixed foreign + // profile). Reading display_name from it stamped that stranger's identity + // onto every user's export while the ids and the collected data were their + // own. const profileAttrs = await gqlFetch('profileAttributes', {}); const pa = profileAttrs?.data?.me?.profile; - if (!profileData && !pa) { - await page.setData('error', 'Could not fetch profile. Token may be invalid.'); - return { error: 'Could not fetch profile' }; + if (!pa?.username) { + await page.setData( + 'error', + 'Could not confirm which Spotify account is signed in. Please sign in again.' + ); + return { error: 'Could not resolve the signed-in Spotify profile' }; + } + + const identityUri = pa.uri || 'spotify:user:' + pa.username; + + // spclient is enrichment ONLY (avatar + counts, which GraphQL does not + // expose), and only when it demonstrably describes the SAME account. The + // literal "spotify:user:me" uri is the not-a-real-user marker. + const profileData = await spClientFetch('/user-profile-view/v3/profile/me'); + const enrichment = + profileData && + profileData.uri && + profileData.uri !== 'spotify:user:me' && + profileData.uri === identityUri + ? profileData + : null; + + if (profileData && !enrichment) { + await page.setData( + 'status', + 'Profile details unavailable for this account; continuing with the signed-in identity.' + ); } + // Avatar: prefer the session-bound GraphQL field when Spotify exposes it, + // and only fall back to the (verified-same-account) spclient image. + const avatar = pa.imageUrl || enrichment?.image_url || null; + state.profile = { - id: pa?.username || '', - display_name: profileData?.name || pa?.name || '', - uri: pa?.uri || profileData?.uri || '', - followers: profileData?.followers_count || 0, - following: profileData?.following_count || 0, - images: profileData?.image_url ? [profileData.image_url] : [], + id: pa.username, + display_name: pa.name || '', + uri: identityUri, + followers: enrichment?.followers_count || 0, + following: enrichment?.following_count || 0, + images: avatar ? [avatar] : [], }; - await page.setData('status', 'Logged in as ' + state.profile.display_name); + await page.setData( + 'status', + 'Logged in as ' + (state.profile.display_name || state.profile.id) + ); // Step 2: Liked Songs (paginated via GraphQL) await page.setProgress({ diff --git a/connectors/spotify/spotify-playwright.json b/connectors/spotify/spotify-playwright.json index d1a5e00..bfd8c3a 100644 --- a/connectors/spotify/spotify-playwright.json +++ b/connectors/spotify/spotify-playwright.json @@ -2,7 +2,7 @@ "manifest_version": "1.0", "connector_id": "spotify-playwright", "source_id": "spotify", - "version": "1.0.0", + "version": "1.1.0", "name": "Spotify", "company": "Spotify", "description": "Exports your Spotify library, playlists, listening history, and preferences.", diff --git a/registry.json b/registry.json index 27fdab1..33d289e 100644 --- a/registry.json +++ b/registry.json @@ -127,7 +127,7 @@ { "id": "spotify-playwright", "company": "spotify", - "version": "1.0.0", + "version": "1.1.0", "name": "Spotify", "status": "stable", "description": "Exports your Spotify library, playlists, listening history, and preferences.", From c2c68ccc090e86d8a8dd477bec69454c8d25eda6 Mon Sep 17 00:00:00 2001 From: Volod Date: Tue, 14 Jul 2026 17:10:02 -0700 Subject: [PATCH 2/2] fix(spotify): fetch profile-view by resolved username, not literal "me" Refines the enrichment call after confirming the exact root cause (matches the interrogator report): the trailing path segment of /user-profile-view/v3/profile/ is a literal vanity username, not a self-alias, and a real user owns the username "me" (open.spotify.com/user/me => "Micael Widell"). Passing "me" returned that stranger's avatar/counts for everyone. Now the profile-view is fetched for the session-resolved username (pa.username), so avatar + follower/following counts are the user's own again instead of being dropped. The uri-equality guard stays as defense-in-depth. Claude-Session: https://claude.ai/code/session_01DYpJrHgZxFcFW9apg9uPaa --- .../spotify-playwright-1.1.0.tgz | Bin 11899 -> 11924 bytes connector-index.json | 10 +++--- connectors/spotify/spotify-playwright.js | 31 ++++++++---------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/artifacts/spotify-playwright/spotify-playwright-1.1.0.tgz b/artifacts/spotify-playwright/spotify-playwright-1.1.0.tgz index f0d867e58b6ed68f6dc4ccf65a843e64e96a6b68..26b28a54a1fa9d5a09437053c80d261b96630201 100644 GIT binary patch literal 11924 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 Wu9g3%2+Y3t0e^$o;R3k@0r?*|=pb_d delta 11883 zcmb8z<60#E0)XLc+qP}noow5jTqjSqZQGcv$u-%w?b?0z4)*)^-NKt<@SzVRj)npG zU*K2%7d{7V^UlBzx&~W(6`jasoi?sG;j!+GEf3`zpX5Ka2Ye|IYcaH{q_lQhvH9ic zw_m58AvdXh?BIbAqN+(RDSv8L-5HbmjahJ}V8Q3MzqY9RvYZip1mEG)-`aOQ`#0gtksyVnQ8*6ltUcRvK_ct6%e!`F|aadpf> zKKMKpUykkepBq<7!u;R0n%G2*vusp9dj~(52t2<051*$6uDD|ld)L>loh|8v{4mTO z8!QCffTF=VgWgXnFJFL76V(N$-tCX&(FNgXZx;U@Wj29&Ub`<)vAer{ENiS*bK|;g z9e|dP0QEt*Y}z35D4*ur ziW9Wn`^XbKeU1Ze_zHr+y+?1tRc1MglM z+DO~Fz9Mc9a?S-sF>xZU5*2nvVhnlByT#hm@3#30B|CTy3#t^^A_;C+A_XYG-=&n6tYAO(GWK!jT#!BZu> zJ||CKhqm9^+=T+WVO}7O(1ON!%OD*ETY^L+^kCby0V_oPG&i6x$j*2m$jDj#P*Wg( zpq{$tlzSo*ND%%rl7$-rt%gYAX*& zbbU^O0X(PN^xZ}`eqpL77r91yeBU2N&(~9TTeR_}ALTT+!m{`mkHGc?u18?=&ZXni z&q>F25L`VC7Ej-?$stsL#b3IDFS=iTu*bl{*)bQCyl!+qQpS9?1DY4+nLohF+2!9vc0Z ztAE?E*ZHmSq_8N>Pk3>7`AhbjwFO@^NUg){k}!D!f&hqilX;;W;(R=3M(3kv6e4n{ z|1h3ChifEhA>(z8na`3SjjcipMh#=Bl@bGsw@bz&<~kVuN^z+gt|$tcj8g^SqUATf z1=y!>2?Wj#)70gNCrS7Ef7!c*^SyxtBq63YV3g}T^Bf8@8&}Phs8FVL)qT{8poPBKdUGIJE}Wp_+o ze$!IbjNVeGLBcSpWpv>)|9YJdCP!c}kt0kS`b_`nuEgDN+(K8D57$V{xP7JC)8IvI zt&$h|*Hkr~G&vV0dR@K--NX^b(`F#dgOU=nH|S&pg-;U&1$hqjF1E0>&itLp1TAi` z8w0u(;#ti=?jj(Yve0J^tDAb0bI7y2!|uu*pm9{--^-fj9x*yr7O;)B`K944-v_Du z=CDvTTAhvTm8T^mrWM!PK91R7AT!^WRPJzM> zT+g%DKer)_0iUF10$ECP1};Cz*f9jN-6rrmkY;k?6QE*>iR4?dxh@}rmKx>^CuHuH zc;uRs4IX$uB7R3Zqig9<`(0WdS~be1RuDPJ2fR8l2T%o(aVde?p-xMdvo2|`pe$1@ z@?(90-fT!AG^V5tG3a*@jG}B1(HXq99Q|JTodDboa~!$tn&OkrxL#p1!tCTkXgpvx ztKt2S?e0$T=uW{H;>Tsc)!eB|kWtn}7ySN9@b6N8;Pq$8Q+63vxlm)Vmhy;zythVF z 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 V|3BxaZRH0Vx { message: 'Loading profile data...', }); - // IDENTITY comes from the session-bound GraphQL `me` query, never from - // spclient. `/user-profile-view/v3/profile/me` is NOT session-bound: called - // with a token that is not tied to a user it still answers 200, with - // `uri: "spotify:user:me"` and some unrelated person's name/avatar/counts - // (reproduced with a fresh anonymous token: it returns a fixed foreign - // profile). Reading display_name from it stamped that stranger's identity - // onto every user's export while the ids and the collected data were their - // own. + // IDENTITY comes from the session-bound GraphQL `me` query, never from the + // profile-view path. `spClientFetch('/user-profile-view/v3/profile/me')` is + // NOT a self-alias: the trailing segment is a literal vanity username, and a + // real Spotify user grabbed the username "me" (open.spotify.com/user/me => + // "Micael Widell"). So "me" resolved to that stranger's public profile for + // every user, stamping his name/avatar/counts onto their spotify.profile + // while the ids and collected data stayed their own. const profileAttrs = await gqlFetch('profileAttributes', {}); const pa = profileAttrs?.data?.me?.profile; @@ -576,16 +575,14 @@ const spClientFetch = async (path) => { const identityUri = pa.uri || 'spotify:user:' + pa.username; // spclient is enrichment ONLY (avatar + counts, which GraphQL does not - // expose), and only when it demonstrably describes the SAME account. The - // literal "spotify:user:me" uri is the not-a-real-user marker. - const profileData = await spClientFetch('/user-profile-view/v3/profile/me'); + // expose). Fetch the profile-view for the RESOLVED username — never the + // literal "me" — and still verify it describes the same account before + // trusting it, in case the username itself is ever ambiguous. + const profileData = await spClientFetch( + '/user-profile-view/v3/profile/' + encodeURIComponent(pa.username) + ); const enrichment = - profileData && - profileData.uri && - profileData.uri !== 'spotify:user:me' && - profileData.uri === identityUri - ? profileData - : null; + profileData && profileData.uri === identityUri ? profileData : null; if (profileData && !enrichment) { await page.setData(