Refreshing tokens

A refresh token is a security credential that allows client applications to obtain new access tokens without requiring users to reauthorize the application.

Access tokens are intentionally configured to have a limited lifespan (1 hour), at the end of which, new tokens can be obtained by providing the original refresh token acquired during the authorization token request response:


_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
}

Refresh token lifetime

Refresh tokens issued to apps registered in the Developer Dashboard have a lifetime of 6 months.

The refresh token lifecycle works as follows:

  1. Creation: The 6-month lifetime starts when the user authorizes your app.
  2. Refresh: Your app can exchange the refresh token for new access tokens. Refreshing an access token does not extend the refresh token's lifetime.
  3. Expiration: After 6 months, the refresh token can no longer be used.
  4. Reauthorization: Your app must send the user through the authorization flow again. Successful user authorization starts a new 6-month lifetime.

The expires_in value returned by the Spotify token endpoint (POST https://accounts.spotify.com/api/token) is the lifetime of the access token, not the refresh token. Access tokens continue to expire after 1 hour.

Info:

Build reauthorization into your app before refresh tokens expire. Do not assume that a refresh token remains valid indefinitely.

Request

To refresh an access token, we must send a POST request with the following parameters:

Body ParameterRelevanceValue
grant_typeRequiredSet it to refresh_token.
refresh_tokenRequiredThe refresh token returned from the authorization token request.
client_idOnly required for the PKCE extensionThe client ID for your app, available from the developer dashboard.

And the following headers:

Header ParameterRelevanceValue
Content-TypeRequiredAlways set to application/x-www-form-urlencoded.
AuthorizationOnly required for the Authorization CodeBase 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>

Example

The following code snippets represent two examples:

browser
nodeJS

_36
const getRefreshToken = async () => {
_36
_36
// refresh token that has been previously stored
_36
const refreshToken = localStorage.getItem('refresh_token');
_36
const url = "https://accounts.spotify.com/api/token";
_36
_36
const payload = {
_36
method: 'POST',
_36
headers: {
_36
'Content-Type': 'application/x-www-form-urlencoded'
_36
},
_36
body: new URLSearchParams({
_36
grant_type: 'refresh_token',
_36
refresh_token: refreshToken,
_36
client_id: clientId
_36
}),
_36
}
_36
const result = await fetch(url, payload);
_36
const response = await result.json();
_36
_36
if (!result.ok) {
_36
if (response.error === 'invalid_grant') {
_36
localStorage.removeItem('access_token');
_36
localStorage.removeItem('refresh_token');
_36
window.location.href = '/login';
_36
return;
_36
}
_36
_36
throw new Error(`Token refresh failed: ${response.error}`);
_36
}
_36
_36
localStorage.setItem('access_token', response.access_token);
_36
if (response.refresh_token) {
_36
localStorage.setItem('refresh_token', response.refresh_token);
_36
}
_36
}

The token endpoint returns invalid_grant when a refresh token is expired, revoked, or otherwise invalid. Your app should discard the refresh token and start the appropriate authorization code flow instead of retrying the refresh request.

Response

If everything goes well, you'll receive a 200 OK response which is very similar to the response when issuing an access token:


_10
{
_10
"access_token": "BQBLuPRYBQ...BP8stIv5xr-Iwaf4l8eg",
_10
"token_type": "Bearer",
_10
"expires_in": 3600,
_10
"refresh_token": "AQAQfyEFmJJuCvAFh...cG_m-2KTgNDaDMQqjrOa3",
_10
"scope": "user-read-email user-read-private"
_10
}

The refresh token contained in the response, can be used to request new tokens. Depending on the grant used to get the initial refresh token, a refresh token might not be included in each response. When a refresh token is not returned, continue using the existing token.