Skip to content

Authorization Code Flow

The authorization code flow is suitable for long-running applications (e.g. web and mobile apps) where the user grants permission only once.

If you’re using the authorization code flow in a mobile app, or any other type of application where the client secret can't be safely stored, then you should use the PKCE extension. Keep reading to learn how to correctly implement it.

The following diagram shows how the authorization code flow works:

Authorization Code Flow

Pre-requisites

This guide assumes that you have created an app following the app guide.

Source Code

You can find an example app implementing Authorization Code flow on GitHub in the web-api-examples repository.

Request User Authorization

The first step is to request authorization from the user, so our app can access to the Spotify resources in behalf that user. To do so, our application must build and send a GET request to the /authorize endpoint with the following parameters:

Query ParameterValue
client_idRequired The Client ID generated after registering your application.
response_typeRequired Set to code.
redirect_uriRequired The URI to redirect to after the user grants or denies permission. This URI needs to have been entered in the Redirect URI allowlist that you specified when you registered your application (See the app guide). The value of redirect_uri here must exactly match one of the values you entered when you registered your application, including upper or lowercase, terminating slashes, and such.
stateOptional, but strongly recommended This provides protection against attacks such as cross-site request forgery. See RFC-6749.
scopeOptional A space-separated list of scopes.If no scopes are specified, authorization will be granted only to access publicly available information: that is, only information normally visible in the Spotify desktop, web, and mobile players.
show_dialogOptional Whether or not to force the user to approve the app again if they’ve already done so. If false (default), a user who has already approved the application may be automatically redirected to the URI specified by redirect_uri. If true, the user will not be automatically redirected and will have to approve the app again.

If you are implementing the PKCE extension, you must include these additional parameters:

Query ParameterValue
code_challenge_methodRequired. Set to S256.
code_challengeRequired. Set to the code challenge that your app calculated in step 1.

In order to generate the code_challenge, your app should hash the code verifier using the SHA256 algorithm. The code verifier is a random string between 43 and 128 characters in length. It can contain letters, digits, underscores, periods, hyphens, or tildes.

Example

The following JavaScript code example implements the /login method using Express framework to initiates the authorization request:


_19
var client_id = 'CLIENT_ID';
_19
var redirect_uri = 'http://localhost:8888/callback';
_19
_19
var app = express();
_19
_19
app.get('/login', function(req, res) {
_19
_19
var state = generateRandomString(16);
_19
var scope = 'user-read-private user-read-email';
_19
_19
res.redirect('https://accounts.spotify.com/authorize?' +
_19
querystring.stringify({
_19
response_type: 'code',
_19
client_id: client_id,
_19
scope: scope,
_19
redirect_uri: redirect_uri,
_19
state: state
_19
}));
_19
});

Once the request is processed, the user will see the authorization dialog asking to authorize access within the user-read-private and user-read-email scopes.

The Spotify OAuth 2.0 service presents details of the scopes for which access is being sought. If the user is not logged in, they are prompted to do so using their Spotify credentials. When the user is logged in, they are asked to authorize access to the data sets or features defined in the scopes.

Finally, the user is redirected back to your specified redirect_uri. After the user accepts, or denies your request, the Spotify OAuth 2.0 service redirects the user back to your redirect_uri. In this example, the redirect address is https://localhost:8888/callback

Response

If the user accepts your request, then the user is redirected back to the application using the redirect_uri passed on the authorized request described above.

The callback contains two query parameters:

Query ParameterValue
codeAn authorization code that can be exchanged for an Access Token.
stateThe value of the state parameter supplied in the request.

For example:


_10
https://my-domain.com/callback?code=NApCCg..BkWtQ&state=34fFs29kd09

If the user does not accept your request or if an error has occurred, the response query string contains the following parameters:

Query ParameterValue
errorThe reason authorization failed, for example: "access_denied"
stateThe value of the state parameter supplied in the request.

For example:


_10
https://my-domain.com/callback?error=access_denied&state=34fFs29kd09

In both cases, your app should compare the state parameter that it received in the redirection URI with the state parameter it originally provided to Spotify in the authorization URI. If there is a mismatch then your app should reject the request and stop the authentication flow.

Request Access Token

If the user accepted your request, then your app is ready to exchange the authorization code for an Access Token. It can do this by making a POST request to the /api/token endpoint.

The body of this POST request must contain the following parameters encoded in application/x-www-form-urlencoded:

REQUEST BODY PARAMETERVALUE
grant_typeRequired This field must contain the value "authorization_code".
codeRequired The authorization code returned from the previous request.
redirect_uriRequired This parameter is used for validation only (there is no actual redirection). The value of this parameter must exactly match the value of redirect_uri supplied when requesting the authorization code.

If you are implementing the PKCE extension, these additional parameters must be included as well:

REQUEST BODY PARAMETERVALUE
client_idRequired. The client ID for your app, available from the developer dashboard.
code_verifierRequired. The value of this parameter must match the value of the code_verifier that your app generated in the previous step.

The request must include the following HTTP headers:

HEADER PARAMETERVALUE
AuthorizationRequired Base 64 encoded string that contains the client ID and client secret key. The field must have the format: Authorization: Basic <base64 encoded client_id:client_secret>
Content-TypeRequired Set to application/x-www-form-urlencoded.

Example

This step is usually implemented within the callback described on the request of the previous steps. The following example implements the Access Token request inside the callback method:


_25
app.get('/callback', function(req, res) {
_25
_25
var code = req.query.code || null;
_25
var state = req.query.state || null;
_25
_25
if (state === null) {
_25
res.redirect('/#' +
_25
querystring.stringify({
_25
error: 'state_mismatch'
_25
}));
_25
} else {
_25
var authOptions = {
_25
url: 'https://accounts.spotify.com/api/token',
_25
form: {
_25
code: code,
_25
redirect_uri: redirect_uri,
_25
grant_type: 'authorization_code'
_25
},
_25
headers: {
_25
'Authorization': 'Basic ' + (new Buffer.from(client_id + ':' + client_secret).toString('base64'))
_25
},
_25
json: true
_25
};
_25
}
_25
});

Response

On success, the response will have a 200 OK status and the following JSON data in the response body:

KEYVALUE TYPEVALUE DESCRIPTION
access_tokenstringAn Access Token that can be provided in subsequent calls, for example to Spotify Web API services.
token_typestringHow the Access Token may be used: always "Bearer".
scopestringA space-separated list of scopes which have been granted for this access_token
expires_inintThe time period (in seconds) for which the Access Token is valid.
refresh_tokenstringA token that can be sent to the Spotify Accounts service in place of an authorization code. (When the access code expires, send a POST request to the Accounts service /api/token endpoint, but use this code in place of an authorization code. A new Access Token will be returned. A new refresh token might be returned too.)

The following example, shows how the successful response looks like:


_10
{
_10
"access_token": "NgCXRK...MzYjw",
_10
"token_type": "Bearer",
_10
"scope": "user-read-private user-read-email",
_10
"expires_in": 3600,
_10
"refresh_token": "NgAagA...Um_SHo"
_10
}

Request a refreshed Access Token

Access tokens are deliberately set to expire after a short time, after which new tokens may be granted by supplying the refresh token originally obtained during the authorization code exchange.

In order to refresh the token, a POST request must be sent with the following body parameters encoded in application/x-www-form-urlencoded:

REQUEST BODY PARAMETERVALUE
grant_typeRequired Set it to refresh_token.
refresh_tokenRequired The refresh token returned from the authorization code exchange.

If you are implementing the PKCE extension, this additional parameter must be also included:

REQUEST BODY PARAMETERVALUE
client_idRequired. The client ID for your app, available from the developer dashboard.

The headers of this POST request must contain the following parameters, except if you are implementing PKCE where only Content-Type is required:

HEADER PARAMETERVALUE
AuthorizationRequired Base 64 encoded string that contains the client ID and client secret key. The field must have the format: Authorization: Basic <base64 encoded client_id:client_secret>
Content-TypeRequired Always set to application/x-www-form-urlencoded.

Example

The following example retrieves a refreshed Access Token once the current one has expired:


_22
app.get('/refresh_token', function(req, res) {
_22
_22
var refresh_token = req.query.refresh_token;
_22
var authOptions = {
_22
url: 'https://accounts.spotify.com/api/token',
_22
headers: { 'Authorization': 'Basic ' + (new Buffer.from(client_id + ':' + client_secret).toString('base64')) },
_22
form: {
_22
grant_type: 'refresh_token',
_22
refresh_token: refresh_token
_22
},
_22
json: true
_22
};
_22
_22
request.post(authOptions, function(error, response, body) {
_22
if (!error && response.statusCode === 200) {
_22
var access_token = body.access_token;
_22
res.send({
_22
'access_token': access_token
_22
});
_22
}
_22
});
_22
});

The response will be similar to this:


_10
{
_10
"access_token": "NgA6ZcYI...ixn8bUQ",
_10
"token_type": "Bearer",
_10
"scope": "user-read-private user-read-email",
_10
"expires_in": 3600
_10
}

What's next?

Learn how to use an access token to fetch data from the Spotify Web API in the Access Token guide.