spotify-cli / Docs
Authentication
Two separate sign-ins, for two different things.
IAM login (spotify login) |
Spotify sign-in (spotify auth login) |
|
|---|---|---|
| Who you are in IAM | ✓ | |
| Needed for | triggers (Ting notifications) | Web API features: search, lyrics, playlists, library, devices, queue reads, repeat-one, likes |
| Stored in | $SILICON_HOME/.spotify/session.json |
spotify_player's cache (~/.cache/spotify-player) |
| Per | Silicon home | Mac user |
Playback control through AppleScript needs neither.
IAM login (Silicons and Carbons)
The CLI never asks for passwords, OTPs, SID or STK. It takes one short-lived token (SLT) minted by
the official iam CLI or IAM's consent screen:
spotify iam --json # {"app_id": "spotify", "org_id": "unlikefraction", …} — offline
# Silicon
iam silicon-login --app-id spotify --grant-org "$SILICON_ORG" --approve-scopes
# Carbon
iam login --app-id spotify --grant-org <org>
spotify login '<SLT>' # or: … | jq -r .slt | spotify login --token-file -
spotify login status --json
spotify logout
What happens:
- The CLI sends the SLT to the backend (
POST /api/v1/auth/login) with an idempotency key derived from the SLT, so a retry after a network error replays instead of burning the single-use token. - The backend exchanges it with IAM using the application secret (which never leaves the server), validates the session against IAM's authorization snapshot and returns the app session.
- The backend registers you as a Ting recipient for this app (
subscriptions.register), so triggers can notify you.spotify ting registerrepeats that step explicitly. - The CLI saves the session (0600) under
$SILICON_HOME/.spotify/.
Scopes requested: self.identity.read (Ting checks your public id), self.profile.read,
obo:ting:subscriptions.register, obo:ting:tings.send. Refresh never adds scopes: if a login
predates a scope, log in again (reconsent_required).
spotify login status --json checks live: it refreshes a near-expiry token (rotation-safe:
the idempotency key is derived from the refresh token, and the refresh runs under an exclusive
lock shared with the daemon) and asks the backend GET /api/v1/auth/me. No saved session or a
revoked session prints {"authenticated": false, …} with exit 0; network failures are errors
(exit 5) so outages are never mistaken for logouts.
Organization: --org, then SILICON_ORG, then config org, then the session's organization.
Spotify sign-in (spotify_player)
spotify auth status
spotify auth login # opens Spotify's consent page in the browser on this Mac
A Carbon clicks Agree once; spotify_player caches and refreshes the tokens. If commands fail
with spotify_auth_required, run it again. Bring your own Spotify client id: see
spotify docs config.
Testing planes
IAM testing environments are isolated planes that use the same code paths as production. Select one with the spotify-cli test application secret of that environment:
printf %s "$SPOTIFY_TEST_APP_SECRET" | spotify testing use --app-secret-file -
spotify login si:test-silicon # in a testing plane an SLT may be a test public id
spotify trigger test
spotify testing exit
SPOTIFY_TEST_APP_SECRET selects a plane for one process. Test and production sessions are stored
in separate slots and never fall back to each other. The backend validates the secret with IAM
(X-Testing-Environment-Key → with_testing_application) and forwards only Ting's test
credentials (from the OBO proof's testing context) to Ting.
Offline: spotify docs auth · Source: docs/auth.md