Skip to content

Commit e047800

Browse files
committed
Preserve old refresh token if Spotify does not return a new one during token refresh
1 parent 1b2c9f8 commit e047800

File tree

1 file changed

+31
-46
lines changed

1 file changed

+31
-46
lines changed

src/commonMain/kotlin/com.adamratzman.spotify/SpotifyApi.kt

+31-46
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,9 @@
44
package com.adamratzman.spotify
55

66
import com.adamratzman.spotify.SpotifyException.BadRequestException
7-
import com.adamratzman.spotify.endpoints.client.ClientEpisodeApi
8-
import com.adamratzman.spotify.endpoints.client.ClientFollowingApi
9-
import com.adamratzman.spotify.endpoints.client.ClientLibraryApi
10-
import com.adamratzman.spotify.endpoints.client.ClientPersonalizationApi
11-
import com.adamratzman.spotify.endpoints.client.ClientPlayerApi
12-
import com.adamratzman.spotify.endpoints.client.ClientPlaylistApi
13-
import com.adamratzman.spotify.endpoints.client.ClientProfileApi
14-
import com.adamratzman.spotify.endpoints.client.ClientShowApi
15-
import com.adamratzman.spotify.endpoints.pub.AlbumApi
16-
import com.adamratzman.spotify.endpoints.pub.ArtistApi
17-
import com.adamratzman.spotify.endpoints.pub.BrowseApi
18-
import com.adamratzman.spotify.endpoints.pub.EpisodeApi
19-
import com.adamratzman.spotify.endpoints.pub.FollowingApi
20-
import com.adamratzman.spotify.endpoints.pub.MarketsApi
21-
import com.adamratzman.spotify.endpoints.pub.PlaylistApi
22-
import com.adamratzman.spotify.endpoints.pub.SearchApi
23-
import com.adamratzman.spotify.endpoints.pub.ShowApi
24-
import com.adamratzman.spotify.endpoints.pub.TrackApi
25-
import com.adamratzman.spotify.endpoints.pub.UserApi
26-
import com.adamratzman.spotify.http.CacheState
27-
import com.adamratzman.spotify.http.HttpHeader
28-
import com.adamratzman.spotify.http.HttpRequest
29-
import com.adamratzman.spotify.http.HttpRequestMethod
30-
import com.adamratzman.spotify.http.HttpResponse
31-
import com.adamratzman.spotify.http.SpotifyEndpoint
32-
import com.adamratzman.spotify.http.SpotifyRequest
7+
import com.adamratzman.spotify.endpoints.client.*
8+
import com.adamratzman.spotify.endpoints.pub.*
9+
import com.adamratzman.spotify.http.*
3310
import com.adamratzman.spotify.models.AuthenticationError
3411
import com.adamratzman.spotify.models.Token
3512
import com.adamratzman.spotify.models.TokenValidityResponse
@@ -238,12 +215,20 @@ public sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B
238215
* @throws BadRequestException if refresh fails
239216
* @throws IllegalStateException if [SpotifyApiOptions.refreshTokenProducer] is null
240217
*/
241-
public suspend fun refreshToken(): Token = spotifyApiOptions.refreshTokenProducer?.invoke(this)?.apply {
242-
this@SpotifyApi.token = this
218+
public suspend fun refreshToken(): Token {
219+
val oldToken = token
220+
val refreshedToken = spotifyApiOptions.refreshTokenProducer?.invoke(this)
221+
?: throw SpotifyException.ReAuthenticationNeededException(IllegalStateException("The refreshTokenProducer is null."))
222+
223+
token = refreshedToken
224+
// Spotify may not provide a new refresh token
225+
if (token.refreshToken == null) token.refreshToken = oldToken.refreshToken
226+
243227
spotifyApiOptions.onTokenRefresh?.invoke(this@SpotifyApi)
244228
spotifyApiOptions.afterTokenRefresh?.invoke(this@SpotifyApi)
229+
230+
return oldToken
245231
}
246-
?: throw SpotifyException.ReAuthenticationNeededException(IllegalStateException("The refreshTokenProducer is null."))
247232

248233
/**
249234
* If the method used to create the [token] supports token refresh and
@@ -291,15 +276,15 @@ public sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B
291276
state: String? = null
292277
): String {
293278
return "https://accounts.spotify.com/authorize/?client_id=$clientId" +
294-
"&response_type=${if (isImplicitGrantFlow) "token" else "code"}" +
295-
"&redirect_uri=$redirectUri" +
296-
(state?.let { "&state=$it" } ?: "") +
297-
if (scopes.isEmpty()) {
298-
""
299-
} else {
300-
"&scope=${scopes.joinToString("%20") { it.uri }}" +
301-
if (shouldShowDialog) "&show_dialog=$shouldShowDialog" else ""
302-
}
279+
"&response_type=${if (isImplicitGrantFlow) "token" else "code"}" +
280+
"&redirect_uri=$redirectUri" +
281+
(state?.let { "&state=$it" } ?: "") +
282+
if (scopes.isEmpty()) {
283+
""
284+
} else {
285+
"&scope=${scopes.joinToString("%20") { it.uri }}" +
286+
if (shouldShowDialog) "&show_dialog=$shouldShowDialog" else ""
287+
}
303288
}
304289

305290
/**
@@ -321,12 +306,12 @@ public sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B
321306
state: String? = null
322307
): String {
323308
return "https://accounts.spotify.com/authorize/?client_id=$clientId" +
324-
"&response_type=code" +
325-
"&redirect_uri=$redirectUri" +
326-
"&code_challenge_method=S256" +
327-
"&code_challenge=$codeChallenge" +
328-
(state?.let { "&state=$it" } ?: "") +
329-
if (scopes.isEmpty()) "" else "&scope=${scopes.joinToString("%20") { it.uri }}"
309+
"&response_type=code" +
310+
"&redirect_uri=$redirectUri" +
311+
"&code_challenge_method=S256" +
312+
"&code_challenge=$codeChallenge" +
313+
(state?.let { "&state=$it" } ?: "") +
314+
if (scopes.isEmpty()) "" else "&scope=${scopes.joinToString("%20") { it.uri }}"
330315
}
331316

332317
/**
@@ -645,8 +630,8 @@ public open class SpotifyClientApi(
645630
null
646631
} else {
647632
isTokenValid(false).isValid &&
648-
token.scopes?.contains(scope) == true &&
649-
scopes.all { token.scopes?.contains(it) == true }
633+
token.scopes?.contains(scope) == true &&
634+
scopes.all { token.scopes?.contains(it) == true }
650635
}
651636

652637
/**

0 commit comments

Comments
 (0)