Summary
spotify-pp-cli auth login fails at the token-exchange step with HTTP 400: invalid_request, Invalid client secret when a user follows the documented PKCE flow (set only SPOTIFY_CLIENT_ID, no secret). The README and SKILL.md both state PKCE is the default in that configuration, but PKCE is not implemented in the code — so a CLIENT_ID-only setup can never complete login. It's a hard onboarding blocker for any new user who follows the docs.
What the docs promise
README.md (~L126): "OAuth flows are auto-detected: PKCE is the default when only SPOTIFY_CLIENT_ID is set, and Authorization Code with secret is used when SPOTIFY_SECRET is also present…"
SKILL.md Auth Setup, Step 3 (~L359): "…only needed if you want to use the Authorization Code flow with a secret. If you skip it, the CLI uses PKCE, which is simpler and recommended for desktop use."
Reproduction
- Create a Spotify app, register redirect URI
http://127.0.0.1:8085/callback.
export SPOTIFY_CLIENT_ID=<id> — do not set any secret (the documented PKCE path).
spotify-pp-cli auth login
- Browser consent succeeds and returns an authorization code; the CLI then fails:
Error: exchanging code for token: HTTP 400: map[error:invalid_request error_description:Invalid client secret]
Root cause
PKCE is never initiated and never used. In internal/cli/auth.go:
- Authorize request (~L140-149) sets only
client_id, redirect_uri, response_type=code, state, scope. There is no code_challenge / code_challenge_method, so the flow is not PKCE.
- Token exchange (~L200-208) sends
grant_type=authorization_code, code, redirect_uri, client_id, and adds client_secret only if non-empty. There is no code_verifier.
A grep over the repo confirms code_challenge / code_verifier / pkce appear nowhere in the source.
Result: with no secret, the token request is a bare authorization_code grant with neither client authentication nor a PKCE verifier, which Spotify's token endpoint rejects as Invalid client secret. The flow only works if a client secret is supplied (SPOTIFY_SECRET / SPOTIFY_CLIENT_SECRET), i.e. plain Authorization Code — the opposite of what the docs say is the default.
Expected
Either of:
- (A) Implement PKCE — generate a
code_verifier, send code_challenge + code_challenge_method=S256 on the authorize request, and code_verifier on the token exchange when no secret is configured. This honors the documented "CLIENT_ID-only" path and is the recommended public-client flow for a desktop CLI.
- (B) Fix the docs — if PKCE is out of scope, make the client secret required in README.md + SKILL.md and remove the "PKCE is the default / secret optional" guidance, so users set
SPOTIFY_SECRET from the start. (Lower-effort stopgap.)
(A) is preferable — Spotify's loopback/native-app guidance pushes PKCE, and it avoids shipping a long-lived secret on every user's machine.
Workaround (for anyone hitting this now)
Set the client secret and re-run login:
export SPOTIFY_CLIENT_SECRET=<secret> # or SPOTIFY_SECRET
spotify-pp-cli auth login
Environment
- CLI:
spotify-pp-cli (library/media-and-entertainment/spotify), version 2026.6.1
- Auth verified working once a secret is supplied (live
me get-current-users-profile returns a real profile).
Summary
spotify-pp-cli auth loginfails at the token-exchange step withHTTP 400: invalid_request, Invalid client secretwhen a user follows the documented PKCE flow (set onlySPOTIFY_CLIENT_ID, no secret). The README and SKILL.md both state PKCE is the default in that configuration, but PKCE is not implemented in the code — so a CLIENT_ID-only setup can never complete login. It's a hard onboarding blocker for any new user who follows the docs.What the docs promise
README.md(~L126): "OAuth flows are auto-detected: PKCE is the default when onlySPOTIFY_CLIENT_IDis set, and Authorization Code with secret is used whenSPOTIFY_SECRETis also present…"SKILL.mdAuth Setup, Step 3 (~L359): "…only needed if you want to use the Authorization Code flow with a secret. If you skip it, the CLI uses PKCE, which is simpler and recommended for desktop use."Reproduction
http://127.0.0.1:8085/callback.export SPOTIFY_CLIENT_ID=<id>— do not set any secret (the documented PKCE path).spotify-pp-cli auth loginRoot cause
PKCE is never initiated and never used. In
internal/cli/auth.go:client_id,redirect_uri,response_type=code,state,scope. There is nocode_challenge/code_challenge_method, so the flow is not PKCE.grant_type=authorization_code,code,redirect_uri,client_id, and addsclient_secretonly if non-empty. There is nocode_verifier.A
grepover the repo confirmscode_challenge/code_verifier/pkceappear nowhere in the source.Result: with no secret, the token request is a bare
authorization_codegrant with neither client authentication nor a PKCE verifier, which Spotify's token endpoint rejects asInvalid client secret. The flow only works if a client secret is supplied (SPOTIFY_SECRET/SPOTIFY_CLIENT_SECRET), i.e. plain Authorization Code — the opposite of what the docs say is the default.Expected
Either of:
code_verifier, sendcode_challenge+code_challenge_method=S256on the authorize request, andcode_verifieron the token exchange when no secret is configured. This honors the documented "CLIENT_ID-only" path and is the recommended public-client flow for a desktop CLI.SPOTIFY_SECRETfrom the start. (Lower-effort stopgap.)(A) is preferable — Spotify's loopback/native-app guidance pushes PKCE, and it avoids shipping a long-lived secret on every user's machine.
Workaround (for anyone hitting this now)
Set the client secret and re-run login:
Environment
spotify-pp-cli(library/media-and-entertainment/spotify), version2026.6.1me get-current-users-profilereturns a real profile).