1
Generate PKCE values and state
Create a Store
code_verifier, derive its S256 challenge, and generate an unguessable state.codeVerifier and state server-side, bound to the user’s session — for example in a signed, httpOnly cookie with a short TTL. Never put the verifier in the browser or in a URL.2
Redirect the user to the authorization endpoint
302) to the Soundlink consent screen. This endpoint sends Cache-Control: no-store.3
The user approves, and Soundlink returns to your callback
state against the value you stored before doing anything else, and reject the request if it does not match. If the user declines, you receive ?error=access_denied&state=… instead — treat this as a normal outcome, not a failure.4
Exchange the code for a token
The request is
application/x-www-form-urlencoded. This grant authenticates with the PKCE verifier and sends no client_secret.200 Response
redirect_uri must be identical to the one used in the previous step. Authorization codes are single-use.5
Record the organization id
organization_id is a claim on the access token, so no extra request is needed to read it.This is the one value you must persist. Soundlink provides no endpoint that lists which organizations have authorized your client, so an organization whose id you have not stored is one you can no longer act for. Keep it in your own database, keyed to your own user or tenant, along with grant_id if you intend to support disconnecting.Access token claims
string
The user’s id for
authorization_code tokens; your client id for client_credentials tokens — those have no user.Userinfo
GET /api/v1/oauth/userinfo requires a token from the authorization_code grant with the openid scope. A Client Credentials token is rejected with 403 access_denied regardless of its scopes, because it represents an application rather than a user.cURL
200 Response
email is present only when the token carries the email scope. Responses send Cache-Control: no-store.
Failures return { "error": "…" } with a WWW-Authenticate header:
Send exactly one
Authorization header — two are rejected rather than merged.